PlanOpticon
Auth API Reference¶
video_processor.auth
¶
Unified OAuth and authentication strategy for PlanOpticon connectors.
Provides a consistent auth pattern across all source connectors: 1. Saved token (auto-refresh if expired) 2. OAuth 2.0 (Authorization Code with PKCE, or Client Credentials) 3. API key fallback (environment variable)
Usage in a connector:
from video_processor.auth import OAuthManager, AuthConfig
config = AuthConfig(
service="notion",
oauth_authorize_url="https://api.notion.com/v1/oauth/authorize",
oauth_token_url="https://api.notion.com/v1/oauth/token",
client_id_env="NOTION_CLIENT_ID",
client_secret_env="NOTION_CLIENT_SECRET",
api_key_env="NOTION_API_KEY",
scopes=["read_content"],
)
manager = OAuthManager(config)
token = manager.authenticate() # Returns access token or None
AuthConfig
dataclass
¶
Configuration for a service's authentication.
Source code in video_processor/auth.py
AuthResult
dataclass
¶
Result of an authentication attempt.
Source code in video_processor/auth.py
OAuthManager
¶
Manages OAuth and API key authentication for a service.
Tries auth methods in order: 1. Load saved token (refresh if expired) 2. Client Credentials grant (if account_id is set) 3. OAuth2 Authorization Code with PKCE (interactive) 4. API key fallback
Source code in video_processor/auth.py
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 | |
authenticate()
¶
Run the auth chain and return the result.
Source code in video_processor/auth.py
clear_token()
¶
get_token()
¶
get_auth_config(service)
¶
get_auth_manager(service)
¶
Overview¶
The video_processor.auth module provides a unified OAuth and authentication strategy for all PlanOpticon source connectors. It supports multiple authentication methods tried in a consistent order:
- Saved token -- load from disk, auto-refresh if expired
- Client Credentials -- server-to-server OAuth (e.g., Zoom S2S)
- OAuth 2.0 PKCE -- interactive Authorization Code flow with PKCE
- API key fallback -- environment variable lookup
Tokens are persisted to ~/.planopticon/ and automatically refreshed on expiry.
AuthConfig¶
Dataclass configuring authentication for a specific service. Defines OAuth endpoints, client credentials, API key fallback, scopes, and token storage.
Fields¶
| Field | Type | Default | Description |
|---|---|---|---|
service |
str |
required | Service identifier (e.g., "zoom", "notion") |
oauth_authorize_url |
Optional[str] |
None |
OAuth authorization endpoint URL |
oauth_token_url |
Optional[str] |
None |
OAuth token exchange endpoint URL |
client_id |
Optional[str] |
None |
OAuth client ID (direct value) |
client_secret |
Optional[str] |
None |
OAuth client secret (direct value) |
client_id_env |
Optional[str] |
None |
Environment variable for client ID |
client_secret_env |
Optional[str] |
None |
Environment variable for client secret |
api_key_env |
Optional[str] |
None |
Environment variable for API key fallback |
scopes |
List[str] |
[] |
OAuth scopes to request |
redirect_uri |
str |
"urn:ietf:wg:oauth:2.0:oob" |
Redirect URI for auth code flow |
account_id |
Optional[str] |
None |
Account ID for client credentials grant (direct value) |
account_id_env |
Optional[str] |
None |
Environment variable for account ID |
token_path |
Optional[Path] |
None |
Custom token storage path |
Resolved Properties¶
These properties resolve values by checking the direct field first, then falling back to the environment variable.
| Property | Return Type | Description |
|---|---|---|
resolved_client_id |
Optional[str] |
Client ID from client_id or os.environ[client_id_env] |
resolved_client_secret |
Optional[str] |
Client secret from client_secret or os.environ[client_secret_env] |
resolved_api_key |
Optional[str] |
API key from os.environ[api_key_env] |
resolved_account_id |
Optional[str] |
Account ID from account_id or os.environ[account_id_env] |
resolved_token_path |
Path |
Token file path: token_path or ~/.planopticon/{service}_token.json |
supports_oauth |
bool |
True if both oauth_authorize_url and oauth_token_url are set |
from video_processor.auth import AuthConfig
config = AuthConfig(
service="notion",
oauth_authorize_url="https://api.notion.com/v1/oauth/authorize",
oauth_token_url="https://api.notion.com/v1/oauth/token",
client_id_env="NOTION_CLIENT_ID",
client_secret_env="NOTION_CLIENT_SECRET",
api_key_env="NOTION_API_KEY",
scopes=["read_content"],
)
# Check resolved values
print(config.resolved_client_id) # From NOTION_CLIENT_ID env var
print(config.supports_oauth) # True
print(config.resolved_token_path) # ~/.planopticon/notion_token.json
AuthResult¶
Dataclass representing the result of an authentication attempt.
| Field | Type | Default | Description |
|---|---|---|---|
success |
bool |
required | Whether authentication succeeded |
access_token |
Optional[str] |
None |
The access token (if successful) |
method |
Optional[str] |
None |
Auth method used: "saved_token", "oauth_pkce", "client_credentials", "api_key" |
expires_at |
Optional[float] |
None |
Token expiration as Unix timestamp |
refresh_token |
Optional[str] |
None |
OAuth refresh token (if available) |
error |
Optional[str] |
None |
Error message (if failed) |
result = manager.authenticate()
if result.success:
print(f"Authenticated via {result.method}")
print(f"Token: {result.access_token[:20]}...")
if result.expires_at:
import time
remaining = result.expires_at - time.time()
print(f"Expires in {remaining/60:.0f} minutes")
else:
print(f"Auth failed: {result.error}")
OAuthManager¶
Manages the full authentication lifecycle for a service. Tries auth methods in priority order and handles token persistence, refresh, and PKCE flow.
Constructor¶
| Parameter | Type | Description |
|---|---|---|
config |
AuthConfig |
Authentication configuration for the target service |
authenticate()¶
Run the full auth chain and return the result. Methods are tried in order:
- Saved token -- checks
~/.planopticon/{service}_token.json, refreshes if expired - Client Credentials -- if
account_idis set and OAuth is configured, uses the client credentials grant (server-to-server) - OAuth PKCE -- if OAuth is configured and client ID is available, opens a browser for interactive authorization with PKCE
- API key -- falls back to the environment variable specified in
api_key_env
Returns: AuthResult -- success/failure with token and method details.
If all methods fail, returns an AuthResult with success=False and a helpful error message listing which environment variables to set.
get_token()¶
Convenience method: run authenticate() and return just the access token string.
Returns: Optional[str] -- the access token, or None if authentication failed.
clear_token()¶
Remove the saved token file for this service (effectively a logout). The next authenticate() call will require re-authentication.
Authentication Flows¶
Saved Token (auto-refresh)¶
Tokens are saved to ~/.planopticon/{service}_token.json as JSON. On each authenticate() call, the saved token is loaded and checked:
- If the token has not expired (
time.time() < expires_at), it is returned immediately - If expired but a refresh token is available, the manager attempts to refresh using the OAuth token endpoint
- The refreshed token is saved back to disk
Client Credentials Grant¶
Used for server-to-server authentication (e.g., Zoom Server-to-Server OAuth). Requires account_id, client_id, and client_secret. Sends a POST to the token endpoint with grant_type=account_credentials.
OAuth 2.0 Authorization Code with PKCE¶
Interactive flow for user authentication:
- Generates a PKCE code verifier and S256 challenge
- Constructs the authorization URL with client ID, redirect URI, scopes, and PKCE challenge
- Opens the URL in the user's browser
- Prompts the user to paste the authorization code
- Exchanges the code for tokens at the token endpoint
- Saves the tokens to disk
API Key Fallback¶
If no OAuth flow succeeds, falls back to checking the environment variable specified in api_key_env. Returns the value directly as the access token.
KNOWN_CONFIGS¶
Pre-built AuthConfig instances for supported services. These cover the most common cloud integrations and can be used directly or as templates for custom configurations.
| Service Key | Service | OAuth Endpoints | Client ID Env | API Key Env |
|---|---|---|---|---|
"zoom" |
Zoom | zoom.us/oauth/... |
ZOOM_CLIENT_ID |
-- |
"notion" |
Notion | api.notion.com/v1/oauth/... |
NOTION_CLIENT_ID |
NOTION_API_KEY |
"dropbox" |
Dropbox | dropbox.com/oauth2/... |
DROPBOX_APP_KEY |
DROPBOX_ACCESS_TOKEN |
"github" |
GitHub | github.com/login/oauth/... |
GITHUB_CLIENT_ID |
GITHUB_TOKEN |
"google" |
accounts.google.com/o/oauth2/... |
GOOGLE_CLIENT_ID |
GOOGLE_API_KEY |
|
"microsoft" |
Microsoft | login.microsoftonline.com/.../oauth2/... |
MICROSOFT_CLIENT_ID |
-- |
Zoom¶
Supports both Server-to-Server (via ZOOM_ACCOUNT_ID) and OAuth PKCE flows.
# Server-to-Server
export ZOOM_CLIENT_ID="..."
export ZOOM_CLIENT_SECRET="..."
export ZOOM_ACCOUNT_ID="..."
# Or interactive OAuth (omit ZOOM_ACCOUNT_ID)
export ZOOM_CLIENT_ID="..."
export ZOOM_CLIENT_SECRET="..."
Google (Drive, Meet, Workspace)¶
Supports OAuth PKCE and API key fallback. Scopes include Drive and Docs read-only access.
export GOOGLE_CLIENT_ID="..."
export GOOGLE_CLIENT_SECRET="..."
# Or for API-key-only access:
export GOOGLE_API_KEY="..."
GitHub¶
Supports OAuth PKCE and personal access token. Requests repo and read:org scopes.
# OAuth
export GITHUB_CLIENT_ID="..."
export GITHUB_CLIENT_SECRET="..."
# Or personal access token
export GITHUB_TOKEN="ghp_..."
Helper Functions¶
get_auth_config()¶
Get a pre-built AuthConfig for a known service.
Parameters:
| Parameter | Type | Description |
|---|---|---|
service |
str |
Service name (e.g., "zoom", "notion", "github") |
Returns: Optional[AuthConfig] -- the config, or None if the service is not in KNOWN_CONFIGS.
get_auth_manager()¶
Get an OAuthManager for a known service. Convenience wrapper that looks up the config and creates the manager in one call.
Returns: Optional[OAuthManager] -- the manager, or None if the service is not known.
Usage Examples¶
Quick authentication for a known service¶
from video_processor.auth import get_auth_manager
manager = get_auth_manager("zoom")
if manager:
result = manager.authenticate()
if result.success:
print(f"Authenticated via {result.method}")
# Use result.access_token for API calls
else:
print(f"Failed: {result.error}")
Custom service configuration¶
from video_processor.auth import AuthConfig, OAuthManager
config = AuthConfig(
service="my_service",
oauth_authorize_url="https://my-service.com/oauth/authorize",
oauth_token_url="https://my-service.com/oauth/token",
client_id_env="MY_SERVICE_CLIENT_ID",
client_secret_env="MY_SERVICE_CLIENT_SECRET",
api_key_env="MY_SERVICE_API_KEY",
scopes=["read", "write"],
)
manager = OAuthManager(config)
token = manager.get_token() # Returns str or None
Using auth in a custom source connector¶
from pathlib import Path
from typing import List, Optional
from video_processor.auth import OAuthManager, AuthConfig
from video_processor.sources.base import BaseSource, SourceFile
class CustomSource(BaseSource):
def __init__(self):
self._config = AuthConfig(
service="custom",
api_key_env="CUSTOM_API_KEY",
)
self._manager = OAuthManager(self._config)
self._token: Optional[str] = None
def authenticate(self) -> bool:
self._token = self._manager.get_token()
return self._token is not None
def list_videos(self, **kwargs) -> List[SourceFile]:
# Use self._token to query the API
...
def download(self, file: SourceFile, destination: Path) -> Path:
# Use self._token for authenticated downloads
...
Logout / clear saved token¶
from video_processor.auth import get_auth_manager
manager = get_auth_manager("zoom")
if manager:
manager.clear_token()
print("Zoom token cleared")
Token storage location¶
All tokens are stored under ~/.planopticon/:
~/.planopticon/
zoom_token.json
notion_token.json
github_token.json
google_token.json
microsoft_token.json
dropbox_token.json
Each file contains a JSON object with access_token, refresh_token (if applicable), expires_at, and client credentials for refresh.