Blog

Rotating an Agent's Credentials Without Breaking It: Claude Code and Codex

Rotating an agent's key is a sequence, not an edit: create the new credential, switch one path, prove which credential is live, then revoke the old one.

Rotating an Agent's Credentials Without Breaking It: Claude Code and Codex

Rotating an agent's credentials is not a matter of changing one variable. Both Claude Code and Codex remember credentials across sessions, and the failure mode is rarely "the new key is wrong". It is that the old credential is still present and still winning, so the new one never gets used. Logging out has its own side effect: it resets first-launch setup, which makes a successful rotation look like a broken installation. The reliable order is therefore create, switch, confirm, then revoke, with a check at each step.

This article covers the operational layer that the earlier explainers skipped. The credential stack itself is documented in the Claude Code precedence guide, the Codex side in the Codex credentials and storage guide, and the policy in the key hygiene guide. All specifics below follow the current Claude Code settings and IAM pages and the Codex manual; verify against those pages before you change anything.

Config is not credentials

The first distinction that prevents most mistakes: a configuration file and a credential store are different objects, and editing one does not touch the other.

In Claude Code, settings files hold behavior: permissions, hooks, model choices, and the apiKeyHelper definition. The credential itself arrives from an environment variable, from what that helper prints, or from a login. Changing a setting changes how a credential is obtained; it does not rotate the credential.

In Codex, config.toml holds configuration, while authentication lives separately. The manual describes local state under CODEX_HOME (defaulting to ~/.codex), where you may find config.toml, auth.json when file-based credential storage is used, history, and other per-user state. That split is the reason "I edited config.toml and it still uses the old account" is a coherent complaint rather than a bug.

The Claude Code credential stack

Claude Code can obtain a credential from several places, and the official precedence list decides which one wins when more than one is present. The order, with the practical meaning of each entry:

Cloud provider credentials come first when the session is configured for one, such as Amazon Bedrock. These authenticate against the provider, not against an Anthropic key.

ANTHROPIC_AUTH_TOKEN comes next, sent as a bearer-style authorization header, and is the intended route for a gateway or proxy that authenticates with tokens rather than Anthropic API keys.

ANTHROPIC_API_KEY follows, sent as an API key header, for direct access with a key from the Claude Console. In interactive mode Claude Code prompts once to approve or decline that key and remembers the choice; the setting can be revisited through the custom API key toggle in /config, which only appears while the variable is set. In non-interactive runs the key is used whenever it is present.

apiKeyHelper output comes after the environment variables. This is a shell script that returns a key, and the documentation recommends it for dynamic or rotating credentials, such as short-lived tokens fetched from a vault.

A detail worth correcting, because older write-ups get it backwards: the current settings documentation says Claude Code watches settings files and applies most edits to the running session without a restart, including edits to permissions, hooks, and credential helpers such as apiKeyHelper. Only a few keys are read once at session start, among them model, effortLevel, and modelSettings. So rotating a helper script can take effect in the live session, while a change to how the model is selected may not. /status shows which settings sources were loaded for the current session, and claude doctor lists entries that were rejected.

A long-lived OAuth token from the token setup command follows, intended for pipelines where a browser login is not available. Next come Anthropic profile and federation credentials, and last the subscription login created by /login, which is the default for individual plans.

Two consequences follow. First, setting a new key in a file while an older environment variable is still exported changes nothing, because the environment variable ranks above the helper and above the login. Second, "which credential is active" is answerable: /status shows the active credential context, including a Login row when a saved login is the active credential.

What logout actually does

/logout is not just "forget the token". The IAM page describes it as the way to log out and re-authenticate, and adds the detail that matters operationally: logging out also resets your first-launch setup state, so the next time you run claude it walks you through login and setup again. For a sign-in that stored a credential, the documentation also describes /logout as removing and revoking the credential that sign-in wrote.

So the "it looks like a fresh install" experience after logging out is expected behavior, not damage. Plan for it: if a rotation requires logging out, the next launch will ask for login and setup, and any unattended job that depends on the previous login will not run until that is done.

The trap that survives a new key

The most expensive rotation mistake is assuming that creating a new credential removes the old one. It does not. The IAM page documents the resulting conflict for cloud provider sessions such as Amazon Bedrock: those sessions are blocked only while an ANTHROPIC_API_KEY, ANTHROPIC_AUTH_TOKEN, or apiKeyHelper credential, or an API key saved by an earlier Claude Console login, is still present on the machine. The documentation's phrasing is blunt: remove it and the session starts.

