{
  "title": "OAuth",
  "content": "OpenClaw supports “subscription auth” via OAuth for providers that offer it (notably **OpenAI Codex (ChatGPT OAuth)**). For Anthropic subscriptions, use the **setup-token** flow. This page explains:\n\n* how the OAuth **token exchange** works (PKCE)\n* where tokens are **stored** (and why)\n* how to handle **multiple accounts** (profiles + per-session overrides)\n\nOpenClaw also supports **provider plugins** that ship their own OAuth or API‑key\nflows. Run them via:\n\n## The token sink (why it exists)\n\nOAuth providers commonly mint a **new refresh token** during login/refresh flows. Some providers (or OAuth clients) can invalidate older refresh tokens when a new one is issued for the same user/app.\n\n* you log in via OpenClaw *and* via Claude Code / Codex CLI → one of them randomly gets “logged out” later\n\nTo reduce that, OpenClaw treats `auth-profiles.json` as a **token sink**:\n\n* the runtime reads credentials from **one place**\n* we can keep multiple profiles and route them deterministically\n\n## Storage (where tokens live)\n\nSecrets are stored **per-agent**:\n\n* Auth profiles (OAuth + API keys): `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`\n* Runtime cache (managed automatically; don’t edit): `~/.openclaw/agents/<agentId>/agent/auth.json`\n\nLegacy import-only file (still supported, but not the main store):\n\n* `~/.openclaw/credentials/oauth.json` (imported into `auth-profiles.json` on first use)\n\nAll of the above also respect `$OPENCLAW_STATE_DIR` (state dir override). Full reference: [/gateway/configuration](/gateway/configuration#auth-storage-oauth--api-keys)\n\n## Anthropic setup-token (subscription auth)\n\nRun `claude setup-token` on any machine, then paste it into OpenClaw:\n\nIf you generated the token elsewhere, paste it manually:\n\n## OAuth exchange (how login works)\n\nOpenClaw’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.\n\n### Anthropic (Claude Pro/Max) setup-token\n\n1. run `claude setup-token`\n2. paste the token into OpenClaw\n3. store as a token auth profile (no refresh)\n\nThe wizard path is `openclaw onboard` → auth choice `setup-token` (Anthropic).\n\n### OpenAI Codex (ChatGPT OAuth)\n\n1. generate PKCE verifier/challenge + random `state`\n2. open `https://auth.openai.com/oauth/authorize?...`\n3. try to capture callback on `http://127.0.0.1:1455/auth/callback`\n4. if callback can’t bind (or you’re remote/headless), paste the redirect URL/code\n5. exchange at `https://auth.openai.com/oauth/token`\n6. extract `accountId` from the access token and store `{ access, refresh, expires, accountId }`\n\nWizard path is `openclaw onboard` → auth choice `openai-codex`.\n\nProfiles store an `expires` timestamp.\n\n* if `expires` is in the future → use the stored access token\n* if expired → refresh (under a file lock) and overwrite the stored credentials\n\nThe refresh flow is automatic; you generally don't need to manage tokens manually.\n\n## Multiple accounts (profiles) + routing\n\n### 1) Preferred: separate agents\n\nIf you want “personal” and “work” to never interact, use isolated agents (separate sessions + credentials + workspace):\n\nThen configure auth per-agent (wizard) and route chats to the right agent.\n\n### 2) Advanced: multiple profiles in one agent\n\n`auth-profiles.json` supports multiple profile IDs for the same provider.\n\nPick which profile is used:\n\n* globally via config ordering (`auth.order`)\n* per-session via `/model ...@<profileId>`\n\nExample (session override):\n\n* `/model Opus@anthropic:work`\n\nHow to see what profile IDs exist:\n\n* `openclaw channels list --json` (shows `auth[]`)\n\n* [/concepts/model-failover](/concepts/model-failover) (rotation + cooldown rules)\n* [/tools/slash-commands](/tools/slash-commands) (command surface)",
  "code_samples": [
    {
      "code": "## The token sink (why it exists)\n\nOAuth providers commonly mint a **new refresh token** during login/refresh flows. Some providers (or OAuth clients) can invalidate older refresh tokens when a new one is issued for the same user/app.\n\nPractical symptom:\n\n* you log in via OpenClaw *and* via Claude Code / Codex CLI → one of them randomly gets “logged out” later\n\nTo reduce that, OpenClaw treats `auth-profiles.json` as a **token sink**:\n\n* the runtime reads credentials from **one place**\n* we can keep multiple profiles and route them deterministically\n\n## Storage (where tokens live)\n\nSecrets are stored **per-agent**:\n\n* Auth profiles (OAuth + API keys): `~/.openclaw/agents/<agentId>/agent/auth-profiles.json`\n* Runtime cache (managed automatically; don’t edit): `~/.openclaw/agents/<agentId>/agent/auth.json`\n\nLegacy import-only file (still supported, but not the main store):\n\n* `~/.openclaw/credentials/oauth.json` (imported into `auth-profiles.json` on first use)\n\nAll of the above also respect `$OPENCLAW_STATE_DIR` (state dir override). Full reference: [/gateway/configuration](/gateway/configuration#auth-storage-oauth--api-keys)\n\n## Anthropic setup-token (subscription auth)\n\nRun `claude setup-token` on any machine, then paste it into OpenClaw:",
      "language": "unknown"
    },
    {
      "code": "If you generated the token elsewhere, paste it manually:",
      "language": "unknown"
    },
    {
      "code": "Verify:",
      "language": "unknown"
    },
    {
      "code": "## OAuth exchange (how login works)\n\nOpenClaw’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands.\n\n### Anthropic (Claude Pro/Max) setup-token\n\nFlow shape:\n\n1. run `claude setup-token`\n2. paste the token into OpenClaw\n3. store as a token auth profile (no refresh)\n\nThe wizard path is `openclaw onboard` → auth choice `setup-token` (Anthropic).\n\n### OpenAI Codex (ChatGPT OAuth)\n\nFlow shape (PKCE):\n\n1. generate PKCE verifier/challenge + random `state`\n2. open `https://auth.openai.com/oauth/authorize?...`\n3. try to capture callback on `http://127.0.0.1:1455/auth/callback`\n4. if callback can’t bind (or you’re remote/headless), paste the redirect URL/code\n5. exchange at `https://auth.openai.com/oauth/token`\n6. extract `accountId` from the access token and store `{ access, refresh, expires, accountId }`\n\nWizard path is `openclaw onboard` → auth choice `openai-codex`.\n\n## Refresh + expiry\n\nProfiles store an `expires` timestamp.\n\nAt runtime:\n\n* if `expires` is in the future → use the stored access token\n* if expired → refresh (under a file lock) and overwrite the stored credentials\n\nThe refresh flow is automatic; you generally don't need to manage tokens manually.\n\n## Multiple accounts (profiles) + routing\n\nTwo patterns:\n\n### 1) Preferred: separate agents\n\nIf you want “personal” and “work” to never interact, use isolated agents (separate sessions + credentials + workspace):",
      "language": "unknown"
    }
  ],
  "headings": [
    {
      "level": "h2",
      "text": "The token sink (why it exists)",
      "id": "the-token-sink-(why-it-exists)"
    },
    {
      "level": "h2",
      "text": "Storage (where tokens live)",
      "id": "storage-(where-tokens-live)"
    },
    {
      "level": "h2",
      "text": "Anthropic setup-token (subscription auth)",
      "id": "anthropic-setup-token-(subscription-auth)"
    },
    {
      "level": "h2",
      "text": "OAuth exchange (how login works)",
      "id": "oauth-exchange-(how-login-works)"
    },
    {
      "level": "h3",
      "text": "Anthropic (Claude Pro/Max) setup-token",
      "id": "anthropic-(claude-pro/max)-setup-token"
    },
    {
      "level": "h3",
      "text": "OpenAI Codex (ChatGPT OAuth)",
      "id": "openai-codex-(chatgpt-oauth)"
    },
    {
      "level": "h2",
      "text": "Refresh + expiry",
      "id": "refresh-+-expiry"
    },
    {
      "level": "h2",
      "text": "Multiple accounts (profiles) + routing",
      "id": "multiple-accounts-(profiles)-+-routing"
    },
    {
      "level": "h3",
      "text": "1) Preferred: separate agents",
      "id": "1)-preferred:-separate-agents"
    },
    {
      "level": "h3",
      "text": "2) Advanced: multiple profiles in one agent",
      "id": "2)-advanced:-multiple-profiles-in-one-agent"
    }
  ],
  "url": "llms-txt#oauth",
  "links": []
}