Automatic Anthropic OAuth token refresh and sync for OpenClaw. Keeps your Claude Max tokens alive indefinitely.
Go to file
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
configs Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
docs Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
scripts Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
templates Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
tests Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
LICENSE Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
README.md Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00
setup.sh Add complete OAuth token refresh and sync solution 2026-02-27 01:51:18 +07:00

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

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:

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):

{
  "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 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 for all formats.

Manual Installation

If you prefer not to use the wizard:

1. Install inotify-tools

apt install inotify-tools

2. Edit and install the sync script

# 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

# 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

# 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.

Verification

# 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

./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 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

License

MIT