PlanOpticon

planopticon / docs / guide / authentication.md
1
# Authentication
2
3
PlanOpticon uses a unified authentication system to connect with cloud services for fetching recordings, documents, and other content. The system is **OAuth-first**: it prefers OAuth 2.0 flows for security and token management, but falls back to API keys when OAuth is not configured.
4
5
## Auth strategy overview
6
7
PlanOpticon supports six cloud services out of the box: Google, Dropbox, Zoom, Notion, GitHub, and Microsoft. Each service uses the same authentication chain, implemented through the `OAuthManager` class. You configure credentials once (via environment variables or directly), and PlanOpticon handles token acquisition, storage, refresh, and fallback automatically.
8
9
All authentication state is managed through the `planopticon auth` CLI command, the `/auth` companion REPL command, or programmatically via the Python API.
10
11
## The auth chain
12
13
When you authenticate with a service, PlanOpticon tries the following methods in order. It stops at the first one that succeeds:
14
15
1. **Saved token** -- Checks `~/.planopticon/{service}_token.json` for a previously saved token. If the token has not expired, it is used immediately. If it has expired but a refresh token is available, PlanOpticon attempts an automatic token refresh.
16
17
2. **Client Credentials grant** (Server-to-Server) -- If an `account_id` is configured (e.g., `ZOOM_ACCOUNT_ID`), PlanOpticon attempts a client credentials grant. This is a non-interactive flow suitable for automated pipelines and server-side integrations. No browser is required.
18
19
3. **OAuth 2.0 Authorization Code with PKCE** (interactive) -- If a client ID is configured and OAuth endpoints are available, PlanOpticon initiates an interactive OAuth PKCE flow. It opens a browser to the service's authorization page, waits for you to paste the authorization code, and exchanges it for tokens. The tokens are saved for future use.
20
21
4. **API key fallback** -- If no OAuth method succeeds, PlanOpticon checks for a service-specific API key environment variable (e.g., `GITHUB_TOKEN`, `NOTION_API_KEY`). This is the simplest setup but may have reduced capabilities compared to OAuth.
22
23
If none of the four methods succeed, PlanOpticon returns an error with hints about which environment variables to set.
24
25
## Token storage
26
27
Tokens are persisted as JSON files in `~/.planopticon/`:
28
29
```
30
~/.planopticon/
31
google_token.json
32
dropbox_token.json
33
zoom_token.json
34
notion_token.json
35
github_token.json
36
microsoft_token.json
37
```
38
39
Each token file contains:
40
41
| Field | Description |
42
|-------|-------------|
43
| `access_token` | The current access token |
44
| `refresh_token` | Refresh token for automatic renewal (if provided by the service) |
45
| `expires_at` | Unix timestamp when the token expires (with a 60-second safety margin) |
46
| `client_id` | The client ID used for this token (for refresh) |
47
| `client_secret` | The client secret used (for refresh) |
48
49
The `~/.planopticon/` directory is created automatically on first use. Token files are overwritten on each successful authentication or refresh.
50
51
To remove a saved token, use `planopticon auth <service> --logout` or delete the file directly.
52
53
## Supported services
54
55
### Google
56
57
Google authentication provides access to Google Drive and Google Docs for fetching documents, recordings, and other content.
58
59
**Scopes requested:**
60
61
- `https://www.googleapis.com/auth/drive.readonly`
62
- `https://www.googleapis.com/auth/documents.readonly`
63
64
**Environment variables:**
65
66
| Variable | Required | Description |
67
|----------|----------|-------------|
68
| `GOOGLE_CLIENT_ID` | For OAuth | OAuth 2.0 Client ID from Google Cloud Console |
69
| `GOOGLE_CLIENT_SECRET` | For OAuth | OAuth 2.0 Client Secret |
70
| `GOOGLE_API_KEY` | Fallback | API key (limited access, no user-specific data) |
71
72
**OAuth app setup:**
73
74
1. Go to the [Google Cloud Console](https://console.cloud.google.com/).
75
2. Create a project (or select an existing one).
76
3. Navigate to **APIs & Services > Credentials**.
77
4. Click **Create Credentials > OAuth client ID**.
78
5. Choose **Desktop app** as the application type.
79
6. Copy the Client ID and Client Secret.
80
7. Under **APIs & Services > Library**, enable the **Google Drive API** and **Google Docs API**.
81
8. Set the environment variables:
82
83
```bash
84
export GOOGLE_CLIENT_ID="your-client-id.apps.googleusercontent.com"
85
export GOOGLE_CLIENT_SECRET="your-client-secret"
86
```
87
88
**Service account fallback:** For automated pipelines, you can use a Google service account instead of OAuth. Generate a service account key JSON file from the Google Cloud Console and set `GOOGLE_APPLICATION_CREDENTIALS` to point to it. The PlanOpticon Google Workspace connector (`planopticon gws`) uses the `gws` CLI which has its own auth flow via `gws auth login`.
89
90
### Dropbox
91
92
Dropbox authentication provides access to files stored in Dropbox.
93
94
**Environment variables:**
95
96
| Variable | Required | Description |
97
|----------|----------|-------------|
98
| `DROPBOX_APP_KEY` | For OAuth | App key from the Dropbox App Console |
99
| `DROPBOX_APP_SECRET` | For OAuth | App secret |
100
| `DROPBOX_ACCESS_TOKEN` | Fallback | Long-lived access token (for quick setup) |
101
102
**OAuth app setup:**
103
104
1. Go to the [Dropbox App Console](https://www.dropbox.com/developers/apps).
105
2. Click **Create App**.
106
3. Choose **Scoped access** and **Full Dropbox** (or **App folder** for restricted access).
107
4. Copy the App key and App secret from the **Settings** tab.
108
5. Set the environment variables:
109
110
```bash
111
export DROPBOX_APP_KEY="your-app-key"
112
export DROPBOX_APP_SECRET="your-app-secret"
113
```
114
115
**Access token shortcut:** For quick testing, you can generate an access token directly from the app's Settings page in the Dropbox App Console and set it as `DROPBOX_ACCESS_TOKEN`. This bypasses OAuth entirely but the token may have a limited lifetime.
116
117
### Zoom
118
119
Zoom authentication provides access to cloud recordings, meeting metadata, and transcripts.
120
121
**Environment variables:**
122
123
| Variable | Required | Description |
124
|----------|----------|-------------|
125
| `ZOOM_CLIENT_ID` | For OAuth | OAuth client ID from the Zoom Marketplace |
126
| `ZOOM_CLIENT_SECRET` | For OAuth | OAuth client secret |
127
| `ZOOM_ACCOUNT_ID` | For S2S | Account ID for Server-to-Server OAuth |
128
129
**Server-to-Server (recommended for automation):**
130
131
When `ZOOM_ACCOUNT_ID` is set alongside `ZOOM_CLIENT_ID` and `ZOOM_CLIENT_SECRET`, PlanOpticon uses the client credentials grant (Server-to-Server OAuth). This is non-interactive and ideal for CI/CD pipelines and scheduled jobs.
132
133
1. Go to the [Zoom Marketplace](https://marketplace.zoom.us/).
134
2. Click **Develop > Build App**.
135
3. Choose **Server-to-Server OAuth**.
136
4. Copy the Account ID, Client ID, and Client Secret.
137
5. Add the required scopes: `recording:read:admin` (or `recording:read`).
138
6. Set the environment variables:
139
140
```bash
141
export ZOOM_CLIENT_ID="your-client-id"
142
export ZOOM_CLIENT_SECRET="your-client-secret"
143
export ZOOM_ACCOUNT_ID="your-account-id"
144
```
145
146
**User-level OAuth PKCE:**
147
148
If `ZOOM_ACCOUNT_ID` is not set, PlanOpticon falls back to the interactive OAuth PKCE flow. This opens a browser window for the user to authorize access.
149
150
1. In the Zoom Marketplace, create a **General App** (or **OAuth** app).
151
2. Set the redirect URI to `urn:ietf:wg:oauth:2.0:oob` (out-of-band).
152
3. Copy the Client ID and Client Secret.
153
154
### Notion
155
156
Notion authentication provides access to pages, databases, and content in your Notion workspace.
157
158
**Environment variables:**
159
160
| Variable | Required | Description |
161
|----------|----------|-------------|
162
| `NOTION_CLIENT_ID` | For OAuth | OAuth client ID from the Notion Integrations page |
163
| `NOTION_CLIENT_SECRET` | For OAuth | OAuth client secret |
164
| `NOTION_API_KEY` | Fallback | Internal integration token |
165
166
**OAuth app setup:**
167
168
1. Go to [My Integrations](https://www.notion.so/my-integrations) in Notion.
169
2. Click **New integration**.
170
3. Select **Public integration** (required for OAuth).
171
4. Copy the OAuth Client ID and Client Secret.
172
5. Set the redirect URI.
173
6. Set the environment variables:
174
175
```bash
176
export NOTION_CLIENT_ID="your-client-id"
177
export NOTION_CLIENT_SECRET="your-client-secret"
178
```
179
180
**Internal integration (API key fallback):**
181
182
For simpler setups, create an **Internal integration** from the Notion Integrations page. Copy the integration token and set it as `NOTION_API_KEY`. You must also share the relevant Notion pages/databases with the integration.
183
184
```bash
185
export NOTION_API_KEY="ntn_your-integration-token"
186
```
187
188
### GitHub
189
190
GitHub authentication provides access to repositories, issues, and organization data.
191
192
**Scopes requested:**
193
194
- `repo`
195
- `read:org`
196
197
**Environment variables:**
198
199
| Variable | Required | Description |
200
|----------|----------|-------------|
201
| `GITHUB_CLIENT_ID` | For OAuth | OAuth App client ID |
202
| `GITHUB_CLIENT_SECRET` | For OAuth | OAuth App client secret |
203
| `GITHUB_TOKEN` | Fallback | Personal access token (classic or fine-grained) |
204
205
**OAuth app setup:**
206
207
1. Go to **GitHub > Settings > Developer Settings > OAuth Apps**.
208
2. Click **New OAuth App**.
209
3. Set the Authorization callback URL to `urn:ietf:wg:oauth:2.0:oob`.
210
4. Copy the Client ID and generate a Client Secret.
211
5. Set the environment variables:
212
213
```bash
214
export GITHUB_CLIENT_ID="your-client-id"
215
export GITHUB_CLIENT_SECRET="your-client-secret"
216
```
217
218
**Personal access token (recommended for most users):**
219
220
The simplest approach is to create a Personal Access Token:
221
222
1. Go to **GitHub > Settings > Developer Settings > Personal Access Tokens**.
223
2. Generate a token with `repo` and `read:org` scopes.
224
3. Set it as `GITHUB_TOKEN`:
225
226
```bash
227
export GITHUB_TOKEN="ghp_your-token"
228
```
229
230
### Microsoft
231
232
Microsoft authentication provides access to Microsoft 365 resources via the Microsoft Graph API, including OneDrive, SharePoint, and Teams recordings.
233
234
**Scopes requested:**
235
236
- `https://graph.microsoft.com/OnlineMeetings.Read`
237
- `https://graph.microsoft.com/Files.Read`
238
239
**Environment variables:**
240
241
| Variable | Required | Description |
242
|----------|----------|-------------|
243
| `MICROSOFT_CLIENT_ID` | For OAuth | Application (client) ID from Azure AD |
244
| `MICROSOFT_CLIENT_SECRET` | For OAuth | Client secret from Azure AD |
245
246
**Azure AD app registration:**
247
248
1. Go to the [Azure Portal](https://portal.azure.com/).
249
2. Navigate to **Azure Active Directory > App registrations**.
250
3. Click **New registration**.
251
4. Name the application (e.g., "PlanOpticon").
252
5. Under **Supported account types**, select the appropriate option for your organization.
253
6. Set the redirect URI to `urn:ietf:wg:oauth:2.0:oob` with platform **Mobile and desktop applications**.
254
7. After registration, go to **Certificates & secrets** and create a new client secret.
255
8. Under **API permissions**, add:
256
- `OnlineMeetings.Read`
257
- `Files.Read`
258
9. Grant admin consent if required by your organization.
259
10. Set the environment variables:
260
261
```bash
262
export MICROSOFT_CLIENT_ID="your-application-id"
263
export MICROSOFT_CLIENT_SECRET="your-client-secret"
264
```
265
266
**Microsoft 365 CLI:** The `planopticon m365` commands use the `@pnp/cli-microsoft365` npm package, which has its own authentication flow via `m365 login`. This is separate from the OAuth flow described above.
267
268
## CLI usage
269
270
### `planopticon auth`
271
272
Authenticate with a cloud service or manage saved tokens.
273
274
```
275
planopticon auth SERVICE [--logout]
276
```
277
278
**Arguments:**
279
280
| Argument | Description |
281
|----------|-------------|
282
| `SERVICE` | One of: `google`, `dropbox`, `zoom`, `notion`, `github`, `microsoft` |
283
284
**Options:**
285
286
| Option | Description |
287
|--------|-------------|
288
| `--logout` | Clear the saved token for the specified service |
289
290
**Examples:**
291
292
```bash
293
# Authenticate with Google (triggers OAuth flow or uses saved token)
294
planopticon auth google
295
296
# Authenticate with Zoom
297
planopticon auth zoom
298
299
# Clear saved GitHub token
300
planopticon auth github --logout
301
```
302
303
On success, the command prints the authentication method used:
304
305
```
306
Google authentication successful (oauth_pkce).
307
```
308
309
or
310
311
```
312
Github authentication successful (api_key).
313
```
314
315
### Companion REPL `/auth`
316
317
Inside the interactive companion REPL (`planopticon -C` or `planopticon -I`), you can authenticate with services using the `/auth` command:
318
319
```
320
/auth SERVICE
321
```
322
323
Without arguments, `/auth` lists all available services:
324
325
```
326
> /auth
327
Usage: /auth SERVICE
328
Available: dropbox, github, google, microsoft, notion, zoom
329
```
330
331
With a service name, it runs the same auth chain as the CLI command:
332
333
```
334
> /auth github
335
Github authentication successful (api_key).
336
```
337
338
## Environment variables reference
339
340
The following table summarizes all environment variables used by the authentication system:
341
342
| Service | OAuth Client ID | OAuth Client Secret | API Key / Token | Account ID |
343
|---------|----------------|--------------------|--------------------|------------|
344
| Google | `GOOGLE_CLIENT_ID` | `GOOGLE_CLIENT_SECRET` | `GOOGLE_API_KEY` | -- |
345
| Dropbox | `DROPBOX_APP_KEY` | `DROPBOX_APP_SECRET` | `DROPBOX_ACCESS_TOKEN` | -- |
346
| Zoom | `ZOOM_CLIENT_ID` | `ZOOM_CLIENT_SECRET` | -- | `ZOOM_ACCOUNT_ID` |
347
| Notion | `NOTION_CLIENT_ID` | `NOTION_CLIENT_SECRET` | `NOTION_API_KEY` | -- |
348
| GitHub | `GITHUB_CLIENT_ID` | `GITHUB_CLIENT_SECRET` | `GITHUB_TOKEN` | -- |
349
| Microsoft | `MICROSOFT_CLIENT_ID` | `MICROSOFT_CLIENT_SECRET` | -- | -- |
350
351
## Python API
352
353
### AuthConfig
354
355
The `AuthConfig` dataclass defines the authentication configuration for a service. It holds OAuth endpoints, credential references, scopes, and token storage paths.
356
357
```python
358
from video_processor.auth import AuthConfig
359
360
config = AuthConfig(
361
service="myservice",
362
oauth_authorize_url="https://example.com/oauth/authorize",
363
oauth_token_url="https://example.com/oauth/token",
364
client_id_env="MYSERVICE_CLIENT_ID",
365
client_secret_env="MYSERVICE_CLIENT_SECRET",
366
api_key_env="MYSERVICE_API_KEY",
367
scopes=["read", "write"],
368
)
369
```
370
371
**Key fields:**
372
373
| Field | Type | Description |
374
|-------|------|-------------|
375
| `service` | `str` | Service identifier (used for token filename) |
376
| `oauth_authorize_url` | `Optional[str]` | OAuth authorization endpoint |
377
| `oauth_token_url` | `Optional[str]` | OAuth token endpoint |
378
| `client_id` / `client_id_env` | `Optional[str]` | Client ID value or env var name |
379
| `client_secret` / `client_secret_env` | `Optional[str]` | Client secret value or env var name |
380
| `api_key_env` | `Optional[str]` | Environment variable for API key fallback |
381
| `scopes` | `List[str]` | OAuth scopes to request |
382
| `redirect_uri` | `str` | Redirect URI (default: `urn:ietf:wg:oauth:2.0:oob`) |
383
| `account_id` / `account_id_env` | `Optional[str]` | Account ID for client credentials grant |
384
| `token_path` | `Optional[Path]` | Override token storage path |
385
386
**Resolved properties:**
387
388
- `resolved_client_id` -- Returns the client ID from the direct value or environment variable.
389
- `resolved_client_secret` -- Returns the client secret from the direct value or environment variable.
390
- `resolved_api_key` -- Returns the API key from the environment variable.
391
- `resolved_account_id` -- Returns the account ID from the direct value or environment variable.
392
- `resolved_token_path` -- Returns the token file path (default: `~/.planopticon/{service}_token.json`).
393
- `supports_oauth` -- Returns `True` if both OAuth endpoints are configured.
394
395
### OAuthManager
396
397
The `OAuthManager` class manages the full authentication lifecycle for a service.
398
399
```python
400
from video_processor.auth import OAuthManager, AuthConfig
401
402
config = AuthConfig(
403
service="notion",
404
oauth_authorize_url="https://api.notion.com/v1/oauth/authorize",
405
oauth_token_url="https://api.notion.com/v1/oauth/token",
406
client_id_env="NOTION_CLIENT_ID",
407
client_secret_env="NOTION_CLIENT_SECRET",
408
api_key_env="NOTION_API_KEY",
409
scopes=["read_content"],
410
)
411
manager = OAuthManager(config)
412
413
# Full auth chain -- returns AuthResult
414
result = manager.authenticate()
415
if result.success:
416
print(f"Authenticated via {result.method}")
417
print(f"Token: {result.access_token[:20]}...")
418
419
# Convenience method -- returns just the token string or None
420
token = manager.get_token()
421
422
# Clear saved token (logout)
423
manager.clear_token()
424
```
425
426
**AuthResult fields:**
427
428
| Field | Type | Description |
429
|-------|------|-------------|
430
| `success` | `bool` | Whether authentication succeeded |
431
| `access_token` | `Optional[str]` | The access token (if successful) |
432
| `method` | `Optional[str]` | One of: `saved_token`, `oauth_pkce`, `client_credentials`, `api_key` |
433
| `expires_at` | `Optional[float]` | Token expiry as a Unix timestamp |
434
| `refresh_token` | `Optional[str]` | Refresh token (if provided) |
435
| `error` | `Optional[str]` | Error message (if unsuccessful) |
436
437
### Pre-built configs
438
439
PlanOpticon ships with pre-built `AuthConfig` instances for all six supported services. Access them via convenience functions:
440
441
```python
442
from video_processor.auth import get_auth_config, get_auth_manager
443
444
# Get just the config
445
config = get_auth_config("zoom")
446
447
# Get a ready-to-use manager
448
manager = get_auth_manager("github")
449
token = manager.get_token()
450
```
451
452
### Building custom connectors
453
454
To add authentication for a new service, create an `AuthConfig` with the service's OAuth endpoints and credential environment variables:
455
456
```python
457
from video_processor.auth import AuthConfig, OAuthManager
458
459
config = AuthConfig(
460
service="slack",
461
oauth_authorize_url="https://slack.com/oauth/v2/authorize",
462
oauth_token_url="https://slack.com/api/oauth.v2.access",
463
client_id_env="SLACK_CLIENT_ID",
464
client_secret_env="SLACK_CLIENT_SECRET",
465
api_key_env="SLACK_BOT_TOKEN",
466
scopes=["channels:read", "channels:history"],
467
)
468
469
manager = OAuthManager(config)
470
result = manager.authenticate()
471
```
472
473
The token will be saved to `~/.planopticon/slack_token.json` and automatically refreshed on subsequent calls.
474
475
## Troubleshooting
476
477
### "No auth method available for {service}"
478
479
This means none of the four auth methods succeeded. Check that:
480
481
- The required environment variables are set and non-empty.
482
- For OAuth: both the client ID and client secret (or app key/secret) are set.
483
- For API key fallback: the correct environment variable is set.
484
485
The error message includes hints about which variables to set.
486
487
### Token refresh fails
488
489
If automatic token refresh fails, PlanOpticon falls back to the next auth method in the chain. Common causes:
490
491
- The refresh token has been revoked (e.g., you changed your password or revoked app access).
492
- The OAuth app's client secret has changed.
493
- The service requires re-authorization after a certain period.
494
495
To resolve, clear the token and re-authenticate:
496
497
```bash
498
planopticon auth google --logout
499
planopticon auth google
500
```
501
502
### OAuth PKCE flow does not open a browser
503
504
If the browser does not open automatically, PlanOpticon prints the authorization URL to the terminal. Copy and paste it into your browser manually. After authorizing, paste the authorization code back into the terminal prompt.
505
506
### "requests not installed"
507
508
The OAuth flows require the `requests` library. It is included as a dependency of PlanOpticon, but if you installed PlanOpticon in a minimal environment, install it manually:
509
510
```bash
511
pip install requests
512
```
513
514
### Permission denied on token file
515
516
PlanOpticon needs write access to `~/.planopticon/`. If the directory or token files have restrictive permissions, adjust them:
517
518
```bash
519
chmod 700 ~/.planopticon
520
chmod 600 ~/.planopticon/*_token.json
521
```
522
523
### Microsoft authentication uses the `/common` tenant
524
525
The default Microsoft OAuth configuration uses the `common` tenant endpoint (`login.microsoftonline.com/common/...`), which supports both personal Microsoft accounts and Azure AD organizational accounts. If your organization requires a specific tenant, you can create a custom `AuthConfig` with the tenant-specific URLs.
526

Keyboard Shortcuts

Open search /
Next entry (timeline) j
Previous entry (timeline) k
Open focused entry Enter
Show this help ?
Toggle theme Top nav button