That is the whole lesson in one sentence. The cloud path was never failing because of the cloud configuration; it was failing because a leftover environment credential was still on the machine. The same page notes that managed settings requiring a particular login method or organization, through forceLoginMethod and forceLoginOrgUUID, block environment credentials and helper output at startup, because organization membership cannot be verified for an environment credential. That is a policy boundary, not something to work around; the correct response is to comply with the required login method.

Practically, the audit question before any rotation is: which of these exist on this machine right now, and which of them will still exist after I "switch"? Environment variables in shell profiles, helper scripts referenced by settings, an earlier console login, and a login from a different surface can each outlive the change you just made.

Codex: two login kinds and one file

Codex draws a similar line, with its own names. The manual distinguishes ChatGPT sign-in from API key login, and notes that login details are cached and reused, shared between the CLI and the IDE extension. Logging out of either surface means signing in again the next time you start the CLI or the extension.

Where that cache lives is a configuration decision. cli_auth_credentials_store accepts file, keyring, auto, and ephemeral: file stores credentials in auth.json under CODEX_HOME, keyring uses the operating system credential store and fails if it is unavailable, auto prefers the OS store and falls back to auth.json, and ephemeral keeps credentials in memory for the current process only. The manual's warning about file-based storage is worth repeating: treat auth.json like a password, because it contains access tokens.

This is also the cleanest answer for CI. Point CODEX_HOME at an isolated directory so the pipeline's credentials and state do not mix with the developer's machine. And remember that config.toml is not the login: editing model or provider settings does not change which account is authenticated.

The rotation order

The order below is the operational core of this article. It assumes the new credential already exists and has the permissions you intend.

  1. Inventory the credential sources on the machine: environment variables, helper scripts, saved logins, and any cloud provider configuration.
  2. Create the new credential, and enable it in exactly one place. Multiple simultaneous sources are what make "which one is live" unanswerable.
  3. Restart or reopen the process so no stale in-memory credential survives the switch.
  4. Confirm which credential is actually in effect, using a minimal request and reading the error and response headers rather than assuming success. The failure triage map is the reference for reading those signals.
  5. Only after confirmation, revoke the old credential at its source.
  6. Re-test the path that was previously blocked, especially a cloud provider session, to confirm the leftover is really gone.
  7. Record what changed, where, and when, without recording the secret itself.
  8. Repeat for every surface that shares the credential: CLI, IDE extension, and automation.

The deletion step is not optional bookkeeping. A rotation that leaves the old key valid has rotated nothing; it has only added a second key to the blast radius. This is why the hygiene guide treats revocation as part of the change, not follow-up work.

Reading the signals

Three outcomes cover most of the confusion after a switch.

If requests still fail with an authentication error after you installed the new key, suspect the old credential is still active. Environment variables outrank helper output and saved logins, so a forgotten export in a shell profile will keep winning after every file edit. The fix is to remove the source, not to add another key.

If requests fail with a permission or region error instead, the credential is probably working and the problem is somewhere else: account permissions, project or workspace scope, or regional policy. That class of failure is a permissions question rather than a credential question, and rotating again will not resolve it.

If the next launch asks you to log in and go through setup again, that is the documented consequence of logging out, not evidence that the new credential broke something. Complete the login and setup, then verify with the minimal request.

Team discipline

Three habits keep a rotation from becoming an outage, whether you rotate an API key for a coding agent or a login for an interactive tool. Keep the overlap window between old and new credentials as short as the migration allows, because every hour of overlap is an hour in which either key works. Write revocation into the change checklist, since it is the step most often skipped once the new key appears to work. And keep credentials out of shell history, repositories, and logs: use the environment rather than inline commands, and follow the field discipline in the logging guide so that request metadata is captured without the secret.

One more habit for managed environments: when a corporate network inspects TLS, the agent may fail before authentication even begins. That case is covered in the certificate and credential store guide, and it is worth ruling out before you rotate a key that was never the problem.

Rotation is a sequence with a proof step in the middle. Create the new credential, make it the only path, prove it is the one in use, then remove the old one. Everything else is a variation on that order, and every step is checkable against the current official documentation.