This document describes the authentication system for the Codex app-server, covering the three authentication modes (API key, ChatGPT OAuth, and external auth tokens), account management API endpoints, and the authentication lifecycle. For configuration of authentication requirements and policies, see Sandbox and Approval Policies.
The app-server supports three authentication modes for accessing OpenAI-backed model providers:
Authentication state is managed by AuthManager in the core layer, while the app-server's CodexMessageProcessor handles protocol-level authentication requests via JSON-RPC endpoints.
Sources: codex-rs/app-server/README.md17-18 codex-rs/app-server-protocol/src/protocol/common.rs27-42
The protocol defines three authentication modes:
| Mode | Description | Token Storage | Refresh |
|---|---|---|---|
ApiKey | Static API key | Persisted by Codex | N/A |
Chatgpt | ChatGPT OAuth | Persisted and managed by Codex | Automatic |
ChatgptAuthTokens | External tokens | Memory only | Host app's responsibility |
Sources: codex-rs/app-server-protocol/src/protocol/common.rs27-42
In this mode, the client supplies an OpenAI API key via account/login/start with LoginAccountParams::ApiKey. The key is persisted by Codex and reused for subsequent sessions.
Sources: codex-rs/app-server/src/codex_message_processor.rs833-838 codex-rs/core/src/auth.rs187
Codex orchestrates a browser-based OAuth flow, persists refresh tokens, and automatically refreshes access tokens when they expire.
Sources: codex-rs/app-server/src/codex_message_processor.rs839-841 codex-rs/login/src/lib.rs
UNSTABLE - FOR OPENAI INTERNAL USE ONLY
This mode allows host applications (such as ChatGPT desktop) to manage authentication externally and supply tokens to Codex. Tokens are stored in memory only and never persisted. The host app must handle token refresh.
When a request fails with HTTP 401, Codex sends a chatgptAuthTokens/refresh server request to the client, which must respond with a fresh token.
Sources: codex-rs/app-server/src/codex_message_processor.rs842-879 codex-rs/app-server-protocol/src/protocol/common.rs34-41
The CodexMessageProcessor routes account-related requests to specific handlers:
| Request | Method | Handler |
|---|---|---|
account/login/start | login_v2 | Dispatches to mode-specific login |
account/logout | logout_v2 | Clears auth state |
account/login/cancel | cancel_login_v2 | Cancels in-progress OAuth flow |
account/read | get_account | Returns account details |
account/rateLimits/read | get_account_rate_limits | Returns rate limit snapshot |
Sources: codex-rs/app-server/src/codex_message_processor.rs683-700 codex-rs/app-server/src/codex_message_processor.rs819-825
The unified login endpoint account/login/start accepts a discriminated union LoginAccountParams:
The handler dispatches based on the variant:
Sources: codex-rs/app-server/src/codex_message_processor.rs833-897
Logout clears the authentication state and removes persisted credentials:
Sources: codex-rs/app-server/src/codex_message_processor.rs1147-1170
The account/read endpoint returns the current authentication state and account details:
Response includes:
authMode: Current authentication mode ("apiKey", "chatgpt", or "chatgptAuthTokens")account: Account identifier, plan type (if available)authTokens: For external auth mode only; otherwise nullWhen refreshToken: true is specified, Codex performs a proactive token refresh before returning (only for managed chatgpt mode).
Sources: codex-rs/app-server/src/codex_message_processor.rs1172-1270 codex-rs/app-server-protocol/src/protocol/v2.rs516-523
The account/rateLimits/read endpoint returns current rate limit information from the backend:
The response includes windows for different rate limit types (requests per minute, tokens per minute, etc.).
Sources: codex-rs/app-server/src/codex_message_processor.rs1272-1289
In chatgpt auth mode, Codex automatically refreshes access tokens when they expire:
The refresh is transparent to the user and happens automatically within the core layer.
Sources: codex-rs/core/src/auth.rs
For external auth token mode, Codex cannot refresh tokens itself. Instead, it emits a chatgptAuthTokens/refresh server request:
| Field | Type | Description |
|---|---|---|
reason | ChatgptAuthTokensRefreshReason | Why refresh is needed (e.g., "unauthorized") |
previousAccountId | string? | Hint for multi-account clients |
The client must respond with ChatgptAuthTokensRefreshResponse containing fresh credentials. If the client fails to respond or responds with an error, the operation fails.
Sources: codex-rs/app-server-protocol/src/protocol/v2.rs1533-1569 codex-rs/app-server/src/codex_message_processor.rs2735-2822
The AuthManager (located in core) maintains the current authentication state as CodexAuth:
The AuthManager provides:
login_with_api_key() — persist and set API key authlogin_with_chatgpt_auth_tokens() — set external auth (memory-only)logout() — clear auth state and remove persisted credentialsget_current_auth() — retrieve current auth for backend requestsSources: codex-rs/core/src/auth.rs
Authentication credentials are stored differently based on mode:
| Mode | Storage | Location |
|---|---|---|
ApiKey | Keyring (with file fallback) | OS keychain or ~/.codex/credentials |
Chatgpt | Keyring (with file fallback) | OS keychain or ~/.codex/credentials |
ChatgptAuthTokens | None (memory only) | N/A |
The keyring integration uses the keyring crate with fallback to encrypted file storage on systems where the OS keychain is unavailable.
Sources: codex-rs/core/src/auth.rs
The app-server emits notifications to keep clients informed of authentication state changes:
| Notification | When Emitted |
|---|---|
account/loginCompleted | After successful login via any mode |
account/updated | When account details change (e.g., plan upgrade) |
account/rateLimitsUpdated | When rate limit info is refreshed |
authStatusChanged | When auth state transitions (deprecated v1) |
Sources: codex-rs/app-server-protocol/src/protocol/v2.rs1498-1532
During a ChatGPT OAuth login, clients can cancel the in-progress flow:
The loginId is a UUID that uniquely identifies the login attempt. If no active login matches the provided ID, the response has status: "notFound".
Sources: codex-rs/app-server/src/codex_message_processor.rs1301-1334 codex-rs/app-server-protocol/src/protocol/v2.rs1454-1467
The app-server maintains backward compatibility with v1 authentication endpoints:
| v1 Endpoint | v2 Equivalent | Status |
|---|---|---|
loginApiKey | account/login/start (ApiKey) | Deprecated |
loginChatGpt | account/login/start (Chatgpt) | Deprecated |
cancelLoginChatGpt | account/login/cancel | Deprecated |
logoutChatGpt | account/logout | Deprecated |
getAuthStatus | account/read | Deprecated |
New integrations should use the v2 endpoints exclusively.
Sources: codex-rs/app-server/src/codex_message_processor.rs737-760
The CodexMessageProcessor::process_request method routes incoming authentication requests:
Each handler interacts with AuthManager and sends responses back via the OutgoingMessageSender.
Sources: codex-rs/app-server/src/codex_message_processor.rs527-831 codex-rs/app-server/src/codex_message_processor.rs833-1289
Refresh this wiki