Authentication Methods
| Method | Description | When to Use |
|---|---|---|
| API Key | Environment variable in .env (e.g., OPENAI_API_KEY) | Programmatic access, CI/CD, platform API keys |
| OAuth (interactive login) | /auth login command in the terminal | ChatGPT Plus/Codex plans, Claude Pro, no key management |
/auth login. All other providers — GoogleAI, xAI, ZAI, MiniMax, Moonshot (Kimi), StackSpot, and Ollama — require authentication via API key (set in .env or environment variables)./auth Commands
All authentication commands have auto-completion — just type /auth and press Tab.
View Status
Login via OAuth
Authorize access
Supported Providers
| Provider | Command | Compatible Plans |
|---|---|---|
| OpenAI | /auth login openai-codex | ChatGPT Plus, Codex, Team, Enterprise |
| Anthropic | /auth login anthropic | Claude Pro, Team |
| GitHub Copilot | /auth login github-copilot | Copilot Individual, Business, Enterprise |
| GitHub Models | /auth login github-models | Any GitHub account (PAT) — details |
Logout
Technical Details of OAuth Flows
This section describes how each OAuth flow works internally, including URLs, parameters, and ChatCLI behavior.Anthropic OAuth (PKCE + Manual Code)
The Anthropic flow uses OAuth 2.0 with PKCE (Proof Key for Code Exchange) and a manual copy-paste mechanism for the authorization code.PKCE generation and browser launch
code_verifier and computes the code_challenge using SHA-256 (method S256). The browser opens to the authorization URL with the required parameters.User authorizes in browser
code#state.User pastes code in terminal
# is the state parameter, used in the token exchange.| Parameter | Value |
|---|---|
| Auth URL | https://claude.ai/oauth/authorize |
| Token URL | https://console.anthropic.com/v1/oauth/token |
| Redirect URI | https://console.anthropic.com/oauth/code/callback |
| Client ID | 9d1c250a-e61b-44d9-88ed-5944d1962f5e |
| Scopes | org:create_api_key user:profile user:inference |
| PKCE Method | S256 (SHA-256 code challenge) |
http.Client and the same User-Agent, for the same reason.
OpenAI Codex OAuth (PKCE + Localhost Callback)
The OpenAI flow uses OAuth 2.0 with PKCE and a local HTTP server to automatically capture the callback.Local server starts on port 1455
http://localhost:1455 to receive the OAuth callback.Browser opens to authorization page
state parameter for CSRF validation.User authorizes in browser
http://localhost:1455/auth/callback with the authorization code.| Parameter | Value |
|---|---|
| Auth URL | https://auth.openai.com/oauth/authorize |
| Token URL | https://auth.openai.com/oauth/token |
| Redirect URI | http://localhost:1455/auth/callback |
| Client ID | app_EMoamEEZ73f0CkXaXp7hrann (overridable via CHATCLI_OPENAI_CLIENT_ID) |
| Scopes | openid profile email offline_access |
| PKCE Method | S256 |
GitHub Copilot Device Flow (RFC 8628)
GitHub Copilot uses the Device Authorization Grant (RFC 8628), a flow designed for devices without an integrated browser, such as CLI terminals.Display code in terminal
user_code and the verification URL (https://github.com/login/device) to the user.User authorizes in browser
user_code, and authorizes ChatCLI access.| Parameter | Value |
|---|---|
| Device Code URL | https://github.com/login/device/code |
| Token Polling URL | https://github.com/login/oauth/access_token |
| Client ID | Ov23lifEydOk2Non90tJ (overridable via CHATCLI_COPILOT_CLIENT_ID) |
| Scope | read:user |
| Max Timeout | 15 minutes |
| Response | Action |
|---|---|
authorization_pending | Continue polling normally |
slow_down | Increase interval by +5 seconds |
expired_token | Fail — the device code has expired |
access_denied | Fail — the user denied access |
Complete Flow: From Zero to First Prompt
If you are starting without any configured credentials, follow these steps:Automatic Endpoint Routing (OpenAI)
ChatCLI automatically detects the credential type and routes requests to the correct endpoint:| Credential | Endpoint | API Format |
|---|---|---|
OPENAI_API_KEY | api.openai.com/v1/responses | Responses API (default) |
OPENAI_API_KEY | api.openai.com/v1/chat/completions | Chat Completions (legacy models) |
OAuth (/auth login) | chatgpt.com/backend-api/codex/responses | Responses API (streaming SSE) |
Credential Storage
OAuth credentials are saved with AES-256-GCM encryption at:Encryption Details
| Aspect | Detail |
|---|---|
| Algorithm | AES-256-GCM (authenticated encryption) |
| Key file | ~/.chatcli/.auth-key (32 random bytes) |
| Key permission | 0o600 (owner read/write only) |
| Encrypted format | chatcli-enc:v1: + Base64(nonce + ciphertext) |
| Key generation | Automatic on first use |
~/.chatcli/.auth-key. Unencrypted data from previous versions is automatically detected (absence of the chatcli-enc:v1: prefix) and migrated to the encrypted format on the next save.
Each stored profile contains:
- Access token (encrypted)
- Refresh token (encrypted, when applicable)
- Expiry (timestamp in milliseconds)
- Account ID and email from the provider
- Provider type (anthropic, openai-codex, github-copilot)
Token Validation
ChatCLI implements strict OAuth token validation to prevent use of invalid or expired credentials:Access Token Validation
- Empty access token rejection — Tokens with an empty access token are rejected immediately, without attempting requests. This prevents confusing “unauthorized” errors when credentials are corrupted.
- Expiry validation — The
expiryfield is checked before each request. Expired tokens trigger the refresh flow automatically.
Maximum Token Lifetime (CHATCLI_MAX_TOKEN_LIFETIME)
For production environments with compliance requirements, you can limit the maximum token lifetime:/auth login. This ensures periodic credential rotation even when the refresh token is still valid.| Variable | Description | Default |
|---|---|---|
CHATCLI_MAX_TOKEN_LIFETIME | Maximum lifetime for OAuth/JWT tokens. Accepts Go durations (e.g., 24h, 168h). | 720h (30 days) |
Automatic Token Refresh
ChatCLI automatically renews expired tokens in two layers:Proactive Refresh
IsExpired()) or about to expire (IsExpiringSoon() with a 5-minute margin). If so, the refresh is executed before the request.Reactive Refresh
External CLI Sync
ChatCLI can import credentials from external CLIs viaSyncExternalCliCreds():
| External CLI | Paths Checked | Imported Profile |
|---|---|---|
| Claude Code | ~/.claude/credentials.json or ~/.claude.json | anthropic-oauth-sync |
| OpenAI Codex CLI | ~/.codex/credentials.json or ~/.codex/auth.json | openai-codex-sync |
Credential Resolution Priority
When determining which credential to use for a provider, ChatCLI follows this priority order:Auth-profiles store (OAuth/Token)
auth-profiles.json, including profiles synced from external CLIs. Automatic refresh is applied at this layer.| Prefix | Meaning |
|---|---|
oauth: | Token obtained via OAuth flow |
apikey: | Traditional API key |
token: | Generic token (e.g., Copilot) |
Advanced Configuration
| Variable | Description |
|---|---|
CHATCLI_OPENAI_CLIENT_ID | Allows overriding the OpenAI OAuth client ID |
CHATCLI_COPILOT_CLIENT_ID | Allows overriding the GitHub Copilot Device Flow client ID (default: Ov23lifEydOk2Non90tJ) |
COPILOT_API_BASE_URL | Copilot API base URL for enterprise environments (default: https://api.githubcopilot.com) |
Troubleshooting
Authentication error when clicking the OAuth link (OpenAI)
Authentication error when clicking the OAuth link (OpenAI)
Code does not appear after authorizing (Anthropic)
Code does not appear after authorizing (Anthropic)
Anthropic: 403 error or connection refused during token exchange
Anthropic: 403 error or connection refused during token exchange
http.Client for Anthropic token exchange to avoid TLS fingerprinting by Cloudflare. If you are developing, ensure that the exchangeAnthropicToken function uses a client without a custom transport.Provider does not appear in /switch after login
Provider does not appear in /switch after login
/auth status to verify that the token was saved correctly. If needed, try /auth logout <provider> followed by /auth login <provider>.GitHub Copilot: authorization denied by user
GitHub Copilot: authorization denied by user
GitHub Copilot: device code expired
GitHub Copilot: device code expired
/auth login github-copilot again to generate a new code.Expired token
Expired token
- Proactively: when resolving credentials, if the token is expired or within the 5-minute margin, a refresh is attempted before the request.
- Reactively: if a request returns a 401 error, ChatCLI invalidates the credential cache, attempts to refresh the OAuth token, recreates the client, and retries the request automatically.
/auth login.GitHub Copilot tokens (Device Flow) do not expire and do not have a refresh token — they are persistent until manually revoked.