Skip to content

server: Codex auth via ChatGPT subscription login (device-auth) instead of API key #83

Description

@louisreingold

Problem

Codex sessions are billed at OpenAI API rates. The Settings page's "Codex token" is an OpenAI API key, forwarded to every codex exec turn as CODEX_API_KEY via scripts/in-workspace.sh. There is no way to use a ChatGPT subscription instead. Nothing on the box is logged in to Codex: the host has no Codex CLI, and each env's workspace/.codex/ holds only a config.toml (codex login status → "Not logged in").

Why it's not a one-liner like Claude

Claude auth is a single long-lived OAuth token (CLAUDE_CODE_OAUTH_TOKEN) passed as an env var. Codex has no equivalent. ChatGPT sign-in yields a token bundle in $CODEX_HOME/auth.json that Codex itself refreshes (after ~8 days, or on a 401) and writes back. OpenAI documents copying that file into containers / CI runners as supported, with one hard rule: one copy per serialized stream — never share a copy across concurrent jobs, because a refresh rotates the token and strands the other copies. Our fleet runs Codex turns in many envs concurrently, so the design has to respect that.

Docs: https://developers.openai.com/codex/auth (Login on headless devices) and https://developers.openai.com/codex/auth/ci-cd-auth (refresh + persistence rules).

Proposed design — server owns one canonical login and fans it out

  1. Log in once, on the box, into a server-owned dir (e.g. data/codex/):
    • Settings button "Sign in with ChatGPT": server runs codex login --device-auth in a throwaway container from the existing workspace image with that dir mounted as CODEX_HOME, streams the URL + one-time code to the UI; operator approves in any browser. Codex 0.154 (current workspace image) supports this. Requires "device code login" enabled once in the operator's ChatGPT security settings.
    • Fallback: codex login on a laptop, paste the contents of ~/.codex/auth.json into a Settings textarea.
  2. Fan out per turn, host-side. Each env's workspace is a host bind mount, so before spawning a Codex turn the server copies the canonical auth.json into workspace/.codex/, sets cli_auth_credentials_store = "file" in that env's config.toml, and does not export CODEX_API_KEY. No container changes, no rebuilds.
  3. Single writer keeps the canonical fresh. A weekly server job runs a one-line codex exec in the throwaway container against the canonical dir, refreshing the bundle well inside the 8-day window. Envs always receive a fresh copy at turn start, so they never take the refresh path themselves → no concurrent rotation. Safety net: after a turn, copy an env's file back if its last_refresh is newer than the canonical's.
  4. Mode switch, not replacement. Settings gains "Codex auth: ChatGPT login / API key" with a status line (account, last refresh). API-key path stays. AGENT_USAGE.md / MCP docs get a note.

Verify before building (each a 5-minute check in a workspace container)

  • Does a present (or empty) CODEX_API_KEY env var override auth.json? Decides whether in-workspace.sh must unset it in ChatGPT mode.
  • Do gpt-6-astra and -c model_reasoning_effort= work under subscription auth with codex exec?
  • Is the access token in the bundle long-lived enough that a fresh per-turn copy never needs a mid-turn refresh?

Scope / effort

~1 day, all server-side: settings fields + two routes, a small helper for the throwaway container and file sync, the pre-turn copy in the agent spawn path (src/claude.js codex agent), Settings UI. Deploys with a server restart, no env rebuilds.

Caveats

  • auth.json holds live tokens → same secret handling as the API key (masked, redacted from logs, never in transcripts).
  • Subscription usage windows apply instead of pay-as-you-go.
  • OpenAI labels this an advanced flow and recommends API keys for automation; if refresh behaviour changes, the recovery path is "sign in again from Settings".

🤖 Generated with Claude Code

https://claude.ai/code/session_018puUXTJeSWdbG8ZMCFVqQL

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions