Skip to main content
ChatCLI supports authentication via OAuth 2.0 with PKCE and Device Flow (RFC 8628) for providers that offer these options. This allows you to use your existing plan (such as ChatGPT Plus, Codex, Claude Pro, or GitHub Copilot) directly in the terminal, without needing to generate or paste API keys.

Authentication Methods

MethodDescriptionWhen to Use
API KeyEnvironment variable in .env (e.g., OPENAI_API_KEY)Programmatic access, CI/CD, platform API keys
OAuth (interactive login)/auth login command in the terminalChatGPT Plus/Codex plans, Claude Pro, no key management
Both methods can coexist. ChatCLI prioritizes OAuth credentials when both are available.
OAuth support varies by provider. Only OpenAI, Anthropic, and GitHub Copilot support OAuth login via /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

/auth status
Displays the authentication status of all configured providers, including credential type (API Key vs OAuth), token validity, and source.

Login via OAuth

/auth login openai-codex
/auth login anthropic
/auth login github-copilot
/auth login github-models
1

Browser opens automatically

The browser opens to the provider’s login page.
2

Authorize access

OpenAI: a local HTTP server captures the callback automatically (port 1455).Anthropic: after authorizing, copy the code displayed on the page and paste it in the terminal.GitHub Copilot: enter the device code displayed in the terminal on the GitHub page.
3

Provider available immediately

The provider appears immediately in /switchno app restart needed.

Supported Providers

ProviderCommandCompatible Plans
OpenAI/auth login openai-codexChatGPT Plus, Codex, Team, Enterprise
Anthropic/auth login anthropicClaude Pro, Team
GitHub Copilot/auth login github-copilotCopilot Individual, Business, Enterprise
GitHub Models/auth login github-modelsAny GitHub account (PAT) — details

Logout

/auth logout openai-codex
/auth logout anthropic
/auth logout github-copilot
Removes the stored OAuth credentials for the specified provider.

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.
1

PKCE generation and browser launch

ChatCLI generates a random code_verifier and computes the code_challenge using SHA-256 (method S256). The browser opens to the authorization URL with the required parameters.
2

User authorizes in browser

The user logs into Claude and authorizes access. The console page displays a code in the format code#state.
3

User pastes code in terminal

ChatCLI prompts the user to paste the displayed code. The portion after the # is the state parameter, used in the token exchange.
4

Code exchanged for tokens

ChatCLI sends the code, state, and code_verifier to the token endpoint and receives an access_token + refresh_token.
ParameterValue
Auth URLhttps://claude.ai/oauth/authorize
Token URLhttps://console.anthropic.com/v1/oauth/token
Redirect URIhttps://console.anthropic.com/oauth/code/callback
Client ID9d1c250a-e61b-44d9-88ed-5944d1962f5e
Scopesorg:create_api_key user:profile user:inference
PKCE MethodS256 (SHA-256 code challenge)
The token exchange uses a plain http.Client (no custom transport) to avoid TLS fingerprinting issues with Cloudflare. The User-Agent: claude-cli/2.1.2 (external, cli) header is required. Using an HTTP client with LoggingTransport or a custom transport causes rejection by Anthropic’s CDN.
Token refresh also uses the plain 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.
1

Local server starts on port 1455

ChatCLI starts an HTTP server at http://localhost:1455 to receive the OAuth callback.
2

Browser opens to authorization page

The browser opens to the OpenAI authorization URL with the PKCE challenge and a state parameter for CSRF validation.
3

User authorizes in browser

After login and authorization, the browser redirects to http://localhost:1455/auth/callback with the authorization code.
4

CSRF validation and token exchange

The local server validates the state parameter against the originally generated value (CSRF protection), exchanges the code for tokens, and shuts down the server.
ParameterValue
Auth URLhttps://auth.openai.com/oauth/authorize
Token URLhttps://auth.openai.com/oauth/token
Redirect URIhttp://localhost:1455/auth/callback
Client IDapp_EMoamEEZ73f0CkXaXp7hrann (overridable via CHATCLI_OPENAI_CLIENT_ID)
Scopesopenid profile email offline_access
PKCE MethodS256
Token refresh is automatic and uses the standard HTTP client with logging.

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.
1

Request device code

ChatCLI requests a device_code and a user_code from the GitHub endpoint.
2

Display code in terminal

