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>
This commit is contained in:
263
README.md
263
README.md
@@ -1,3 +1,262 @@
|
||||
# openclaw_oauth_sync
|
||||
# OAuth Fix for OpenClaw + Claude Max
|
||||
|
||||
Automatic Anthropic OAuth token refresh and sync for OpenClaw. Keeps your Claude Max tokens alive indefinitely.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user