openclaw_oauth_sync/docs/ARCHITECTURE.md
shamid202 22731fff60 Add complete OAuth token refresh and sync solution
- Setup wizard with auto-detection of OpenClaw paths and Claude CLI
- Token sync watcher (inotifywait) for real-time credential updates
- Auto-refresh trigger timer that runs Claude CLI every 30 min
- Supports Claude CLI in Docker container or on host
- Temporary ANTHROPIC_BASE_URL override for container environments
- Anthropic model configuration for OpenClaw
- Auth profile management (fixes key vs access field)
- Systemd services and timers for both sync and trigger
- Comprehensive documentation and troubleshooting guides
- Re-authentication notification system

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 01:51:18 +07:00

6.4 KiB

Architecture

Token Flow Diagram

+--------------------+     auto-refresh    +------------------------------------------+
| Claude Code CLI    |  ================>  | .credentials.json                        |
| (inside            |  (every ~8 hours,   | {                                        |
|  claude-proxy      |   built-in to CLI)  |   "claudeAiOauth": {                     |
|  container)        |                     |     "accessToken": "sk-ant-oat01-...",    |
+--------------------+                     |     "refreshToken": "sk-ant-ort01-...",   |
                                           |     "expiresAt": 1772120060006            |
                                           |   }                                       |
                                           | }                                         |
                                           +-------------------+-----------------------+
                                                               |
                                                    inotifywait detects
                                                    CLOSE_WRITE / MOVED_TO
                                                               |
                                           +-------------------v-----------------------+
                                           | sync-oauth-token.sh                       |
                                           | (systemd service, runs continuously)       |
                                           +---+----------------+----------------+-----+
                                               |                |                |
                              +----------------+    +-----------+---------+      |
                              |                     |                     |      |
                    +---------v---------+  +--------v--------+  +--------v--------+
                    | oauth.json        |  | .env            |  | docker compose   |
                    | {                 |  | ANTHROPIC_      |  | down/up gateway  |
                    |   "anthropic": {  |  | OAUTH_TOKEN=    |  | (reloads env)    |
                    |     "access":..., |  | "sk-ant-oat01-" |  +---------+--------+
                    |     "refresh":...,|  +-----------------+            |
                    |     "expires":... |                       +----------v----------+
                    |   }               |                       | OpenClaw Gateway    |
                    | }                 |                       | (fresh token loaded  |
                    +--------+----------+                       |  from container env) |
                             |                                  +----------+----------+
                             |  mergeOAuthFileIntoStore()                   |
                             |  (reads on startup)                         |
                             +-------------------->+                       |
                                                   |              +--------v---------+
                                                   +------------->| api.anthropic.com|
                                                                  | Claude Opus 4.6  |
                                                                  +------------------+

Volume Mounts (Docker)

HOST PATH                                                  CONTAINER PATH
=========                                                  ==============

Gateway container (openclaw-openclaw-gateway-1):
  /root/.openclaw/                                      -> /home/node/.openclaw/
  /root/.openclaw/credentials/oauth.json                -> /home/node/.openclaw/credentials/oauth.json
  /root/.openclaw/agents/*/agent/auth-profiles.json     -> /home/node/.openclaw/agents/*/agent/auth-profiles.json
  /home/node/.claude/                                   -> /home/node/.claude/
  /root/openclaw/.env                                   -> loaded as container env vars (at creation time only)

Claude CLI container (claude-proxy):
  /root/.openclaw/workspaces/workspace-claude-proxy/
    config/                                             -> /root/
    config/.claude/.credentials.json                    -> /root/.claude/.credentials.json

Auth Resolution Order (inside gateway)

When the gateway needs to authenticate with Anthropic:

1. resolveApiKeyForProvider("anthropic")
2.   -> resolveAuthProfileOrder()
3.     -> reads agents/<agent>/agent/auth-profiles.json
4.     -> isValidProfile() checks each profile:
5.       - type:"api_key" -> requires cred.key
6.       - type:"oauth"   -> requires cred.access (NOT cred.key!)
7.       - type:"token"   -> requires cred.token
8.   -> If valid profile found: use it
9.   -> If no valid profile: resolveEnvApiKey("anthropic")
10.    -> Reads ANTHROPIC_OAUTH_TOKEN from container env
11.  -> isOAuthToken(key) detects "sk-ant-oat" prefix
12.  -> Uses Bearer auth + Claude Code identity headers
13.  -> Sends request to api.anthropic.com

On gateway startup:
  mergeOAuthFileIntoStore()
    -> Reads /home/node/.openclaw/credentials/oauth.json
    -> Merges into auth profile store (if profile doesn't exist)

Why down/up and NOT restart

docker compose restart openclaw-gateway
  -> Sends SIGTERM to container process
  -> Restarts the SAME container (same env vars from creation time)
  -> .env changes are NOT reloaded
  -> Result: gateway still has OLD token

docker compose down openclaw-gateway && docker compose up -d openclaw-gateway
  -> Stops and REMOVES the container
  -> Creates a NEW container (reads .env fresh)
  -> New env vars are loaded
  -> Result: gateway has NEW token

Source Code References (inside gateway container)

File Line Function
/app/dist/paths-CyR9Pa1R.js 190 OAUTH_FILENAME = "oauth.json"
/app/dist/paths-CyR9Pa1R.js 198-204 resolveOAuthDir() -> $STATE_DIR/credentials/
/app/dist/paths-CyR9Pa1R.js 203 resolveOAuthPath() -> joins dir + filename
/app/dist/model-auth-CmUeBbp-.js 3048 mergeOAuthFileIntoStore() -- reads oauth.json
/app/dist/model-auth-CmUeBbp-.js 3358 buildOAuthApiKey() -- returns credentials.access
/app/dist/model-auth-CmUeBbp-.js 3832 isValidProfile() -- for oauth, checks cred.access
/app/dist/model-auth-CmUeBbp-.js 3942 resolveApiKeyForProvider() -- profiles then env fallback
/app/dist/model-auth-CmUeBbp-.js 4023 resolveEnvApiKey("anthropic") -> reads env var