diff --git a/docs/cli/index.md b/docs/cli/index.md index 8aa28d29f..0c9c52108 100644 --- a/docs/cli/index.md +++ b/docs/cli/index.md @@ -166,7 +166,7 @@ Options: - `--workspace ` - `--non-interactive` - `--mode ` -- `--auth-choice ` +- `--auth-choice ` - `--anthropic-api-key ` - `--gateway-port ` - `--gateway-bind ` @@ -202,7 +202,7 @@ Options: Manage chat provider accounts (WhatsApp/Telegram/Discord/Slack/Signal/iMessage). Subcommands: -- `providers list`: show configured chat providers and auth profiles (Claude CLI + Codex CLI sync included). +- `providers list`: show configured chat providers and auth profiles (Claude Code + Codex CLI OAuth sync included). - `providers status`: check gateway reachability and provider health (`--probe` to verify credentials; use `status --deep` for local-only probes). - `providers add`: wizard-style setup when no flags are passed; flags switch to non-interactive mode. - `providers remove`: disable by default; pass `--delete` to remove config entries without prompts. @@ -227,6 +227,12 @@ Common options: - `--no-usage`: skip provider usage/quota snapshots (OAuth/API-backed only). - `--json`: output JSON (includes usage unless `--no-usage` is set). +OAuth sync sources: +- `~/.claude/.credentials.json` → `anthropic:claude-cli` +- `~/.codex/auth.json` → `openai-codex:codex-cli` + +More detail: [/concepts/oauth](/concepts/oauth) + Examples: ```bash clawdbot providers add --provider telegram --account alerts --name "Alerts Bot" --token $TELEGRAM_BOT_TOKEN diff --git a/docs/concepts/model-failover.md b/docs/concepts/model-failover.md index cbf5aaf44..b6e660d01 100644 --- a/docs/concepts/model-failover.md +++ b/docs/concepts/model-failover.md @@ -21,6 +21,8 @@ Clawdbot uses **auth profiles** for both API keys and OAuth tokens. - Config `auth.profiles` / `auth.order` are **metadata + routing only** (no secrets). - Legacy import-only OAuth file: `~/.clawdbot/credentials/oauth.json` (imported into `auth-profiles.json` on first use). +More detail: [/concepts/oauth](/concepts/oauth) + Credential types: - `type: "api_key"` → `{ provider, key }` - `type: "oauth"` → `{ provider, access, refresh, expires, email? }` (+ `projectId`/`enterpriseUrl` for some providers) diff --git a/docs/concepts/oauth.md b/docs/concepts/oauth.md new file mode 100644 index 000000000..633e0d61d --- /dev/null +++ b/docs/concepts/oauth.md @@ -0,0 +1,133 @@ +--- +summary: "OAuth in Clawdbot: token exchange, storage, CLI sync, and multi-account patterns" +read_when: + - You want to understand Clawdbot OAuth end-to-end + - You hit token invalidation / logout issues + - You want to reuse Claude Code / Codex CLI OAuth tokens + - You want multiple accounts or profile routing +--- +# OAuth + +Clawdbot supports “subscription auth” via OAuth for providers that offer it (notably **Anthropic (Claude Pro/Max)** and **OpenAI Codex (ChatGPT OAuth)**). This page explains: + +- how the OAuth **token exchange** works (PKCE) +- where tokens are **stored** (and why) +- how we **reuse external CLI tokens** (Claude Code / Codex CLI) +- how to handle **multiple accounts** (profiles + per-session overrides) + +## The token sink (why it exists) + +OAuth 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. + +Practical symptom: +- you log in via Clawdbot *and* via Claude Code / Codex CLI → one of them randomly gets “logged out” later + +To reduce that, Clawdbot treats `auth-profiles.json` as a **token sink**: +- the runtime reads credentials from **one place** +- we can **sync in** credentials from external CLIs instead of doing a second login +- we can keep multiple profiles and route them deterministically + +## Storage (where tokens live) + +Secrets are stored **per-agent**: + +- Auth profiles (OAuth + API keys): `~/.clawdbot/agents//agent/auth-profiles.json` +- Runtime cache (managed automatically; don’t edit): `~/.clawdbot/agents//agent/auth.json` + +Legacy import-only file (still supported, but not the main store): +- `~/.clawdbot/credentials/oauth.json` (imported into `auth-profiles.json` on first use) + +All of the above also respect `$CLAWDBOT_STATE_DIR` (state dir override). Full reference: [/gateway/configuration](/gateway/configuration#auth-storage-oauth--api-keys) + +## Reusing Claude Code / Codex CLI OAuth tokens (recommended) + +If you already signed in with the external CLIs *on the gateway host*, Clawdbot can reuse those tokens without starting a separate OAuth flow: + +- Claude Code: reads `~/.claude/.credentials.json` → profile `anthropic:claude-cli` +- Codex CLI: reads `~/.codex/auth.json` → profile `openai-codex:codex-cli` + +Sync happens when Clawdbot loads the auth store (so it stays up-to-date when the CLIs refresh tokens). + +How to verify: + +```bash +clawdbot providers list +``` + +Or JSON: + +```bash +clawdbot providers list --json +``` + +## OAuth exchange (how login works) + +Clawdbot’s interactive login flows are implemented in `@mariozechner/pi-ai` and wired into the wizards/commands. + +### Anthropic (Claude Pro/Max) + +Flow shape (PKCE): + +1) generate PKCE verifier/challenge +2) open `https://claude.ai/oauth/authorize?...` +3) user pastes `code#state` +4) exchange at `https://console.anthropic.com/v1/oauth/token` +5) store `{ access, refresh, expires }` under an auth profile + +The wizard path is `clawdbot onboard` → auth choice `oauth` (Anthropic). + +### OpenAI Codex (ChatGPT OAuth) + +Flow shape (PKCE): + +1) generate PKCE verifier/challenge + random `state` +2) open `https://auth.openai.com/oauth/authorize?...` +3) try to capture callback on `http://127.0.0.1:1455/auth/callback` +4) if callback can’t bind (or you’re remote/headless), paste the redirect URL/code +5) exchange at `https://auth.openai.com/oauth/token` +6) extract `accountId` from the access token and store `{ access, refresh, expires, accountId }` + +Wizard path is `clawdbot onboard` → auth choice `openai-codex` (or `codex-cli` to reuse an existing Codex CLI login). + +## Refresh + expiry + +Profiles store an `expires` timestamp. + +At runtime: +- if `expires` is in the future → use the stored access token +- if expired → refresh (under a file lock) and overwrite the stored credentials + +See implementation: `src/agents/auth-profiles.ts`. + +## Multiple accounts (profiles) + routing + +Two patterns: + +### 1) Preferred: separate agents + +If you want “personal” and “work” to never interact, use isolated agents (separate sessions + credentials + workspace): + +```bash +clawdbot agents add work +clawdbot agents add personal +``` + +Then configure auth per-agent (wizard) and route chats to the right agent. + +### 2) Advanced: multiple profiles in one agent + +`auth-profiles.json` supports multiple profile IDs for the same provider. + +Pick which profile is used: +- globally via config ordering (`auth.order`) +- per-session via `/model ...@` + +Example (session override): +- `/model Opus@anthropic:work` + +How to see what profile IDs exist: +- `clawdbot providers list --json` (shows `auth[]`) + +Related docs: +- [/concepts/model-failover](/concepts/model-failover) (rotation + cooldown rules) +- [/tools/slash-commands](/tools/slash-commands) (command surface) diff --git a/docs/docs.json b/docs/docs.json index 8728ef949..c9068b8b4 100644 --- a/docs/docs.json +++ b/docs/docs.json @@ -508,6 +508,10 @@ { "source": "/wizard", "destination": "/start/wizard" + }, + { + "source": "/oauth", + "destination": "/concepts/oauth" } ], "navigation": { @@ -544,6 +548,7 @@ "concepts/agent", "concepts/agent-loop", "concepts/system-prompt", + "concepts/oauth", "concepts/agent-workspace", "concepts/multi-agent", "concepts/compaction", diff --git a/docs/gateway/configuration.md b/docs/gateway/configuration.md index 334bd4de3..cc0ebc641 100644 --- a/docs/gateway/configuration.md +++ b/docs/gateway/configuration.md @@ -116,6 +116,8 @@ Env var equivalent: Clawdbot stores **per-agent** auth profiles (OAuth + API keys) in: - `/auth-profiles.json` (default: `~/.clawdbot/agents//agent/auth-profiles.json`) +See also: [/concepts/oauth](/concepts/oauth) + Legacy OAuth imports: - `~/.clawdbot/credentials/oauth.json` (or `$CLAWDBOT_STATE_DIR/credentials/oauth.json`) @@ -131,6 +133,10 @@ Overrides: On first use, Clawdbot imports `oauth.json` entries into `auth-profiles.json`. +Clawdbot also auto-syncs OAuth tokens from external CLIs into `auth-profiles.json` (when present on the gateway host): +- `~/.claude/.credentials.json` (Claude Code) → `anthropic:claude-cli` +- `~/.codex/auth.json` (Codex CLI) → `openai-codex:codex-cli` + ### `auth` Optional metadata for auth profiles. This does **not** store secrets; it maps diff --git a/docs/start/faq.md b/docs/start/faq.md index 8088230f3..67540700f 100644 --- a/docs/start/faq.md +++ b/docs/start/faq.md @@ -339,6 +339,8 @@ Fix: either provide Google auth, or remove/avoid Google models in `agent.model.f ## Auth profiles: what they are and how to manage them +Related: [/concepts/oauth](/concepts/oauth) (OAuth flows, token storage, multi-account patterns, CLI sync) + ### What is an auth profile? An auth profile is a named credential record (OAuth or API key) tied to a provider. Profiles live in: diff --git a/docs/start/hubs.md b/docs/start/hubs.md index bd0af2226..dc6d27644 100644 --- a/docs/start/hubs.md +++ b/docs/start/hubs.md @@ -54,6 +54,7 @@ Use these hubs to discover every page, including deep dives and reference docs t - [Groups](https://docs.clawd.bot/concepts/groups) - [Group messages](https://docs.clawd.bot/concepts/group-messages) - [Model failover](https://docs.clawd.bot/concepts/model-failover) +- [OAuth](https://docs.clawd.bot/concepts/oauth) ## Providers + ingress diff --git a/docs/start/onboarding.md b/docs/start/onboarding.md index e724ff08e..4ed664933 100644 --- a/docs/start/onboarding.md +++ b/docs/start/onboarding.md @@ -32,6 +32,8 @@ Implementation note (2025-12-19): in local mode, the macOS app bundles the Gatew This is the “bind Clawdbot to subscription auth” step. It is explicitly the **Anthropic (Claude Pro/Max)** or **OpenAI (ChatGPT/Codex)** OAuth flow, not a generic “login”. +More detail: [/concepts/oauth](/concepts/oauth) + ### Recommended: OAuth (Anthropic) The macOS app should: @@ -165,15 +167,29 @@ On the gateway host, create `~/.clawdbot/credentials/oauth.json` with this exact Set permissions: `chmod 600 ~/.clawdbot/credentials/oauth.json` -**Note:** Clawdbot auto-imports from legacy pi-coding-agent paths (`~/.pi/agent/oauth.json`, etc.) but this does NOT work with Claude Code credentials — different file and format. +**Note:** Clawdbot can import from legacy pi-coding-agent paths (`~/.pi/agent/oauth.json`, etc.), but Claude Code/Codex CLI credentials live in different files. -### Using Claude Code credentials +### Using Claude Code + Codex CLI credentials (direct) -If Claude Code is installed on the gateway host, convert its credentials: +If these CLIs are installed on the **gateway host** and you’ve already signed in, Clawdbot auto-syncs their OAuth tokens into the per-agent auth profile store (`~/.clawdbot/agents//agent/auth-profiles.json`) on load: + +- **Claude Code**: reads `~/.claude/.credentials.json` → profile `anthropic:claude-cli` +- **Codex CLI**: reads `~/.codex/auth.json` → profile `openai-codex:codex-cli` + +Verification: + +```bash +clawdbot providers list +``` + +### Fallback: convert Claude Code credentials into `oauth.json` + +If you don’t want to install Claude Code on the gateway host, you can still seed the legacy import file: ```bash cat ~/.claude/.credentials.json | jq '{ anthropic: { + type: "oauth", access: .claudeAiOauth.accessToken, refresh: .claudeAiOauth.refreshToken, expires: .claudeAiOauth.expiresAt @@ -182,12 +198,6 @@ cat ~/.claude/.credentials.json | jq '{ chmod 600 ~/.clawdbot/credentials/oauth.json ``` -| Claude Code field | Clawdbot field | -|-------------------|---------------| -| `accessToken` | `access` | -| `refreshToken` | `refresh` | -| `expiresAt` | `expires` | - ## Workspace backup (recommended) We suggest creating a **private GitHub repository** to back up the agent diff --git a/docs/start/wizard.md b/docs/start/wizard.md index b20d01a13..ee12100a0 100644 --- a/docs/start/wizard.md +++ b/docs/start/wizard.md @@ -56,7 +56,9 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` ( - Full reset (also removes workspace) 2) **Model/Auth** + - **Anthropic OAuth (Claude CLI)**: if `~/.claude/.credentials.json` exists, the wizard can reuse it. - **Anthropic OAuth (recommended)**: browser flow; paste the `code#state`. + - **OpenAI Codex OAuth (Codex CLI)**: if `~/.codex/auth.json` exists, the wizard can reuse it. - **OpenAI Codex OAuth**: browser flow; paste the `code#state`. - Sets `agent.model` to `openai-codex/gpt-5.2` when model is unset or `openai/*`. - **API key**: stores the key for you. @@ -64,6 +66,7 @@ Tip: `--json` does **not** imply non-interactive mode. Use `--non-interactive` ( - **Skip**: no auth configured yet. - Wizard runs a model check and warns if the configured model is unknown or missing auth. - OAuth credentials live in `~/.clawdbot/credentials/oauth.json`; auth profiles live in `~/.clawdbot/agents//agent/auth-profiles.json` (API keys + OAuth). + - More detail: [/concepts/oauth](/concepts/oauth) 3) **Workspace** - Default `~/clawd` (configurable). diff --git a/src/cli/program.ts b/src/cli/program.ts index 218646766..8c09a5758 100644 --- a/src/cli/program.ts +++ b/src/cli/program.ts @@ -232,7 +232,7 @@ export function buildProgram() { .option("--mode ", "Wizard mode: local|remote") .option( "--auth-choice ", - "Auth: oauth|openai-codex|antigravity|apiKey|minimax|skip", + "Auth: oauth|claude-cli|openai-codex|codex-cli|antigravity|apiKey|minimax|skip", ) .option("--anthropic-api-key ", "Anthropic API key") .option("--gateway-port ", "Gateway port") @@ -259,7 +259,9 @@ export function buildProgram() { mode: opts.mode as "local" | "remote" | undefined, authChoice: opts.authChoice as | "oauth" + | "claude-cli" | "openai-codex" + | "codex-cli" | "antigravity" | "apiKey" | "minimax"