# Authentication

URL: https://developertoolkit.ai/en/codex/quick-start/authentication/
Description: Choose ChatGPT sign-in or an API key for Codex, store credentials safely, sign in over SSH and in CI, and prove it worked with login status and doctor.

Codex authenticates with either a ChatGPT sign-in, which draws on a ChatGPT plan's Codex allowance and is required for Codex cloud, or an OpenAI API key, which bills per token and suits local and CI work. The Codex CLI stores the credential in `~/.codex/auth.json` by default, and `codex login status` and `codex doctor` confirm which method is active.

You installed Codex, ran `codex`, and the sign-in screen offers ChatGPT, a device code, and an API key. On your laptop the choice takes a second. On a build server with no browser, in a Docker container, or in a GitHub Actions job, the wrong choice shows up later as a CI step that says "Not logged in" while the agent runs fine, or as `codex cloud` refusing to start.

This page is for the **developer** setting up Codex on a laptop, a remote box, or a CI runner, and for the **tech lead** who wants every machine on the team signed in the same, checkable way. Every command and message below was checked against Codex CLI 0.157.1 on 2026-09-26. Install Codex first with the [installation guide](/en/codex/quick-start/installation/).

## What you get from a checked Codex sign-in

- A decision on ChatGPT sign-in versus API key, made on billing and on the features each one unlocks.
- Credentials stored where your security policy wants them: a file, the OS keyring, or memory only.
- A working sign-in on machines without a browser: SSH sessions, containers, and CI runners.
- A two-command check that proves which method is active, so nobody has to guess.
- A team-wide rule that pins the login method and the ChatGPT workspace.

## Which Codex sign-in method should you choose?

Pick by who pays and which features you need. The sign-in screen in 0.157.1 offers **Sign in with ChatGPT**, **Sign in with Device Code**, **Provide your own API key**, and Amazon Bedrock for organizations that route Codex through AWS.

| Situation | Use | Why |
| --- | --- | --- |
| You have a ChatGPT Plus, Pro, Business, Edu, or Enterprise plan | ChatGPT sign-in | Codex usage comes out of the plan's allowance, and cloud tasks, `/usage`, and remote control work |
| A machine with no browser, and you want your ChatGPT plan | Device code | Same account as ChatGPT sign-in; you finish the sign-in on any other device |
| A CI job or script calling `codex exec` | API key in `CODEX_API_KEY` | Usage-based billing on your OpenAI Platform account, no personal session on a shared runner |
| You want Codex costs separate from any ChatGPT plan | API key | One bill per API organization and its policies |
| Your company runs models through AWS | Amazon Bedrock | See the [enterprise governance guide](/en/codex/team/enterprise-governance/) |

OpenAI's pricing page (read 2026-10-12) also lists Free and Go plans, for which it names only "GPT-6 Luna at Standard speed in the desktop app, subject to rollout"; the plan table on [Codex credits, usage limits and cost optimization](/en/codex/operate/cost-management/) lists every plan and what it includes. For an API key, the page says "No cloud-based features". Since 0.161.0 (2026-10-07), API-key model discovery is on by default (`api_key_model_discovery` is stable and on in 0.162.1), so a pipeline that logs in with an API key can behave differently from an older pin; re-run its smoke test after you upgrade.

An API key does not unlock everything. In Codex CLI 0.157.1, these features refuse API-key auth and ask for a ChatGPT sign-in:

- `codex cloud` ("Not signed in. Please run 'codex login' to sign in with ChatGPT").
- Remote control ("remote control requires ChatGPT authentication; API key auth is not supported").
- The remote plugin catalog and shared plugins.
- `/usage` and rate-limit readouts in the terminal UI.

The model you get depends on the plan or API organization, not on the sign-in screen. The [models hub](/en/ecosystem/models/) lists what Codex runs by default and what each model costs.

## Sign in to Codex and prove it worked

**ChatGPT sign-in**

Run this in a terminal on a machine with a browser:

```bash
codex login
```

Codex starts a local callback server on `127.0.0.1:1455`, opens the browser, and stores the returned tokens when you finish signing in. Running `codex` with no credentials shows the same choice in the terminal UI.

