Automatic Anthropic OAuth token refresh and sync for OpenClaw. Keeps your Claude Max tokens alive indefinitely.
Go to file
shamid202 32a4e739dc Improve wizard UX: CLI install/sign-in flow, user-controlled installs, ROOH credit
- Add ROOH credit (www.rooh.red) to banner, summary, and README
- Step 1: Offer to install python3 and curl instead of hard-failing
- Step 3: Detect missing Claude CLI and offer to install via npm
- Step 3: Detect not-signed-in CLI and offer interactive OAuth sign-in
- Step 3: Provide clear instructions and exit paths at every decision point
- Update README with correct git clone URL and wizard capabilities
- All install steps now require user confirmation before proceeding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-27 02:08:50 +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 Improve wizard UX: CLI install/sign-in flow, user-controlled installs, ROOH credit 2026-02-27 02:08:50 +07:00
setup.sh Improve wizard UX: CLI install/sign-in flow, user-controlled installs, ROOH credit 2026-02-27 02:08:50 +07:00

OAuth Fix for OpenClaw + Claude Max

Created by ROOHwww.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

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:

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

Author

ROOHwww.rooh.red

License

MIT