Model or dataset
griffinmartin/opencode-claude-auth avatar
griffinmartin/opencode-claude-auth

opencode-claude-auth copies your Claude OAuth token into a second store and auto-updates on `@latest`

OpenCode plugin that uses your existing Claude Code credentials — no separate login needed.

1,299 stars189 forksTypeScriptMIT

At a glance

What is it?
This is an OpenCode plugin that registers its own Anthropic auth provider, reads the OAuth token Claude Code already has on your machine, and refreshes it in the background so you never log in twice. The mechanism is short and readable, but it deliberately puts one credential in two stores, tells you to install from a floating npm tag, and offers an install path that fetches its instructions from a mutable branch for an agent to follow.
Who is it for?
Take this plugin if you already pay for Claude Code and want OpenCode to use the same session without a second login or an API key, and if you are on macOS where the Keychain path avoids a plaintext credentials file. Four things to check before you hand it a token.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 12 days ago.
What is it written in?
Mainly TypeScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The install path tells an agent to fetch instructions from `main` and follow them

There are two documented ways in. The manual one is a single line added to `~/.config/opencode/opencode.json`:

json
{
  "plugin": ["opencode-claude-auth@latest"]
}

The other is offered as letting an LLM do it. You paste one instruction into any agent, Claude Code, OpenCode or Cursor among them, and that instruction is this:

code
Install the opencode-claude-auth plugin and configure it by following: https://raw.githubusercontent.com/griffinmartin/opencode-claude-auth/main/installation.md

Read what that asks. The agent retrieves a document from a GitHub raw URL, with no tag or commit in the path, and executes whatever it says. That agent is, by construction, one that can write OpenCode configuration, and once the plugin runs it holds a long-lived Anthropic OAuth token. `installation.md` is also listed in the package's `files` array, so it ships inside the npm tarball as well as living on the branch.

The safe version is to read that file yourself and apply it by hand.

`@latest` in the plugin list means the credential-holding code floats

The note attached to the plugin entry says the `@latest` tag ensures OpenCode always pulls the newest version on startup, and that no manual `npm install` is needed because OpenCode automatically installs npm plugins using Bun at startup.

So the lifecycle is: every time you launch OpenCode, a package manager fetches the newest published build and runs it. There is no lockfile consulted at that point and no version to roll back to. The repository does have a `pnpm-lock.yaml`, which pins contributors, but that has no bearing on what an installed plugin resolves to on a user's machine.

This is the ordinary plugin convention, and it is fine for a plugin that adjusts colours. This plugin's job is to sit between your Anthropic credentials and every request the agent makes, so the part of the system that floats is the part holding the secret.

For reference, the manifest is at `2.2.1`, matching the latest tag v2.2.1 from 2026-09-22, after v2.2.0 on 2026-09-01 and v2.1.6 on 2026-08-03.

The token is read from one store and deliberately written to another

The plugin registers its own auth provider with a custom fetch handler that intercepts all Anthropic API requests, so no built-in Anthropic auth plugin is needed. Credentials come from one of two places, checked in order: the macOS Keychain, scanning every entry named `Claude Code-credentials*`, and failing that `~/.claude/.credentials.json`, or `$CLAUDE_CONFIG_DIR/.credentials.json` when that environment variable is set.

Then it syncs those credentials into OpenCode's own `auth.json` as a fallback. On Windows it writes to both `%USERPROFILE%\.local\share\opencode\auth.json` and `%LOCALAPPDATA%\opencode\auth.json`, with the stated reason of covering all installation methods.

So the same OAuth token lives in two stores by design, and on Windows in two paths, and a background re-sync runs every five minutes to keep the copy current. In memory it is cached with a 30-second TTL.

Those three cadences are worth keeping separate in your head: 30 seconds for the in-process cache, five minutes for the copy into OpenCode's store, and whatever Anthropic decides the token lifetime is.

Refresh goes over HTTP first, then shells out to `claude`, and a make target rotates the token for real

When a token is near expiry the plugin refreshes it directly through Anthropic's OAuth endpoint, which the project notes consumes zero LLM tokens. If that direct refresh fails, it falls back to the Claude CLI. So the failure path of a credential refresh is executing a different agent's binary from inside your coding agent.

The repository also exposes this as a build target, with two variants and their own descriptions:

make
validate-oauth: build  ## Run live OAuth refresh (rotates token, writes back)
validate-oauth-dry: build  ## Dry-run OAuth refresh (no network request)

That is a scripted, documented way to rotate the live Claude Code OAuth token on your machine and write the new one back, and the dry-run variant skips the network request entirely. The help text says so plainly.

The remaining troubleshooting rows are more mundane and more useful in practice: a locked Keychain is fixed with `security unlock-keychain ~/Library/Keychains/login.keychain-db`, a read timeout by restarting Keychain Access, and missing credentials on Linux or Windows by running `claude` once so the file exists.

Six export subpaths resolve to two different files under one shared types entry

The manifest declares six subpaths in `exports`, and every one of them points `types` at the same file, `./dist/index.d.ts`. The runtime targets split in two. The root `.` and `./server` both resolve to `./opencode-claude-auth.js`, a file checked in at the repository root. The other four, `./claude-auth-plugin`, `./claude-auth-plugin.js`, `./opencode-claude-auth` and `./opencode-claude-auth.js`, all resolve to `./dist/index.js`, the compiled output.

Three of those four differ from one another only by a trailing `.js` on the subpath, which a normal resolver collapses back to the same file. And `./server` is the same module as the root export, so a subpath named for a server leads to the plugin entry point.

