# OAuth Fix for OpenClaw + Claude Max > Created by **ROOH** — [www.rooh.red](https://www.rooh.red) 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://git.eeqj.de/ROOH/openclaw_oauth_sync.git cd openclaw_oauth_sync sudo ./setup.sh ``` The interactive wizard will: 1. Check prerequisites (offers to install python3, curl, inotify-tools if missing) 2. Detect your OpenClaw installation paths 3. Find Claude CLI credentials (offers to install CLI and help with sign-in if needed) 4. Configure the Anthropic model (if not already set up) 5. Install the token sync watcher (inotifywait or timer fallback) 6. Detect Claude CLI (container or host) and install the auto-refresh trigger 7. Test the CLI invocation to confirm it works 8. Verify everything works Every install step asks for your confirmation first — you have full control over what gets installed. ## 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 (wizard can install it) - **python3** (wizard offers to install if missing) - **curl** (wizard offers to install if missing) - **inotify-tools** (optional, wizard offers to install 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 ## Author **ROOH** — [www.rooh.red](https://www.rooh.red) ## License MIT