opencode-claude-auth
Self-contained Anthropic auth provider for OpenCode 2 using your Claude Code subscription credentials — no separate login or API key needed.
3.0.0 and later require OpenCode 2.0+ and will not load on OpenCode 1.x. The last version that supports OpenCode 1.x is 2.2.1 — see Migrating from OpenCode 1.
Compatibility
| opencode-claude-auth | OpenCode | Config key | Install |
|---|---|---|---|
| 3.0.0+ | 2.0+ only | plugins |
opencode-claude-auth@latest |
| 2.2.1 and earlier | 1.x | plugin |
opencode-claude-auth@2.2.1 |
How it works
The plugin registers a Claude Code subscription auth method on OpenCode's built-in Anthropic provider. Run /connect, choose Anthropic, then Import Claude Code subscription — it reads OAuth tokens directly from the macOS Keychain (or ~/.claude/.credentials.json — or $CLAUDE_CONFIG_DIR/.credentials.json if that env var is set — on other platforms) and hands them to OpenCode.
OpenCode's native Anthropic provider handles prompt conversion and streaming. Session hooks add the Claude Code identity and adjust subscription requests to set Authorization: Bearer with the current access token (and strip x-api-key), translate tool names, and process responses. OpenCode owns the credential's lifecycle from there and calls back into the plugin to refresh it as needed. Refreshes go directly to Anthropic's OAuth endpoint (zero LLM tokens consumed); the plugin always checks the keychain first and adopts whatever is there if it differs from what OpenCode handed back, so an independent claude re-login or refresh doesn't leave OpenCode stuck retrying a dead token. It falls back to the claude CLI only in the narrow window where Claude Code will actually rotate the token.
Alternative: CLIProxyAPI (plugin not required)
If you run CLIProxyAPI, you don't need this plugin. CLIProxyAPI logs in to your Claude Code account with OAuth, handles token refresh, and exposes a Claude-compatible API on http://localhost:8317 by default. Point OpenCode's built-in Anthropic provider at it:
-
Log in with your Claude Code account (via the proxy's
-claude-loginflag) and set anapi-keysentry in itsconfig.yaml. -
Configure the Anthropic provider in
~/.config/opencode/opencode.json:{ "provider": { "anthropic": { "options": { "baseURL": "http://localhost:8317", "apiKey": "your-api-key-1" } } } }
Remove opencode-claude-auth from your plugins list when using this setup — the two approaches shouldn't be combined. CLIProxyAPI also supports multi-account load balancing and other providers (Codex, Gemini, Grok). This plugin remains the lighter option if you only need Claude Code credentials in OpenCode with no extra service running.
Prerequisites
- OpenCode 2 installed — see the OpenCode 2 migration guide for installation instructions
- Claude Code installed and authenticated (run
claudeat least once)
macOS is preferred (uses Keychain). Linux and Windows work via the credentials file fallback.
Installation
For Humans
Option A: Let an LLM do it
Paste this into any LLM agent (Claude Code, OpenCode, Cursor, etc.):
Install the opencode-claude-auth plugin and configure it by following: https://raw.githubusercontent.com/griffinmartin/opencode-claude-auth/main/installation.md
Option B: Manual setup
-
Add the plugin to
~/.config/opencode/opencode.json:{ "plugins": ["opencode-claude-auth@latest"] }OpenCode 2 uses the
pluginskey (plural) — OpenCode 1 usedplugin(singular). The@latesttag ensures OpenCode always pulls the newest version on startup. No manualnpm installis needed — OpenCode automatically installs npm plugins using Bun at startup. -
Connect it — restart OpenCode, run
/connect, choose Anthropic, then Import Claude Code subscription. Use/modelsand pick a model under Anthropic.
For LLM Agents
See installation.md for step-by-step agent instructions.
Usage
Run /connect once to import your Claude Code subscription, then use OpenCode normally — the plugin refreshes credentials automatically and provides them to the Anthropic API. If your credentials aren't OAuth-based, the connection falls through to standard API key auth.
Migrating from OpenCode 1
OpenCode 2 replaced the plugin and auth APIs this plugin used under OpenCode 1, so 3.0.0 and later only load on OpenCode 2.
-
Upgrade OpenCode — see the OpenCode 2 migration guide.
-
In
~/.config/opencode/opencode.json, move the plugin from thepluginkey (OpenCode 1) toplugins(OpenCode 2):{ "plugins": ["opencode-claude-auth@latest"] } -
Restart OpenCode, run
/connect, choose Anthropic, then Import Claude Code subscription. The OpenCode 1 connection isn't carried over.
Staying on OpenCode 1
2.2.1 is the last release that supports OpenCode 1. Pin it instead of tracking @latest:
{
"plugin": ["opencode-claude-auth@2.2.1"]
}
Supported models
14 supported models. Run pnpm run test:models to verify against your account.
| Model |
|---|
| claude-fable-5 |
| claude-fable-5-1 |
| claude-haiku-4-5 |
| claude-haiku-4-5-20251001 |
| claude-opus-4-5 |
| claude-opus-4-5-20251101 |
| claude-opus-4-6 |
| claude-opus-4-7 |
| claude-opus-4-8 |
| claude-opus-5 |
| claude-sonnet-4-5 |
| claude-sonnet-4-5-20250929 |
| claude-sonnet-4-6 |
| claude-sonnet-5 |
Credential sources
The plugin checks these in order:
- macOS Keychain (all
Claude Code-credentials*entries — multiple accounts are detected automatically) ~/.claude/.credentials.json(fallback, works on all platforms; ifCLAUDE_CONFIG_DIRis set, reads$CLAUDE_CONFIG_DIR/.credentials.jsoninstead)
Multiple accounts (macOS)
If you have multiple Claude Code accounts authenticated on macOS, they're all detected from the Keychain automatically and offered as a selection prompt when you run /connect. Each account is labeled by its subscription tier (Claude Pro, Claude Max, etc.).
To switch accounts, re-run /connect and pick a different one. Your selection is persisted across sessions.
If only one account is found, the selection prompt is skipped and the plugin uses it directly.
Troubleshooting
| Problem | Solution |
|---|---|
| "Credentials not found" | Run claude to authenticate with Claude Code first |
| "Keychain is locked" | Run security unlock-keychain ~/Library/Keychains/login.keychain-db |
| "Token expired and refresh failed" | The plugin runs claude CLI to refresh automatically. If this fails, re-authenticate manually by running claude |
| Not working on Linux/Windows | Ensure ~/.claude/.credentials.json exists (or $CLAUDE_CONFIG_DIR/.credentials.json if that env var is set). Run claude to create it |
| Keychain access denied | Grant access when macOS prompts you |
| Keychain read timed out | Restart Keychain Access (can happen on macOS Tahoe) |
| "Credentials are unavailable or expired" | Run claude to refresh your Claude Code credentials |
Auth keeps failing right after re-authenticating with claude |
OpenCode may still be holding a refresh token from a previous /connect that's now stale. The plugin re-syncs from the keychain automatically on the next refresh; if it's still stuck, run /connect again |
| "Extra usage is required for long context requests" | Your conversation exceeded 200k tokens. See Long context (1M) below |
| Plugin not updating to latest version | Delete the cached package: rm -rf ~/.cache/opencode/packages/opencode-claude-auth@latest/ then restart OpenCode |
opencode auth login prints "Only http and https links can be opened in the browser" |
Harmless: importing has no browser step, but the CLI tries to open one anyway. The credential is still saved — confirm with opencode auth list |
Diagnostic logging
If you're hitting auth errors that are hard to reproduce, enable debug logging to capture the full auth flow:
export CLAUDE_AUTH_DEBUG=1
Restart OpenCode and reproduce the issue. The plugin writes structured JSON logs to ~/.local/share/opencode/claude-auth-debug.log. All secrets (tokens, API keys) are automatically redacted — the log file is safe to paste into a GitHub issue.
To write logs to a custom path:
export CLAUDE_AUTH_DEBUG=/tmp/claude-auth-debug.log
Disable when done:
unset CLAUDE_AUTH_DEBUG
Long context (1M)
1M token context is supported natively — the API no longer requires a beta flag for it, so the plugin doesn't send the legacy context-1m-2025-08-07 header.
If your plan doesn't cover long context billing, requests beyond the standard window fail with "Extra usage is required for long context requests". When a long context error is caused by a beta flag (e.g. one added via ANTHROPIC_BETA_FLAGS), the plugin retries without the offending flag.
Validating OAuth refresh
To verify the direct OAuth token refresh works with your credentials:
pnpm run validate:oauth # refresh + write-back (safe, keeps credentials valid)
pnpm run validate:oauth -- --dry-run # show what would be sent without making the request
This reads your stored credentials, calls Anthropic's OAuth token endpoint, and writes the new tokens back to storage. Refresh tokens rotate on each use, so write-back is enabled by default to keep your stored credentials valid.
Environment variable overrides
All configurable parameters can be overridden via environment variables. If Anthropic changes something before we publish an update, set an env var and keep working:
| Variable | Description | Default |
|---|---|---|
ANTHROPIC_CLI_VERSION |
Claude CLI version for user-agent and billing headers | config.ccVersion in src/model-config.ts |
ANTHROPIC_USER_AGENT |
Full User-Agent string (overrides CLI version) | claude-cli/{version} (external, sdk-cli) |
ANTHROPIC_BETA_FLAGS |
Comma-separated beta feature flags | baseBetas list in src/model-config.ts |
CLAUDE_AUTH_DEBUG |
Enable diagnostic logging (1 for default path, or a custom file path) |
disabled |
CLAUDE_CONFIG_DIR |
Claude Code config directory used for the credentials-file fallback (reads $CLAUDE_CONFIG_DIR/.credentials.json). macOS still checks the Keychain first. |
~/.claude |
OPENCODE_CLAUDE_AUTH_MAX_RETRY_MS |
Max ms the plugin waits when honouring a 429/529 retry-after header. Beyond this cap the response surfaces immediately so OpenCode doesn't appear to hang on hour-long quota resets. |
30000 |
OPENCODE_CLAUDE_AUTH_TOOL_REPAIR |
Strategy for reconciling tool_use/tool_result adjacency broken by OpenCode auto-compaction. placeholder synthesizes a paired result for orphaned tool_use blocks (lossless, preserves thinking blocks); drop removes orphaned blocks (omitting whole thinking turns). |
placeholder |
OPENCODE_CLAUDE_AUTH_REFRESH_WAIT_MS |
Max ms a single request waits through a transient token-refresh rate-limit (429) before returning a retryable error instead of a hard "run claude". |
45000 |
OPENCODE_CLAUDE_AUTH_REFRESH_COOLDOWN_MS |
Base per-account cooldown after a rate-limited refresh, before the plugin retries the token endpoint. Escalates with consecutive failures and is jittered; capped at 60s. | 15000 |
OPENCODE_CLAUDE_AUTH_REFRESH_LOCK_TTL_MS |
TTL for the cross-process refresh lock. A held lock older than this is treated as stale (crashed holder) and taken over. | 20000 |
OPENCODE_CLAUDE_AUTH_REFRESH_LOCK_DIR |
Directory for the advisory cross-process refresh lock files. | OpenCode data dir (~/.local/share/opencode) |
Example:
export ANTHROPIC_CLI_VERSION=2.2.0
How it works (technical)
- Registers a Claude Code subscription auth method on OpenCode's built-in Anthropic integration (
ctx.integration.transform), surfaced as Import Claude Code subscription under/connect - Uses OpenCode 2's native Anthropic provider for prompt conversion and streaming, while session HTTP hooks in
src/index.tsapply the subscription transport insrc/provider.tsonly to Claude Code OAuth connections - The request hook sets
Authorization: Bearerwith the current OAuth access token and stripsx-api-key, translates tool names between OpenCode and Anthropic API formats (adds/stripsmcp_prefix), and sets required API headers (beta flags, billing, user-agent) with model-aware selection; the response hook buffers SSE streams at event boundaries for reliable tool name translation - Injects the Claude Code identity into system prompts via the
context,compaction,generate, andtitlesession hooks — OpenCode 2 dispatches a separate hook per request kind, and all four bill to the connected subscription - On macOS, enumerates all
Claude Code-credentials*Keychain entries and labels them by subscription tier; multiple accounts are offered as a selection prompt during/connect - OpenCode persists the resulting OAuth credential (access + refresh token) itself and calls back into the plugin's
refreshhook whenever it decides one is needed — the plugin doesn't run its own background timer - Before ever attempting a network refresh, re-reads the keychain directly and adopts whatever is there if it differs from the token OpenCode handed back. This keeps things working if
clauderotated credentials independently of this connection (an interactive re-login, its own periodic refresh, ...) — otherwise OpenCode would keep retrying the same now-dead refresh token on every call, forever - OpenCode's
refreshcallback and the request path share one in-flight refresh per account behind a cross-process lock, so they never spend the same single-use refresh token twice - Refreshes directly via
POST https://claude.ai/v1/oauth/tokenusing the runtime's ownfetch(no LLM tokens consumed, no subprocess); falls back to theclaudeCLI only within the window where Claude Code will actually rotate the token, and writes new tokens back to Keychain (macOS) or the credentials file (Linux/Windows) to keep stored credentials in sync - Retries API requests on 429 (rate limit) and 529 (overloaded) with exponential backoff, respecting
retry-afterheaders - On a 401, recovers in place rather than surfacing it: adopts an externally rotated token if the keychain now holds one, otherwise forces an OAuth refresh, then retries the request
- If credentials aren't OAuth-based, the connection falls through to standard API key auth
- If credentials are unavailable or unreadable, the plugin logs the error and OpenCode continues without a Claude Code auth connection
Disclaimer
This plugin uses Claude Code's OAuth credentials to authenticate with Anthropic's API. Anthropic's Terms of Service state that Claude Pro/Max subscription tokens should only be used with official Anthropic clients. This plugin exists as a community workaround and may stop working if Anthropic changes their OAuth infrastructure. Use at your own discretion.
License
MIT