ChatCLI displays the user_code and the verification URL (https://github.com/login/device) to the user.
3

User authorizes in browser

The user visits the URL, enters the user_code, and authorizes ChatCLI access.
4

Polling until authorization

ChatCLI polls the token endpoint every 5 seconds until it receives the access token or a terminal error.
ParameterValue
Device Code URLhttps://github.com/login/device/code
Token Polling URLhttps://github.com/login/oauth/access_token
Client IDOv23lifEydOk2Non90tJ (overridable via CHATCLI_COPILOT_CLIENT_ID)
Scoperead:user
Max Timeout15 minutes
Polling responses:
ResponseAction
authorization_pendingContinue polling normally
slow_downIncrease interval by +5 seconds
expired_tokenFail — the device code has expired
access_deniedFail — the user denied access
GitHub Copilot tokens are non-expiring and do not have a refresh token. They persist until manually revoked on the GitHub settings page.

Complete Flow: From Zero to First Prompt

If you are starting without any configured credentials, follow these steps:
1

Start ChatCLI

Works even without configured credentials.
chatcli
2

Log in via OAuth

/auth login openai-codex
3

Authorize in the browser

The browser opens automatically to the provider’s login page.
4

Switch to the provider

/switch
5

Send your first prompt

Hello, how are you?

Automatic Endpoint Routing (OpenAI)

ChatCLI automatically detects the credential type and routes requests to the correct endpoint:
CredentialEndpointAPI Format
OPENAI_API_KEYapi.openai.com/v1/responsesResponses API (default)
OPENAI_API_KEYapi.openai.com/v1/chat/completionsChat Completions (legacy models)
OAuth (/auth login)chatgpt.com/backend-api/codex/responsesResponses API (streaming SSE)
You don’t need to configure anything — routing is automatic based on the token type.

Credential Storage

OAuth credentials are saved with AES-256-GCM encryption at:
~/.chatcli/auth-profiles.json

Encryption Details

AspectDetail
AlgorithmAES-256-GCM (authenticated encryption)
Key file~/.chatcli/.auth-key (32 random bytes)
Key permission0o600 (owner read/write only)
Encrypted formatchatcli-enc:v1: + Base64(nonce + ciphertext)
Key generationAutomatic on first use
The encryption key is automatically generated and stored at ~/.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)
To customize the storage directory, set the environment variable:
export CHATCLI_AUTH_DIR="~/.config/chatcli/auth"

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 expiry field 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:
# Limit tokens to 24 hours (default: 720h / 30 days)
export CHATCLI_MAX_TOKEN_LIFETIME=24h
When the maximum lifetime is reached, the token is invalidated and the user must log in again via /auth login. This ensures periodic credential rotation even when the refresh token is still valid.
VariableDescriptionDefault
CHATCLI_MAX_TOKEN_LIFETIMEMaximum 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

When resolving credentials for a request, ChatCLI checks if the token is expired (IsExpired()) or about to expire (IsExpiringSoon() with a 5-minute margin). If so, the refresh is executed before the request.

Reactive Refresh

If a request returns an HTTP 401 error, ChatCLI invalidates the credential cache, performs the token refresh, recreates the HTTP client, and retries the request automatically — all transparent to the user.
The 5-minute safety margin before actual expiry prevents race conditions where the token expires between the check and the request being sent.
GitHub Copilot tokens (Device Flow) do not expire and do not have a refresh token — they persist until manually revoked.

External CLI Sync

ChatCLI can import credentials from external CLIs via SyncExternalCliCreds():
External CLIPaths CheckedImported Profile
Claude Code~/.claude/credentials.json or ~/.claude.jsonanthropic-oauth-sync
OpenAI Codex CLI~/.codex/credentials.json or ~/.codex/auth.jsonopenai-codex-sync
Import only occurs if the external credentials are fresher than those already stored. This ensures that a login performed directly in ChatCLI is not overwritten by stale data from another CLI.

Credential Resolution Priority

When determining which credential to use for a provider, ChatCLI follows this priority order:
1

Auth-profiles store (OAuth/Token)

OAuth credentials stored in auth-profiles.json, including profiles synced from external CLIs. Automatic refresh is applied at this layer.
2

Environment variables

Variables such as ANTHROPIC_OAUTH_TOKEN, ANTHROPIC_API_KEY, OPENAI_API_KEY, and GITHUB_COPILOT_TOKEN.
Prefix system: credential values may include prefixes that signal the type to the provider:
PrefixMeaning
oauth:Token obtained via OAuth flow
apikey:Traditional API key
token:Generic token (e.g., Copilot)

Advanced Configuration

VariableDescription
CHATCLI_OPENAI_CLIENT_IDAllows overriding the OpenAI OAuth client ID
CHATCLI_COPILOT_CLIENT_IDAllows overriding the GitHub Copilot Device Flow client ID (default: Ov23lifEydOk2Non90tJ)
COPILOT_API_BASE_URLCopilot API base URL for enterprise environments (default: https://api.githubcopilot.com)

Troubleshooting

The Anthropic flow redirects to the console page that displays the authorization code in the format code#state. Copy the full text (including the part after the #) and paste it in the terminal when prompted. ChatCLI separates the code from the state automatically.
This typically occurs when a custom HTTP client (with LoggingTransport) is used. ChatCLI uses a plain 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.
Run /auth status to verify that the token was saved correctly. If needed, try /auth logout <provider> followed by /auth login <provider>.
Make sure your GitHub account has an active Copilot subscription (Individual, Business, or Enterprise). Check at https://github.com/settings/copilot if Copilot is enabled.
The device code has limited validity. If polling exceeds 15 minutes without authorization, the code expires. Run /auth login github-copilot again to generate a new code.
OAuth tokens are automatically renewed in two ways:
  • 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.
If both attempts fail (e.g., refresh token is also expired), log in again with /auth login.GitHub Copilot tokens (Device Flow) do not expire and do not have a refresh token — they are persistent until manually revoked.

Next Steps

Command Reference

Complete list of all available commands.

Configuration (.env)

All available environment variables.

Basic Usage

Learn the fundamentals of ChatCLI.