**API key**

Pipe the key from an environment variable so it never appears in your shell history or on the command line:

```bash
printenv OPENAI_API_KEY | codex login --with-api-key
```

Create the key in your OpenAI Platform account and keep it in a password manager or secret store. Codex stores the key in its credential store, so the environment variable is only needed once.

The desktop app and the IDE extension offer sign-in on their own screens. The CLI and the IDE extension read credentials from the same `CODEX_HOME` directory (default `~/.codex`), so one sign-in serves both as long as `CODEX_HOME` matches.

Now check the result instead of assuming it:

1. Ask Codex which method it holds. The exit code is `0` when signed in and `1` when not, so scripts can branch on it:

   ```bash
   codex login status
   ```

   Expect `Logged in using ChatGPT`, or `Logged in using an API key - ` followed by the key with its middle masked. The message goes to stderr, so redirect with `2>&1` when you capture it.

2. Run the diagnosis. The `auth` row names the source of the credentials, including keys that come from the environment, which `codex login status` does not see:

   ```bash
   codex doctor --summary
   ```

3. For an onboarding ticket or a support case, save the redacted report and attach it:

   ```bash
   codex doctor --json > codex-doctor.json
   ```

4. If you chose ChatGPT sign-in and need cloud tasks, confirm them from the terminal. `codex cloud list` prints your cloud tasks without opening the TUI, and a task list, even an empty one, proves the cloud side accepts your account:

   ```bash
   codex cloud list
   ```

The tech lead signs off on the `codex doctor --json` file, not on a screenshot of a sign-in page: the `auth` row and the version are in it, and the report redacts secrets.

**Copy-paste prompt: audit Codex credential hygiene in this repository**

```text
Do not modify any files and do not print any secret values.
1. Search this repository, including CI workflow files, Dockerfiles,
   scripts, and .env* files, for OpenAI or Codex credentials: strings
   starting with "sk-", and assignments to OPENAI_API_KEY or CODEX_API_KEY
   that use a literal value instead of a secret reference.
2. Check that .gitignore ignores .env files and any auth.json.
3. List every place a CI job passes a key to Codex and say whether it
   comes from the platform's secret store.
Output a table: file, line, finding, risk (high/medium/low), fix.
Mask any value you find to its first 4 characters.
```

## Where does Codex store credentials?

The `cli_auth_credentials_store` key in `~/.codex/config.toml` decides. The default is `file`.

```toml
# ~/.codex/config.toml
cli_auth_credentials_store = "auto"
```

| Value | Where credentials live | Use it when |
| --- | --- | --- |
| `file` (default) | `$CODEX_HOME/auth.json` | You need the credential to survive reboots on a machine without a keyring |
| `keyring` | macOS Keychain, Windows Credential Manager, or Linux Secret Service | Your laptop has an unlocked keyring and you want no token file on disk. Codex fails if the keyring is unavailable |
| `auto` | Keyring when available, otherwise `auth.json` | Most laptops: the keyring where it works, a file where it does not |
| `ephemeral` | Memory, for the current process only | Shared or short-lived machines where nothing may be written to disk |

`auth.json` holds live tokens or your API key. Never commit it, paste it into a ticket, or bake it into a container image. Never write a raw key into `config.toml`.

## Sign in to Codex over SSH, in a container, or on a headless server

The browser sign-in needs the browser to reach Codex's callback server on `localhost:1455`. On a remote machine it cannot, so use one of these.

**Device code (first choice)**

Run this on the remote machine:

```bash
codex login --device-auth
```

Codex prints a URL and a one-time code. Open the URL on any device, sign in, and enter the code. The device code expires after 15 minutes. If Codex reports that device code login is not enabled, use SSH port forwarding instead.

**SSH port forwarding**

Forward the callback port, then sign in from the forwarded session:

```bash
# On your laptop
ssh -L 1455:localhost:1455 -L 1457:localhost:1457 user@remote

# In that SSH session, on the remote machine
codex login
```

