A Python CLI tool for authenticating with Frame.io using Adobe's Native App OAuth flow with PKCE (Proof Key for Code Exchange).
This solution uses a hybrid Python + Electron approach to automatically capture custom URL scheme redirects, eliminating the need for manual copy/pasting of authorization codes.
- Python generates the authorization URL with PKCE parameters
- Electron helper registers as a URL scheme handler and opens your browser
- You sign in and click "Allow access"
- When prompted "Open FrameioOAuth?", click Open
- Electron captures the redirect and passes it back to Python
- Python exchanges the code for tokens and saves them
| Platform | Status | Notes |
|---|---|---|
| macOS | β Supported | Universal binary (Intel + Apple Silicon) |
| Linux | β Supported | x64 and arm64 builds available |
| Windows | π§ Experimental | Build script available, testing welcome |
Note: The core Python OAuth logic is cross-platform. The Electron helper (for capturing custom URL scheme redirects) supports macOS and Linux, with experimental Windows support.
- Python 3.9+
- Node.js 18+ (for Electron helper) β verify with
node --version - macOS, Linux, or Windows (see Platform Support)
- A Frame.io Developer Console application with Native App credentials
cd frameio-python-oauth
python3 -m venv venv
source venv/bin/activate # On Windows: venv\Scripts\activate
pip install -r requirements.txtnode --version)
cd electron-helper
npm installThen build for your platform:
| Platform | Command | Output |
|---|---|---|
| macOS | npm run package |
FrameioOAuth-darwin-universal/ |
| Linux | npm run package:linux |
FrameioOAuth-linux-x64/ |
| Windows | npm run package:win |
FrameioOAuth-win32-x64/ |
# Example for Linux:
npm run package:linux
cd ..This creates the Electron app that handles the custom URL scheme redirect.
Copy the example environment file and add your credentials:
cp .env.example .envEdit .env with your Frame.io app credentials from the Adobe Developer Console:
ADOBE_CLIENT_ID=your_client_id_here
ADOBE_REDIRECT_URI=adobe+your_unique_scheme://adobeid/your_client_id
ADOBE_SCOPES="email additional_info.roles profile openid offline_access"
python src/cli.py authThis will:
- Open your browser to Adobe's sign-in page
- After you authorize, click "Open" when prompted to open FrameioOAuth
- Save tokens to
~/.frameio-tokens.json
For debugging, use the --verbose flag to see detailed information:
python src/cli.py auth --verbosepython src/cli.py testVerifies your token works by making an API call.
python src/cli.py tokenShows your current token status and expiration.
python src/cli.py logoutRemoves saved tokens.
frameio-python-oauth/
βββ README.md
βββ requirements.txt
βββ .env.example
βββ .gitignore
βββ src/
β βββ __init__.py
β βββ cli.py # Command-line interface
β βββ auth.py # OAuth authentication logic
β βββ pkce.py # PKCE helper functions
β βββ electron_auth.py # Electron integration
βββ electron-helper/
βββ package.json
βββ main.js # Electron URL scheme handler
βββ FrameioOAuth-{platform}/ # Built app (created by npm run package*)
PKCE (Proof Key for Code Exchange) is used instead of a client secret for native/public applications:
- Generate a random
code_verifier - Create a
code_challenge= SHA256(code_verifier) - Send
code_challengewith the authorization request - Send
code_verifierwhen exchanging the code for tokens - Adobe verifies that SHA256(code_verifier) matches the original challenge
This prevents authorization code interception attacks.
By default, tokens are stored in ~/.frameio-tokens.json with owner-only permissions (0600). While this is reasonably secure for development and personal use, for production applications you should use a more secure storage mechanism:
| Method | Best For | Python Library |
|---|---|---|
| System Keychain | Desktop apps, highest security | keyring |
| Encrypted File | Cross-platform, self-contained | cryptography |
| Environment Variables | CI/CD, containers | Built-in os.environ |
| Secret Manager | Cloud deployments | AWS Secrets Manager, GCP Secret Manager, etc. |
import keyring
# Store token
keyring.set_password("frameio", "access_token", access_token)
keyring.set_password("frameio", "refresh_token", refresh_token)
# Retrieve token
access_token = keyring.get_password("frameio", "access_token")The keyring library automatically uses the appropriate secure storage:
- macOS: Keychain
- Windows: Windows Credential Locker
- Linux: Secret Service API (GNOME Keyring, KWallet)
from cryptography.fernet import Fernet
# Generate key once and store securely (e.g., in keychain)
key = Fernet.generate_key()
cipher = Fernet(key)
# Encrypt and save
encrypted = cipher.encrypt(json.dumps(tokens).encode())
Path("~/.frameio-tokens.enc").expanduser().write_bytes(encrypted)
# Decrypt and load
encrypted = Path("~/.frameio-tokens.enc").expanduser().read_bytes()
tokens = json.loads(cipher.decrypt(encrypted))This implementation includes several security measures:
- β PKCE - Prevents authorization code interception
- β State Parameter - Protects against CSRF attacks (authentication aborts on mismatch)
- β
Restricted File Permissions - Token and IPC files are owner-readable only (
0600) - β
Minimal Debug Output - Sensitive data hidden unless
--verboseis used - β Automatic Token Refresh - Minimizes exposure of long-lived credentials
- Never commit tokens - The
.gitignoreexcludes token files, but always verify - Rotate credentials - Use
logoutcommand when done testing - Use minimal scopes - Only request the permissions your app needs
- Secure your
.env- Contains your client ID; don't commit to version control
- Cause: Your Node.js version is too old. Electron requires Node.js 18+.
- Fix: Upgrade Node.js to version 18 or later. Check your version with
node --version. - If using
nvm, run:nvm install 18 && nvm use 18 - If using system packages, update via your package manager or download from nodejs.org
- Make sure you've built the Electron app for your platform:
- macOS:
cd electron-helper && npm run package - Linux:
cd electron-helper && npm run package:linux - Windows:
cd electron-helper && npm run package:win
- macOS:
- The packaged app needs to be in the appropriate directory (e.g.,
FrameioOAuth-linux-x64/for Linux)
- Click "Open" when the browser asks to open FrameioOAuth
- If the dialog doesn't appear, the URL scheme might not be registered
- Verify your
ADOBE_CLIENT_IDandADOBE_REDIRECT_URImatch your Adobe Console settings - Make sure scopes include
offline_accessfor refresh tokens
This project is licensed under the MIT License - see the LICENSE file for details.
Built for the Frame.io developer community.