@nextsparkjs/plugin-social-media-publisher 0.1.0-beta.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.env.example +76 -0
- package/README.md +423 -0
- package/api/social/connect/callback/route.ts +669 -0
- package/api/social/connect/route.ts +327 -0
- package/api/social/disconnect/route.ts +187 -0
- package/api/social/publish/route.ts +402 -0
- package/docs/01-getting-started/01-introduction.md +471 -0
- package/docs/01-getting-started/02-installation.md +471 -0
- package/docs/01-getting-started/03-configuration.md +515 -0
- package/docs/02-core-features/01-oauth-integration.md +501 -0
- package/docs/02-core-features/02-publishing.md +527 -0
- package/docs/02-core-features/03-token-management.md +661 -0
- package/docs/02-core-features/04-audit-logging.md +646 -0
- package/docs/03-advanced-usage/01-provider-apis.md +764 -0
- package/docs/03-advanced-usage/02-custom-integrations.md +695 -0
- package/docs/03-advanced-usage/03-per-client-architecture.md +575 -0
- package/docs/04-use-cases/01-agency-management.md +661 -0
- package/docs/04-use-cases/02-content-publishing.md +668 -0
- package/docs/04-use-cases/03-analytics-reporting.md +748 -0
- package/entities/audit-logs/audit-logs.config.ts +150 -0
- package/lib/oauth-helper.ts +167 -0
- package/lib/providers/facebook.ts +672 -0
- package/lib/providers/index.ts +21 -0
- package/lib/providers/instagram.ts +791 -0
- package/lib/validation.ts +155 -0
- package/migrations/001_social_media_tables.sql +167 -0
- package/package.json +15 -0
- package/plugin.config.ts +81 -0
- package/tsconfig.json +47 -0
- package/types/social.types.ts +171 -0
|
@@ -0,0 +1,515 @@
|
|
|
1
|
+
# Configuration
|
|
2
|
+
|
|
3
|
+
## Overview
|
|
4
|
+
|
|
5
|
+
The Social Media Publisher plugin requires specific configuration for Facebook OAuth, token encryption, and security settings. This guide covers all configuration options and best practices.
|
|
6
|
+
|
|
7
|
+
## Environment Variables
|
|
8
|
+
|
|
9
|
+
### Required Variables
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
# ==============================================
|
|
13
|
+
# FACEBOOK OAUTH
|
|
14
|
+
# ==============================================
|
|
15
|
+
|
|
16
|
+
# Facebook App ID
|
|
17
|
+
FACEBOOK_CLIENT_ID=your_app_id
|
|
18
|
+
|
|
19
|
+
# Facebook App Secret
|
|
20
|
+
FACEBOOK_CLIENT_SECRET=your_app_secret
|
|
21
|
+
|
|
22
|
+
# ==============================================
|
|
23
|
+
# APPLICATION
|
|
24
|
+
# ==============================================
|
|
25
|
+
|
|
26
|
+
# Base URL (used for OAuth redirects)
|
|
27
|
+
NEXT_PUBLIC_APP_URL=https://yourdomain.com
|
|
28
|
+
|
|
29
|
+
# ==============================================
|
|
30
|
+
# SECURITY
|
|
31
|
+
# ==============================================
|
|
32
|
+
|
|
33
|
+
# 32-byte encryption key for tokens (64 hex characters)
|
|
34
|
+
OAUTH_ENCRYPTION_KEY=your_encryption_key_here
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Variable Details
|
|
38
|
+
|
|
39
|
+
#### `FACEBOOK_CLIENT_ID`
|
|
40
|
+
|
|
41
|
+
**Purpose:** Facebook App ID for OAuth authentication
|
|
42
|
+
|
|
43
|
+
**Where to Find:**
|
|
44
|
+
1. Go to [Facebook Developers](https://developers.facebook.com/)
|
|
45
|
+
2. Select your app
|
|
46
|
+
3. Settings → Basic
|
|
47
|
+
4. Copy "App ID"
|
|
48
|
+
|
|
49
|
+
**Example:**
|
|
50
|
+
```bash
|
|
51
|
+
FACEBOOK_CLIENT_ID=123456789012345
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
**Notes:**
|
|
55
|
+
- Same app can be used for Facebook Pages and Instagram Business
|
|
56
|
+
- No quotes needed
|
|
57
|
+
- Numeric string
|
|
58
|
+
|
|
59
|
+
#### `FACEBOOK_CLIENT_SECRET`
|
|
60
|
+
|
|
61
|
+
**Purpose:** Facebook App Secret for token exchange
|
|
62
|
+
|
|
63
|
+
**Where to Find:**
|
|
64
|
+
1. Same location as App ID
|
|
65
|
+
2. Settings → Basic
|
|
66
|
+
3. Click "Show" next to "App Secret"
|
|
67
|
+
4. Copy the secret
|
|
68
|
+
|
|
69
|
+
**Example:**
|
|
70
|
+
```bash
|
|
71
|
+
FACEBOOK_CLIENT_SECRET=a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
**Security:**
|
|
75
|
+
- ⚠️ **Never commit to version control**
|
|
76
|
+
- ⚠️ **Never expose in client-side code**
|
|
77
|
+
- ⚠️ **Never log or display**
|
|
78
|
+
- ✅ Store in environment variables
|
|
79
|
+
- ✅ Use secrets manager in production (AWS Secrets Manager, etc.)
|
|
80
|
+
- ✅ Rotate periodically
|
|
81
|
+
|
|
82
|
+
#### `NEXT_PUBLIC_APP_URL`
|
|
83
|
+
|
|
84
|
+
**Purpose:** Base URL for OAuth redirect URIs
|
|
85
|
+
|
|
86
|
+
**Format:** `https://domain.com` (no trailing slash)
|
|
87
|
+
|
|
88
|
+
**Examples:**
|
|
89
|
+
```bash
|
|
90
|
+
# Development
|
|
91
|
+
NEXT_PUBLIC_APP_URL=http://localhost:5173
|
|
92
|
+
|
|
93
|
+
# Production
|
|
94
|
+
NEXT_PUBLIC_APP_URL=https://yourapp.com
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
**Important:**
|
|
98
|
+
- Must match exactly the URL configured in Facebook App
|
|
99
|
+
- No trailing slash
|
|
100
|
+
- Include protocol (`http://` or `https://`)
|
|
101
|
+
- Port number if using non-standard ports
|
|
102
|
+
|
|
103
|
+
#### `OAUTH_ENCRYPTION_KEY`
|
|
104
|
+
|
|
105
|
+
**Purpose:** Encrypt OAuth tokens before storing in database
|
|
106
|
+
|
|
107
|
+
**Format:** 64 hexadecimal characters (32 bytes)
|
|
108
|
+
|
|
109
|
+
**Generate:**
|
|
110
|
+
```bash
|
|
111
|
+
node -e "console.log(require('crypto').randomBytes(32).toString('hex'))"
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Example:**
|
|
115
|
+
```bash
|
|
116
|
+
OAUTH_ENCRYPTION_KEY=a7f9b2c8d1e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
**Security:**
|
|
120
|
+
- ✅ Must be exactly 64 hex characters
|
|
121
|
+
- ✅ Generate unique key per environment
|
|
122
|
+
- ✅ Never commit to git
|
|
123
|
+
- ✅ Store securely
|
|
124
|
+
- ⚠️ Changing key invalidates all existing tokens (users must reconnect)
|
|
125
|
+
|
|
126
|
+
## OAuth Scopes
|
|
127
|
+
|
|
128
|
+
### Instagram Business Permissions
|
|
129
|
+
|
|
130
|
+
```typescript
|
|
131
|
+
[
|
|
132
|
+
'pages_show_list', // Required: List Facebook Pages
|
|
133
|
+
'instagram_basic', // Required: Read Instagram profile
|
|
134
|
+
'instagram_content_publish', // Required: Publish to Instagram
|
|
135
|
+
'instagram_manage_insights' // Optional: Read analytics
|
|
136
|
+
]
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
**What Each Permission Does:**
|
|
140
|
+
|
|
141
|
+
**`pages_show_list`** (Required)
|
|
142
|
+
- Lists Facebook Pages user manages
|
|
143
|
+
- Needed to find Pages with Instagram accounts
|
|
144
|
+
- Prerequisite for Instagram access
|
|
145
|
+
|
|
146
|
+
**`instagram_basic`** (Required)
|
|
147
|
+
- Read Instagram Business Account profile
|
|
148
|
+
- Get account ID, username, follower count
|
|
149
|
+
- Required even for publishing
|
|
150
|
+
|
|
151
|
+
**`instagram_content_publish`** (Required)
|
|
152
|
+
- Create and publish Instagram posts
|
|
153
|
+
- Photos and videos
|
|
154
|
+
- Cannot publish without this
|
|
155
|
+
|
|
156
|
+
**`instagram_manage_insights`** (Optional)
|
|
157
|
+
- Read Instagram analytics
|
|
158
|
+
- Post performance metrics
|
|
159
|
+
- Account insights
|
|
160
|
+
- Not required for publishing
|
|
161
|
+
|
|
162
|
+
### Facebook Pages Permissions
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
[
|
|
166
|
+
'pages_show_list', // Required: List Pages
|
|
167
|
+
'pages_manage_posts', // Required: Create/publish posts
|
|
168
|
+
'pages_read_engagement', // Optional: Read likes/comments
|
|
169
|
+
'read_insights' // Optional: Read analytics
|
|
170
|
+
]
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**What Each Permission Does:**
|
|
174
|
+
|
|
175
|
+
**`pages_show_list`** (Required)
|
|
176
|
+
- Lists Facebook Pages user manages
|
|
177
|
+
- Required to fetch Pages
|
|
178
|
+
|
|
179
|
+
**`pages_manage_posts`** (Required)
|
|
180
|
+
- Create posts on Pages
|
|
181
|
+
- Edit posts
|
|
182
|
+
- Delete posts
|
|
183
|
+
- Required for publishing
|
|
184
|
+
|
|
185
|
+
**`pages_read_engagement`** (Optional)
|
|
186
|
+
- Read post reactions
|
|
187
|
+
- Read comments
|
|
188
|
+
- Track engagement
|
|
189
|
+
- Not required for publishing
|
|
190
|
+
|
|
191
|
+
**`read_insights`** (Optional)
|
|
192
|
+
- Page analytics
|
|
193
|
+
- Visitor demographics
|
|
194
|
+
- Performance metrics
|
|
195
|
+
- Not required for publishing
|
|
196
|
+
|
|
197
|
+
## Facebook App Configuration
|
|
198
|
+
|
|
199
|
+
### OAuth Settings
|
|
200
|
+
|
|
201
|
+
**Location:** Facebook App → Products → Facebook Login → Settings
|
|
202
|
+
|
|
203
|
+
**Valid OAuth Redirect URIs:**
|
|
204
|
+
```
|
|
205
|
+
Development:
|
|
206
|
+
http://localhost:5173/api/v1/plugin/social-media-publisher/social/connect/callback
|
|
207
|
+
|
|
208
|
+
Staging:
|
|
209
|
+
https://staging.yourapp.com/api/v1/plugin/social-media-publisher/social/connect/callback
|
|
210
|
+
|
|
211
|
+
Production:
|
|
212
|
+
https://yourapp.com/api/v1/plugin/social-media-publisher/social/connect/callback
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**Client OAuth Login:** ON
|
|
216
|
+
**Web OAuth Login:** ON
|
|
217
|
+
**Force Web OAuth Reauthentication:** OFF
|
|
218
|
+
**Embedded Browser OAuth Login:** OFF
|
|
219
|
+
|
|
220
|
+
### App Domains
|
|
221
|
+
|
|
222
|
+
**Location:** Settings → Basic → App Domains
|
|
223
|
+
|
|
224
|
+
Add your domains:
|
|
225
|
+
```
|
|
226
|
+
localhost (for development)
|
|
227
|
+
yourapp.com (for production)
|
|
228
|
+
staging.yourapp.com (for staging)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
### Privacy and Terms URLs
|
|
232
|
+
|
|
233
|
+
**Required for Live Mode:**
|
|
234
|
+
|
|
235
|
+
**Privacy Policy URL:**
|
|
236
|
+
```
|
|
237
|
+
https://yourapp.com/privacy
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
**Terms of Service URL:**
|
|
241
|
+
```
|
|
242
|
+
https://yourapp.com/terms
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
### App Review (Production)
|
|
246
|
+
|
|
247
|
+
**For Production Use:** Submit app for review to use permissions with non-admin users.
|
|
248
|
+
|
|
249
|
+
**Checklist:**
|
|
250
|
+
- [ ] App icon uploaded (1024x1024px)
|
|
251
|
+
- [ ] Category selected (e.g., "Business and Pages")
|
|
252
|
+
- [ ] Privacy policy URL added
|
|
253
|
+
- [ ] Terms of service URL added
|
|
254
|
+
- [ ] Detailed description of how you use each permission
|
|
255
|
+
- [ ] Screen recording showing OAuth flow
|
|
256
|
+
- [ ] Screenshots of publishing interface
|
|
257
|
+
- [ ] Business verification completed (for certain permissions)
|
|
258
|
+
|
|
259
|
+
## Security Configuration
|
|
260
|
+
|
|
261
|
+
### Token Encryption
|
|
262
|
+
|
|
263
|
+
**Algorithm:** AES-256-GCM
|
|
264
|
+
**Key Size:** 256 bits (32 bytes)
|
|
265
|
+
**IV:** Unique per token (96 bits)
|
|
266
|
+
**Tag:** 128 bits (for authentication)
|
|
267
|
+
|
|
268
|
+
**Storage Format:**
|
|
269
|
+
```
|
|
270
|
+
encrypted:iv:keyId
|
|
271
|
+
|
|
272
|
+
Example:
|
|
273
|
+
a3f9b2c8d1e3f4a5b6c7d8e9f0a1b2c3d4e5f6a7b8c9d0e1f2:9f7e3ab5c2:key_2024_01
|
|
274
|
+
```
|
|
275
|
+
|
|
276
|
+
**Key Rotation:**
|
|
277
|
+
|
|
278
|
+
If you need to rotate encryption keys:
|
|
279
|
+
|
|
280
|
+
1. Generate new key
|
|
281
|
+
2. Set new `OAUTH_ENCRYPTION_KEY`
|
|
282
|
+
3. All existing tokens become invalid
|
|
283
|
+
4. Users must reconnect accounts
|
|
284
|
+
5. Consider migration script for seamless transition
|
|
285
|
+
|
|
286
|
+
**Migration Script Example:**
|
|
287
|
+
```typescript
|
|
288
|
+
// Decrypt with old key, re-encrypt with new key
|
|
289
|
+
const oldKey = process.env.OLD_OAUTH_ENCRYPTION_KEY
|
|
290
|
+
const newKey = process.env.OAUTH_ENCRYPTION_KEY
|
|
291
|
+
|
|
292
|
+
// ... migration logic
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### Row-Level Security (RLS)
|
|
296
|
+
|
|
297
|
+
**Purpose:** Ensure users can only access social platforms for clients they own.
|
|
298
|
+
|
|
299
|
+
**Policy:**
|
|
300
|
+
```sql
|
|
301
|
+
-- Users can only SELECT social platforms for their clients
|
|
302
|
+
CREATE POLICY "clients_social_platforms_select_own"
|
|
303
|
+
ON "clients_social_platforms" FOR SELECT
|
|
304
|
+
USING (
|
|
305
|
+
"parentId" IN (
|
|
306
|
+
SELECT id FROM "clients"
|
|
307
|
+
WHERE "userId" = current_setting('app.current_user_id', true)
|
|
308
|
+
)
|
|
309
|
+
);
|
|
310
|
+
|
|
311
|
+
-- Users can only INSERT social platforms for their clients
|
|
312
|
+
CREATE POLICY "clients_social_platforms_insert_own"
|
|
313
|
+
ON "clients_social_platforms" FOR INSERT
|
|
314
|
+
WITH CHECK (
|
|
315
|
+
"parentId" IN (
|
|
316
|
+
SELECT id FROM "clients"
|
|
317
|
+
WHERE "userId" = current_setting('app.current_user_id', true)
|
|
318
|
+
)
|
|
319
|
+
);
|
|
320
|
+
|
|
321
|
+
-- Similar policies for UPDATE and DELETE
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
**Verify RLS is Active:**
|
|
325
|
+
```sql
|
|
326
|
+
-- Check RLS is enabled
|
|
327
|
+
SELECT tablename, rowsecurity
|
|
328
|
+
FROM pg_tables
|
|
329
|
+
WHERE tablename = 'clients_social_platforms';
|
|
330
|
+
|
|
331
|
+
-- Should return: rowsecurity = true
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
### Audit Configuration
|
|
335
|
+
|
|
336
|
+
**Retention:** Audit logs are **immutable** and never deleted automatically.
|
|
337
|
+
|
|
338
|
+
**Recommended Retention Policy:**
|
|
339
|
+
```sql
|
|
340
|
+
-- Archive logs older than 1 year
|
|
341
|
+
CREATE TABLE "audit_logs_archive" (
|
|
342
|
+
LIKE "audit_logs" INCLUDING ALL
|
|
343
|
+
);
|
|
344
|
+
|
|
345
|
+
-- Move old logs
|
|
346
|
+
INSERT INTO "audit_logs_archive"
|
|
347
|
+
SELECT * FROM "audit_logs"
|
|
348
|
+
WHERE "createdAt" < NOW() - INTERVAL '1 year';
|
|
349
|
+
|
|
350
|
+
-- Optionally delete from main table
|
|
351
|
+
DELETE FROM "audit_logs"
|
|
352
|
+
WHERE "createdAt" < NOW() - INTERVAL '1 year';
|
|
353
|
+
```
|
|
354
|
+
|
|
355
|
+
## Token Refresh Configuration
|
|
356
|
+
|
|
357
|
+
### Automatic Refresh Settings
|
|
358
|
+
|
|
359
|
+
**Refresh Threshold:** 10 minutes before expiration
|
|
360
|
+
**Token Lifetime:** 60 days (long-lived Facebook tokens)
|
|
361
|
+
**Retry Logic:** None (fail fast if refresh fails)
|
|
362
|
+
|
|
363
|
+
**Threshold Configuration:**
|
|
364
|
+
|
|
365
|
+
Currently hardcoded in publish endpoint:
|
|
366
|
+
```typescript
|
|
367
|
+
const REFRESH_THRESHOLD_MINUTES = 10
|
|
368
|
+
```
|
|
369
|
+
|
|
370
|
+
To customize, edit:
|
|
371
|
+
```typescript
|
|
372
|
+
// contents/plugins/social-media-publisher/api/social/publish/route.ts
|
|
373
|
+
const REFRESH_THRESHOLD_MINUTES = 60 // Refresh 1 hour before expiration
|
|
374
|
+
```
|
|
375
|
+
|
|
376
|
+
**Trade-offs:**
|
|
377
|
+
- Lower threshold (10 min): More last-minute refreshes
|
|
378
|
+
- Higher threshold (60 min): More proactive, but more refresh API calls
|
|
379
|
+
|
|
380
|
+
### Refresh Endpoint
|
|
381
|
+
|
|
382
|
+
**Meta Token Exchange:**
|
|
383
|
+
```
|
|
384
|
+
GET https://graph.facebook.com/v18.0/oauth/access_token
|
|
385
|
+
?grant_type=fb_exchange_token
|
|
386
|
+
&client_id={app_id}
|
|
387
|
+
&client_secret={app_secret}
|
|
388
|
+
&fb_exchange_token={current_token}
|
|
389
|
+
```
|
|
390
|
+
|
|
391
|
+
**Response:**
|
|
392
|
+
```json
|
|
393
|
+
{
|
|
394
|
+
"access_token": "new_long_lived_token",
|
|
395
|
+
"token_type": "bearer",
|
|
396
|
+
"expires_in": 5184000 // 60 days in seconds
|
|
397
|
+
}
|
|
398
|
+
```
|
|
399
|
+
|
|
400
|
+
## Environment-Specific Configuration
|
|
401
|
+
|
|
402
|
+
### Development
|
|
403
|
+
|
|
404
|
+
```bash
|
|
405
|
+
# .env.development
|
|
406
|
+
FACEBOOK_CLIENT_ID=dev_app_id
|
|
407
|
+
FACEBOOK_CLIENT_SECRET=dev_app_secret
|
|
408
|
+
NEXT_PUBLIC_APP_URL=http://localhost:5173
|
|
409
|
+
OAUTH_ENCRYPTION_KEY=dev_key_here
|
|
410
|
+
```
|
|
411
|
+
|
|
412
|
+
**Notes:**
|
|
413
|
+
- Use test Facebook App
|
|
414
|
+
- Can use same account for testing
|
|
415
|
+
- Admins/Developers/Testers can test without App Review
|
|
416
|
+
|
|
417
|
+
### Staging
|
|
418
|
+
|
|
419
|
+
```bash
|
|
420
|
+
# .env.staging
|
|
421
|
+
FACEBOOK_CLIENT_ID=staging_app_id
|
|
422
|
+
FACEBOOK_CLIENT_SECRET=staging_app_secret
|
|
423
|
+
NEXT_PUBLIC_APP_URL=https://staging.yourapp.com
|
|
424
|
+
OAUTH_ENCRYPTION_KEY=staging_key_here
|
|
425
|
+
```
|
|
426
|
+
|
|
427
|
+
**Notes:**
|
|
428
|
+
- Separate Facebook App recommended
|
|
429
|
+
- Use different encryption key
|
|
430
|
+
- Test App Review process
|
|
431
|
+
|
|
432
|
+
### Production
|
|
433
|
+
|
|
434
|
+
```bash
|
|
435
|
+
# .env.production
|
|
436
|
+
FACEBOOK_CLIENT_ID=prod_app_id
|
|
437
|
+
FACEBOOK_CLIENT_SECRET=prod_app_secret
|
|
438
|
+
NEXT_PUBLIC_APP_URL=https://yourapp.com
|
|
439
|
+
OAUTH_ENCRYPTION_KEY=prod_key_here
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
**Notes:**
|
|
443
|
+
- App must be in Live mode
|
|
444
|
+
- Permissions approved via App Review
|
|
445
|
+
- Unique encryption key
|
|
446
|
+
- Use secrets manager for credential storage
|
|
447
|
+
|
|
448
|
+
## Troubleshooting Configuration
|
|
449
|
+
|
|
450
|
+
### Invalid Redirect URI Error
|
|
451
|
+
|
|
452
|
+
**Check:**
|
|
453
|
+
1. `NEXT_PUBLIC_APP_URL` matches Facebook App settings exactly
|
|
454
|
+
2. No trailing slash in URL
|
|
455
|
+
3. Protocol (http/https) correct
|
|
456
|
+
4. Port number included if non-standard
|
|
457
|
+
|
|
458
|
+
### Token Encryption Failures
|
|
459
|
+
|
|
460
|
+
**Check:**
|
|
461
|
+
1. `OAUTH_ENCRYPTION_KEY` is exactly 64 hex characters
|
|
462
|
+
2. Key hasn't changed since accounts were connected
|
|
463
|
+
3. Key is valid hexadecimal
|
|
464
|
+
|
|
465
|
+
**Validate Key:**
|
|
466
|
+
```bash
|
|
467
|
+
# Should output 32
|
|
468
|
+
echo -n "your_key_here" | wc -c | awk '{print $1/2}'
|
|
469
|
+
```
|
|
470
|
+
|
|
471
|
+
### Permission Errors
|
|
472
|
+
|
|
473
|
+
**Check:**
|
|
474
|
+
1. Permissions requested in OAuth URL match Facebook App config
|
|
475
|
+
2. App has permissions approved (if in Live mode)
|
|
476
|
+
3. User granted all requested permissions
|
|
477
|
+
4. Permissions still valid (not revoked by user)
|
|
478
|
+
|
|
479
|
+
## Best Practices
|
|
480
|
+
|
|
481
|
+
### Security
|
|
482
|
+
|
|
483
|
+
✅ **Use Secrets Manager** in production (AWS, Vercel, etc.)
|
|
484
|
+
✅ **Rotate secrets** periodically (quarterly recommended)
|
|
485
|
+
✅ **Separate keys per environment**
|
|
486
|
+
✅ **Never commit** secrets to git
|
|
487
|
+
✅ **Audit access** to environment variables
|
|
488
|
+
|
|
489
|
+
### OAuth
|
|
490
|
+
|
|
491
|
+
✅ **Request minimum permissions** needed
|
|
492
|
+
✅ **Handle permission denials** gracefully
|
|
493
|
+
✅ **Show clear permission justifications** to users
|
|
494
|
+
✅ **Test OAuth flow** in all environments
|
|
495
|
+
✅ **Monitor token refresh** success rates
|
|
496
|
+
|
|
497
|
+
### Token Management
|
|
498
|
+
|
|
499
|
+
✅ **Encrypt all tokens** before storage
|
|
500
|
+
✅ **Refresh proactively** (10+ min threshold)
|
|
501
|
+
✅ **Handle refresh failures** gracefully
|
|
502
|
+
✅ **Log refresh events** for monitoring
|
|
503
|
+
✅ **Never log** decrypted tokens
|
|
504
|
+
|
|
505
|
+
## Next Steps
|
|
506
|
+
|
|
507
|
+
✅ Configuration complete
|
|
508
|
+
✅ Environment variables set
|
|
509
|
+
✅ Facebook App configured
|
|
510
|
+
✅ Security enabled
|
|
511
|
+
|
|
512
|
+
**Continue to:**
|
|
513
|
+
- **[OAuth Integration](../02-core-features/01-oauth-integration.md)** - Connect accounts
|
|
514
|
+
- **[Publishing](../02-core-features/02-publishing.md)** - Start publishing
|
|
515
|
+
- **[Token Management](../02-core-features/03-token-management.md)** - Understand token lifecycle
|