Open the printed URL in your laptop's browser. Port `1457` is the fallback Codex uses when `1455` is already taken, so forwarding both covers that case.

**Copy the credential (last resort)**

Sign in on your laptop, then copy the file over an encrypted channel:

```bash
ssh user@remote 'mkdir -p ~/.codex && chmod 700 ~/.codex'
scp ~/.codex/auth.json user@remote:~/.codex/auth.json
ssh user@remote 'chmod 600 ~/.codex/auth.json && codex login status'
```

This works only with `file` storage, and the remote machine now holds your personal session. Use it on machines only you can access, and run `codex logout` there when you finish.

## Authenticate Codex in CI without a personal session

A CI runner must never hold a developer's ChatGPT session. Give `codex exec` an API key through the `CODEX_API_KEY` environment variable, read from the CI platform's secret store, and keep nothing on disk. The [Codex CI/CD guide](/en/codex/ship/ci-cd/) covers the `openai/codex-action` GitHub Action; this is the shell-level pattern it builds on.

```yaml
# .github/workflows/codex-check.yml
name: codex-check
on: pull_request
permissions:
  contents: read
jobs:
  codex:
    # Skip pull requests from forks: they get no secrets
    if: github.event.pull_request.head.repo.full_name == github.repository
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v7
        with:
          fetch-depth: 0
          persist-credentials: false
      - run: npm install -g @openai/codex@0.157.1
      - name: Check Codex sees a credential
        env:
          CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
        run: |
          codex doctor --summary --no-color > doctor.txt || true
          grep -E '✓ auth' doctor.txt
      - name: Read-only review
        env:
          CODEX_API_KEY: ${{ secrets.CODEX_API_KEY }}
        run: |
          codex -c cli_auth_credentials_store=ephemeral \
            -c default_permissions=":read-only" \
            exec --ephemeral \
            "Summarize the risk in this pull request's diff against origin/main. Do not modify files."
```

`on: pull_request` does not pass repository secrets to pull requests from forks, which is what you want: a fork's code never runs next to your key. The `if:` line skips fork pull requests outright, so the job runs only for branches pushed by people who already have write access. Do not switch it to `pull_request_target` to get secrets for forks. The job trusts branches from people with write access; a branch can still change project `.codex/` config and `AGENTS.md`, so keep the agent step read-only and require review before merging workflow or `.codex/` changes. `--ephemeral` keeps the session off the runner's disk, and `cli_auth_credentials_store=ephemeral` keeps any credential in memory. The doctor step ignores the command's exit code because other rows, such as network reachability, can fail on a runner while auth is fine. Use `codex doctor`, not `codex login status`, to check a key supplied this way: `codex login status` reports `Not logged in` for keys that come from the environment, while the `doctor` auth row reports `auth is provided by environment`. The auth row confirms a key is present, not that OpenAI accepts it; the exec step is what fails on a revoked or mistyped key. The agent step selects the built-in `:read-only` permission profile (beta), which OpenAI prefers over the legacy `--sandbox` flag; do not add `--sandbox` next to it, because the two systems do not compose. See [Codex configuration](/en/codex/quick-start/configuration/).

**Copy-paste prompt: add a safe Codex CI job to this repository**

```text
Add a GitHub Actions workflow at .github/workflows/codex-check.yml that
runs Codex in read-only mode on pull_request events. Requirements:
- top-level permissions: contents: read, and nothing else
- actions/checkout with persist-credentials: false
- the API key comes only from secrets.CODEX_API_KEY via the
  CODEX_API_KEY environment variable; never echo it
- a step that saves `codex doctor --summary` output (ignore its exit
  code) and fails unless the auth row shows a check mark
- `codex exec --ephemeral` with `-c default_permissions=":read-only"`
  (a permission profile, not --sandbox) for the agent step
- skip pull requests from forks with an if: condition on the job
- do not use pull_request_target
Then show me the diff and list each requirement with the line that
satisfies it.
```

The requirement list at the end is your acceptance check: each requirement maps to a line, so you check seven lines, not the whole file.

## Enforce one login method across a team

