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