# Codex CLI Authentication: How the Device-Code Flow Works with ChatGPT

> Learn how Codex CLI uses Rust and OAuth 2.0 device code flow for secure authentication with ChatGPT. Get a one-time code, verify in your browser, and store tokens locally.

- Repository: [OpenAI/codex](https://github.com/openai/codex)
- Tags: how-to-guide
- Published: 2026-03-06

---

**The Codex CLI authenticates users via an OAuth 2.0 Device-Code grant implemented in Rust, where the terminal displays a one-time code for browser verification, polls for authorization, and securely persists tokens to the local credential store.**

The `openai/codex` repository implements a secure, headless-friendly login mechanism for the Codex CLI that avoids manual API key entry. Instead of requiring users to paste tokens into the terminal, the **Codex CLI authentication** system orchestrates a browser-based device-code flow across three distinct architectural layers: the HTTP client library, server-side utilities, and the terminal UI.

## The OAuth 2.0 Device-Code Flow Implementation

The authentication sequence is implemented in [`codex-rs/login/src/device_code_auth.rs`](https://github.com/openai/codex/blob/main/codex-rs/login/src/device_code_auth.rs) and follows the standard device-authorization grant pattern with four distinct stages.

### Stage 1: Requesting the Device Code

The flow begins when the CLI calls `request_user_code` to initiate a session with the Codex account API. This function sends a `POST /deviceauth/usercode` request to the server, which returns a unique `user_code`, a `device_auth_id`, and a polling `interval` that dictates how frequently the client should check for completion.

According to the source in [`device_code_auth.rs`](https://github.com/openai/codex/blob/main/device_code_auth.rs) (lines 62-76), the response includes the verification URL and the temporary device code required for the next steps.

### Stage 2: User Verification Prompt

Once the device code is obtained, `print_device_code_prompt` (lines 49-57) renders the instructions in the terminal. This function displays the one-time code and the verification URL (`{issuer}/codex/device`), instructing the user to open a browser, navigate to the URL, and enter the displayed code to authorize the CLI session.

### Stage 3: Polling for Authorization

While the user completes the browser step, the CLI enters a polling loop via `poll_for_token` (lines 99-126). This function repeatedly posts to `/deviceauth/token` at the server-specified interval, respecting rate limits and terminating after a 15-minute timeout. Upon successful authorization, the server responds with an `authorization_code`, a `code_challenge`, and a `code_verifier` required for the final token exchange.

### Stage 4: Token Exchange and Persistence

The CLI constructs a PKCE pair using `PkceCodes` and invokes `exchange_code_for_tokens` to trade the authorization code for long-lived credentials. This yields an `id_token`, `access_token`, and `refresh_token`. The server utilities in `codex-rs/server` then perform two final operations: `ensure_workspace_allowed` validates the workspace permissions, and `persist_tokens_async` writes the credentials to the CLI’s credential store at `$CODX_HOME`, respecting the user’s configured `cli_auth_credentials_store_mode`.

## Architecture Overview

The authentication system spans three crates in the `codex-rs` workspace, each handling a specific concern:

- **`codex-rs/login`** – Contains the pure HTTP implementation of the device-code flow, including `request_user_code`, `poll_for_token`, and the orchestration function `run_device_code_login` (lines 25-31).
- **`codex-rs/server`** – Handles PKCE generation, token exchange, workspace validation, and secure credential persistence to disk or system keyring.
- **`codex-rs/tui`** – Drives the interactive experience via [`headless_chatgpt_login.rs`](https://github.com/openai/codex/blob/main/headless_chatgpt_login.rs), which updates the `SignInState` enum while the async polling runs, allowing users to cancel or view progress animations.

## Code Implementation Examples

### Stand-Alone Device Code Request

To initiate the flow programmatically without the full TUI:

```rust
use codex_login::{request_device_code, run_device_code_login};
use codex_rs::login::ServerOptions;

#[tokio::main]
async fn main() -> std::io::Result<()> {
    let opts = ServerOptions::default();
    
    // Fetch device code from the server
    let device_code = request_device_code(&opts).await?;
    
    println!("Open {} and enter {}", 
        device_code.verification_url, 
        device_code.user_code);
    
    // Poll and persist tokens
    run_device_code_login(opts).await
}

```

### TUI Integration

Inside the terminal UI, the flow is coordinated through [`headless_chatgpt_login.rs`](https://github.com/openai/codex/blob/main/headless_chatgpt_login.rs):

```rust
// From headless_chatgpt_login::render_device_code_login
let device_code = request_device_code(&opts).await?;
print_device_code_prompt(&device_code.verification_url, &device_code.user_code);

// Update UI state while polling
let _ = complete_device_code_login(opts.clone(), device_code).await?;

```

### Credential Persistence

The final storage operation uses the server utilities:

```rust
crate::server::persist_tokens_async(
    &opts.codex_home,          // e.g., $HOME/.codex
    None,                      // default profile
    tokens.id_token,
    tokens.access_token,
    tokens.refresh_token,
    opts.cli_auth_credentials_store_mode,
).await?;

```

## Key Source Files and Functions

| File Path | Role | Key Functions |
|-----------|------|---------------|
| [`codex-rs/login/src/device_code_auth.rs`](https://github.com/openai/codex/blob/main/codex-rs/login/src/device_code_auth.rs) | Core HTTP flow implementation | `request_user_code`, `poll_for_token`, `run_device_code_login` |
| [`codex-rs/tui/src/onboarding/auth/headless_chatgpt_login.rs`](https://github.com/openai/codex/blob/main/codex-rs/tui/src/onboarding/auth/headless_chatgpt_login.rs) | UI state management and user prompts | `render_device_code_login`, `complete_device_code_login` |
| [`codex-rs/tui/src/onboarding/auth.rs`](https://github.com/openai/codex/blob/main/codex-rs/tui/src/onboarding/auth.rs) | Authentication state definitions | `SignInState` enum |
| [`codex-rs/server/src/lib.rs`](https://github.com/openai/codex/blob/main/codex-rs/server/src/lib.rs) | PKCE handling and token storage | `exchange_code_for_tokens`, `persist_tokens_async` |
| [`docs/authentication.md`](https://github.com/openai/codex/blob/main/docs/authentication.md) | High-level CLI authentication documentation | — |

## Summary

- The **Codex CLI authentication** flow uses the OAuth 2.0 Device-Code grant to enable browser-based login without manual API key entry.
- The `request_user_code` function initiates the flow by posting to `/deviceauth/usercode`, while `poll_for_token` handles the 15-minute polling window to `/deviceauth/token`.
- Token exchange employs PKCE verification via `PkceCodes` to securely obtain `id_token`, `access_token`, and `refresh_token` credentials.
- Credentials are persisted to `$CODX_HOME` through `persist_tokens_async`, supporting both encrypted keyring and plaintext storage modes based on user preference.
- The terminal UI layer in [`headless_chatgpt_login.rs`](https://github.com/openai/codex/blob/main/headless_chatgpt_login.rs) manages user-facing state updates and cancellation handling during the asynchronous authorization wait.

## Frequently Asked Questions

### Does Codex CLI support API key authentication instead of the device-code flow?

No, as implemented in the current `openai/codex` source, the CLI exclusively uses the OAuth 2.0 Device-Code flow for ChatGPT authentication. The codebase does not expose a manual API key entry path; all authentication routes through `run_device_code_login` and the browser-based verification URL.

### Where are the authentication tokens stored after a successful login?

The tokens are written to the local credential store via `persist_tokens_async` in the `codex-rs/server` crate. By default, this resolves to the `$CODX_HOME` directory (typically `$HOME/.codex`), though the exact storage mechanism—encrypted system keyring versus plaintext file—depends on the `cli_auth_credentials_store_mode` configuration option set during the exchange phase.

### What happens if I don't complete the browser verification within 15 minutes?

The `poll_for_token` function enforces a hard 15-minute timeout while polling `/deviceauth/token`. If the user does not authorize the device code in the browser within this window, the function returns an error, the CLI aborts the login attempt, and the user must restart the flow by invoking `request_user_code` again to generate a new one-time code.

### Can the device-code authentication be used in headless or CI environments?

While the underlying HTTP implementation in [`device_code_auth.rs`](https://github.com/openai/codex/blob/main/device_code_auth.rs) supports headless operation, the standard flow requires a human to open a browser and manually enter the `user_code`. For fully automated environments, the current implementation would require pre-existing valid tokens in the `$CODX_HOME` credential store, as there is no non-interactive authentication method exposed in the public API surface.