The practical effect is a mismatch. TypeScript sees one declaration file for all six paths, while Node may load either the checked-in root script or the compiled `dist` build depending on which subpath the consumer wrote. `main` and `module` both point at the root script, and `files` ships the root script, `dist` and `installation.md`.

The test command hands its glob to the shell and strips types experimentally

The test script is one line:

json
"test": "node --test --experimental-strip-types src/**/*.test.ts scripts/**/*.test.ts"

Two mechanisms sit in it. The `**` is not expanded by Node's test runner, which takes file paths as arguments; it is expanded by whichever shell runs the script. Under a shell without globstar, `src/**/*.test.ts` collapses toward a single-level match and test files sitting directly in `src/` are not passed to the runner at all. Test discovery here depends on the shell that invoked pnpm.

`--experimental-strip-types` is the other half. It lets the runner execute TypeScript sources without compiling them, which is why tests live next to the code rather than in `dist`. It is an experimental flag, so the test setup depends on a runtime capability that has changed name and behaviour across Node releases.

Three other scripts each rebuild before running, `test:models`, `test:headless` and `intercept`, so checking anything live costs a `rm -rf dist && tsc` first. The tree also carries `test-results/` in version control alongside `specs/` and `scripts/`.

The 14 model names include three models twice, and verifying the list costs a live request

The supported model table has fourteen rows, and three entries appear twice under a rolling name and a dated snapshot: `claude-haiku-4-5` and `claude-haiku-4-5-20251001`, `claude-opus-4-5` and `claude-opus-4-5-20251101`, `claude-sonnet-4-5` and `claude-sonnet-4-5-20250929`. The rest are undated point releases such as `claude-opus-4-6`, `claude-opus-4-7` and `claude-sonnet-5`.

So a hardcoded table is carrying both an alias and a pinned identifier for the same weights. Which one the plugin actually sends is not something the table settles.

The instruction attached to it is that you run `pnpm run test:models` to verify against your account. That script runs `pnpm run build` and then `node scripts/test-models.ts`, so confirming the list is a live call against your own credentials, not a static check anyone else can run for you. It is the right way to test an auth plugin, and it means the table in the README is a snapshot rather than a contract.

The named alternative puts your Claude session behind a local plaintext service

The project names its own substitute and tells you not to combine them. If you run CLIProxyAPI, it logs in to your Claude Code account with OAuth through its `-claude-login` flag, holds an `api-keys` entry in its `config.yaml`, handles token refresh itself, and exposes a Claude-compatible API on `http://localhost:8317` by default. You then point OpenCode's built-in Anthropic provider at it:

json
{
  "provider": {
    "anthropic": {
      "options": {
        "baseURL": "http://localhost:8317",
        "apiKey": "your-api-key-1"
      }
    }
  }
}

Read that config closely. It is plaintext HTTP to a local port, guarded by a static string, and the documentation's own example value is the literal placeholder `your-api-key-1`. The trade is explicit: this plugin is called the lighter option because you need no extra service running.

That framing also explains why the plugin goes to the trouble of writing two different paths on Windows. The alternative adds a process, a port and a key of your own; this one adds a Keychain read and a copy. CLIProxyAPI additionally supports multi-account load balancing and other providers, Codex, Gemini and Grok, which is the capability you give up.

Editorial conclusion

Take this plugin if you already pay for Claude Code and want OpenCode to use the same session without a second login or an API key, and if you are on macOS where the Keychain path avoids a plaintext credentials file. Four things to check before you hand it a token. It duplicates your credential into OpenCode's own store by design, and on Windows into two paths, so understand where the token now lives before you do. The plugin entry pins nothing, so every OpenCode start can pull a different build of the code that holds your token. If you use the agent-directed install, the instructions come from the `main` branch at execution time, not from a tag. And note the documented alternative runs a local plaintext service on port 8317, which is the trade this plugin exists to avoid.

Frequently asked questions

How do I install opencode-claude-auth into OpenCode?

Add `{"plugin": ["opencode-claude-auth@latest"]}` to `~/.config/opencode/opencode.json`. No manual `npm install` is required because OpenCode automatically installs npm plugins using Bun at startup, and the `@latest` tag is what makes it pull the newest version on each launch. There is also an agent-directed option that fetches installation instructions from the repository's main branch.

Where does opencode-claude-auth read Claude Code credentials from?

It checks two sources in order. On macOS it reads every macOS Keychain entry named `Claude Code-credentials*`, which is also how multiple Claude Code accounts are detected. Otherwise it falls back to `~/.claude/.credentials.json`, or to `$CLAUDE_CONFIG_DIR/.credentials.json` when that environment variable is set. It then syncs those credentials into OpenCode's own auth.json.

Do I need a separate login or API key to use opencode-claude-auth?

No separate login is needed. The prerequisites are Claude Code installed and authenticated, meaning you have run `claude` at least once, and OpenCode installed. macOS is preferred because it uses the Keychain, while Linux and Windows work through the credentials file fallback. If your credentials are not OAuth-based, the plugin falls through to standard API key auth.

Can I run opencode-claude-auth together with CLIProxyAPI?

No, and the project says so explicitly. CLIProxyAPI exposes a Claude-compatible API on http://localhost:8317 by default after logging in through its `-claude-login` flag, and you should remove opencode-claude-auth from the `plugin` list when using that setup, because the two approaches should not be combined. CLIProxyAPI also adds multi-account load balancing and support for Codex, Gemini and Grok.

Official sources

  1. griffinmartin/opencode-claude-auth on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/griffinmartin-opencode-claude-auth.svg)](https://hysenlabs.com/projects/griffinmartin-opencode-claude-auth)