For a single machine or a shared base config, pin the method and the workspace in `config.toml`:

```toml
# ~/.codex/config.toml, or a config your team distributes
forced_login_method = "chatgpt"          # or "api"
forced_chatgpt_workspace_id = "00000000-0000-0000-0000-000000000000"
```

If the stored credential does not match, Codex logs the user out and says why, for example "ChatGPT login is required, but an API key is currently being used. Logging out." A user can edit their own `config.toml`, so for organization-wide rules, administrators set `allowed_login_methods` and `allowed_chatgpt_workspaces` in the managed `requirements.toml`. The [enterprise governance guide](/en/codex/team/enterprise-governance/) covers managed configuration.

**Copy-paste prompt: write the team's Codex sign-in onboarding check**

```text
Create scripts/check-codex-auth.sh for our onboarding checklist.
It must: print `codex --version`; run `codex login status 2>&1` and fail
with a clear message unless it exits 0 and the output starts with
"Logged in using ChatGPT"; read ~/.codex/config.toml without printing
secrets and warn if forced_login_method is not "chatgpt"; warn if
~/.codex/auth.json exists and is readable by group or others; and
finish with `codex doctor --summary`. Use POSIX sh, no dependencies.
Do not read or print the contents of auth.json. Run the script once
and show me its output.
```

## When Codex sign-in breaks

**The browser never returns to the terminal (WSL, SSH, containers).** The browser cannot reach `localhost:1455` on the machine running Codex. Cancel, then use `codex login --device-auth` or the SSH forwarding above.

**"Port … is already in use".** Both callback ports, 1455 and the fallback 1457, are taken, usually by stale `codex login` processes. Codex has already tried to cancel the previous sign-in. Close terminals still running a sign-in (or kill the process holding the port) and run `codex login` again.

**"device code login is not enabled for this Codex server".** Device-code sign-in is off for this account or server. Use SSH port forwarding, or ask your workspace admin whether the setting is available to you.

**"device auth timed out after 15 minutes".** Run `codex login --device-auth` again and enter the new code promptly.

**"Your access token could not be refreshed".** The session expired, or you signed in to another account elsewhere. Run `codex logout`, then `codex login`.

**Codex logs you out on start with "ChatGPT login is required, but an API key is currently being used".** A `forced_login_method` rule does not match your credential. Sign in with the method the rule asks for. Change the rule only if you own it.

**"API key login is disabled by this workspace".** Your admin allows only ChatGPT sign-in. Run `codex login` and choose ChatGPT.

**`keyring` storage fails on a headless Linux box.** No Secret Service is running, and `keyring` fails rather than falling back. Set `cli_auth_credentials_store = "auto"`, or `"ephemeral"` on shared machines.

**Signed in in one terminal, "Not logged in" in another.** The two shells use different `CODEX_HOME` values. Run `echo "$CODEX_HOME"` in both and make them match, or unset it to use `~/.codex`.

**`codex login status` fails in CI although the job works.** The key comes from `CODEX_API_KEY`, which `login status` does not report. Check with `codex doctor --summary` as in the CI example.

**`codex cloud` says "Not signed in" while local work runs.** You are on an API key. Cloud tasks need ChatGPT sign-in: run `codex login` and choose ChatGPT.

For errors after sign-in, such as rate limits or sandbox denials, see [Codex error recovery](/en/codex/quick-start/error-recovery/).

## Where to go next after Codex sign-in

- [Configure Codex](/en/codex/quick-start/configuration/) — Set the model, approval policy, permission profiles, and profiles in config.toml.
- [Run your first Codex task](/en/codex/quick-start/first-task/) — Give Codex a scoped task and check the result against tests, not by reading every line.
- [Run Codex non-interactively](/en/codex/automate/non-interactive/) — Script codex exec for batch jobs and pipelines.
- [Authenticate Claude Code](/en/claude-code/quick-start/authentication/) — Sign in to Claude Code with a subscription or an API key, and set up its CI credentials.
- [Codex cloud environments](/en/codex/automate/cloud-environments/) — Set up the environments that cloud tasks run in once ChatGPT sign-in works.
