# OAuth Fix for OpenClaw + Claude Max Automatic Anthropic OAuth token refresh for OpenClaw. Keeps your Claude Max tokens alive indefinitely. ## The Problem OpenClaw uses Anthropic Claude models (e.g., `claude-opus-4-6`) via OAuth tokens from Claude Max subscriptions. These tokens **expire every ~8 hours**. Without automated refresh, all agents stop working with: ``` HTTP 401 authentication_error: OAuth token has expired ``` ## The Solution Two services working together: 1. **Auto-refresh trigger** — A timer that runs Claude CLI every 30 minutes to check and refresh the token before it expires. Supports Claude CLI in a Docker container or installed on the host. 2. **Token sync watcher** — An `inotifywait` service that detects when Claude CLI writes new credentials and instantly syncs them to OpenClaw. ``` Timer (every 30min) Claude Code CLI sync-oauth-token.sh OpenClaw Gateway (triggers CLI when ---> (refreshes token, ---> (watches for changes, ---> (gets fresh token) token near expiry) writes creds file) syncs via inotifywait) ``` ## Quick Start ```bash git clone https://github.com/YOUR_USER/oauth-fix-openclaw-final.git cd oauth-fix-openclaw-final sudo ./setup.sh ``` The interactive wizard will: 1. Detect your OpenClaw installation paths 2. Find Claude CLI credentials 3. Configure the Anthropic model (if not already set up) 4. Install the token sync watcher (inotifywait) 5. Detect Claude CLI (container or host) and install the auto-refresh trigger 6. Test the CLI invocation to confirm it works 7. Verify everything works ## Prerequisites - Linux server with **systemd** - **Docker** + **Docker Compose v2** - **OpenClaw** installed and running - **Claude Max subscription** with OAuth credentials - **Claude Code CLI** — either in a Docker container or installed on the host - **python3** - **inotify-tools** (optional, installed by wizard if missing) ## How It Works ### Architecture ``` +---------------------------+ | trigger-claude-refresh.sh | (systemd timer, every 30 min) | checks token expiry | | if < 1.5h remaining: | | triggers Claude CLI | +-------------+-------------+ | v +--------------------+ auto-refresh +-------------------+ | Claude Code CLI | =================> | .credentials.json | | (container or host)| (token near expiry) +--------+----------+ +--------------------+ | inotifywait detects change | +----------v----------+ | sync-oauth-token.sh | +--+------+------+----+ | | | oauth.json .env gateway (mapped (env restart fields) var) (down/up) ``` ### Token Flow 1. `trigger-claude-refresh.sh` runs every 30 minutes, checks token expiry 2. If token has < 1.5 hours remaining, triggers Claude CLI 3. Claude CLI detects its token is near expiry 4. CLI calls Anthropic's refresh endpoint, gets new access token 5. CLI writes updated `.credentials.json` 6. `inotifywait` detects the file change (< 1 second) 7. `sync-oauth-token.sh` reads the new token 8. Maps fields: `accessToken` -> `access`, `refreshToken` -> `refresh`, `expiresAt` -> `expires` 9. Writes to `oauth.json` (OpenClaw's format) 10. Updates `ANTHROPIC_OAUTH_TOKEN` in `.env` 11. Recreates gateway container (`docker compose down/up` — NOT restart!) 12. Gateway starts with the fresh token ### ANTHROPIC_BASE_URL Override If Claude CLI runs in a container with `ANTHROPIC_BASE_URL` set to a proxy (e.g., LiteLLM), the trigger script uses a **temporary per-invocation override**: ```bash docker exec -e ANTHROPIC_BASE_URL=https://api.anthropic.com container claude -p "say ok" ``` The `-e` flag overrides the env var only for that single command. The container's running processes are unaffected. This is detected and configured automatically by the wizard. ### Re-authentication Notification If the refresh token itself expires (e.g., subscription lapsed), the trigger script: - Creates a flag file at `REAUTH_NEEDED` in the OpenClaw directory - Logs an error to journalctl - Future: Mattermost webhook notification ### Why down/up and NOT restart? `docker compose restart` does **NOT** reload `.env` variables. It restarts the same container with the same environment. Only `docker compose down` + `docker compose up -d` creates a new container that reads `.env` fresh. ## Anthropic Model Configuration OpenClaw has a **built-in** Anthropic provider. **Do NOT** add `anthropic` to `models.providers` in `openclaw.json` — it causes double `/v1` in URLs resulting in 404 errors. The correct configuration (set by the wizard): ```json { "agents": { "defaults": { "model": { "primary": "anthropic/claude-opus-4-6" }, "models": { "anthropic/claude-opus-4-6": { "alias": "Claude Opus 4.6 (Max)" }, "anthropic/claude-sonnet-4-6": { "alias": "Claude Sonnet 4.6 (Max)" } } } } } ``` See [docs/OPENCLAW-MODEL-CONFIG.md](docs/OPENCLAW-MODEL-CONFIG.md) for full details. ## Credential Field Mapping Claude CLI and OpenClaw use different field names: | Claude CLI (`.credentials.json`) | OpenClaw (`oauth.json`) | |----------------------------------|------------------------| | `claudeAiOauth.accessToken` | `anthropic.access` | | `claudeAiOauth.refreshToken` | `anthropic.refresh` | | `claudeAiOauth.expiresAt` | `anthropic.expires` | See [docs/FIELD-MAPPING.md](docs/FIELD-MAPPING.md) for all formats. ## Manual Installation If you prefer not to use the wizard: ### 1. Install inotify-tools ```bash apt install inotify-tools ``` ### 2. Edit and install the sync script ```bash # Edit scripts/sync-oauth-token.sh — replace @@PLACEHOLDER@@ values: # @@CLAUDE_CREDS_FILE@@ = path to Claude CLI .credentials.json # @@OPENCLAW_OAUTH_FILE@@ = path to OpenClaw oauth.json # @@OPENCLAW_ENV_FILE@@ = path to OpenClaw .env # @@COMPOSE_DIR@@ = path to OpenClaw docker-compose directory cp scripts/sync-oauth-token.sh /usr/local/bin/ chmod +x /usr/local/bin/sync-oauth-token.sh ``` ### 3. Install systemd service ```bash # Edit templates/sync-oauth-token.service — replace @@SYNC_SCRIPT_PATH@@ cp templates/sync-oauth-token.service /etc/systemd/system/ systemctl daemon-reload systemctl enable --now sync-oauth-token.service ``` ### 4. Install the auto-refresh trigger ```bash # Edit scripts/trigger-claude-refresh.sh — replace @@PLACEHOLDER@@ values: # @@CREDS_FILE@@ = path to Claude CLI .credentials.json # @@REAUTH_FLAG@@ = path to REAUTH_NEEDED flag file # @@CLI_MODE@@ = "container" or "host" # @@CLI_CONTAINER@@ = container name (if container mode) # @@CLI_BASE_URL_OVERRIDE@@ = "true" or "false" cp scripts/trigger-claude-refresh.sh /usr/local/bin/ chmod +x /usr/local/bin/trigger-claude-refresh.sh # Edit templates/trigger-claude-refresh.service — replace @@TRIGGER_SCRIPT_PATH@@ cp templates/trigger-claude-refresh.service /etc/systemd/system/ cp templates/trigger-claude-refresh.timer /etc/systemd/system/ systemctl daemon-reload systemctl enable --now trigger-claude-refresh.timer ``` ### 5. Configure OpenClaw See [docs/OPENCLAW-MODEL-CONFIG.md](docs/OPENCLAW-MODEL-CONFIG.md). ## Verification ```bash # Run the health check ./scripts/verify.sh # Watch sync logs in real-time journalctl -u sync-oauth-token.service -f # Check trigger logs journalctl -u trigger-claude-refresh -n 20 # Check all timers systemctl list-timers sync-oauth-token* trigger-claude-refresh* # Check service status systemctl status sync-oauth-token.service systemctl status trigger-claude-refresh.timer ``` ## Uninstall ```bash ./setup.sh --uninstall # or ./scripts/uninstall.sh ``` ## Fallback Method (Timer) If `inotifywait` is unavailable, the wizard installs a systemd timer that refreshes the token directly via Anthropic's API every 6 hours. This is less responsive but doesn't require inotify. ## Troubleshooting See [docs/TROUBLESHOOTING.md](docs/TROUBLESHOOTING.md) for common issues: - Token expired errors - `docker compose restart` not reloading env - Auth profile `key` vs `access` field - 404 from custom anthropic provider - Cooldown errors - Claude CLI not responding (ANTHROPIC_BASE_URL override) - REAUTH_NEEDED flag (refresh token expired) ## Documentation - [Architecture](docs/ARCHITECTURE.md) — Token flow, volume mounts, auth resolution - [Troubleshooting](docs/TROUBLESHOOTING.md) — Common issues and fixes - [Model Config](docs/OPENCLAW-MODEL-CONFIG.md) — Anthropic model setup in OpenClaw - [Token Refresh](docs/HOW-TOKEN-REFRESH-WORKS.md) — How Claude CLI refreshes tokens - [Field Mapping](docs/FIELD-MAPPING.md) — Credential format reference ## License MIT