Skip to content

fix: use form-urlencoded and Claude CLI User-Agent for OAuth token requests - #12

Closed
MoerAI wants to merge 1 commit into
ex-machina-co:mainfrom
MoerAI:fix/oauth-token-exchange-429
Closed

MoerAI wants to merge 1 commit into
ex-machina-co:mainfrom
MoerAI:fix/oauth-token-exchange-429

Conversation

@MoerAI

@MoerAI MoerAI commented Mar 20, 2026

Copy link
Copy Markdown

Summary

  • Switch Content-Type from application/json to application/x-www-form-urlencoded for both token exchange (exchange()) and token refresh requests
  • Add User-Agent: claude-cli/2.1.2 (external, cli) header to token exchange and refresh requests — this header was already set for API calls in setOAuthHeaders() but was missing from the OAuth token endpoint requests
  • Update tests to validate the new request format and headers

Root Cause

Anthropic's /v1/oauth/token endpoint validates the request shape including User-Agent. Requests without the Claude CLI User-Agent are rate-limited with HTTP 429 (rate_limit_error: "Rate limited. Please try again later."), even when the authorization code is valid.

The Content-Type: application/json was also incorrect — the OAuth token endpoint expects application/x-www-form-urlencoded per the OAuth 2.0 spec (RFC 6749 §4.1.3).

Verified Fix

After applying both changes:

  1. opencode auth login → Anthropic → Claude Pro/Max → successfully obtains tokens
  2. Claude models work correctly through OpenCode

Related Issues

Test plan

  • All 59 existing tests pass
  • auth.test.ts updated to verify form-urlencoded body and User-Agent header
  • index.test.ts updated to parse URLSearchParams for refresh token body
  • Manual end-to-end test: opencode auth login with Claude Pro/Max succeeds

🤖 Generated with Claude Code

…quests

The OAuth token exchange and refresh requests were sending
`Content-Type: application/json` with `JSON.stringify()` body, and
no `User-Agent` header. Anthropic's `/v1/oauth/token` endpoint
rate-limits (429) requests that don't match the expected Claude CLI
request shape.

Changes:
- Switch Content-Type to `application/x-www-form-urlencoded` with
  `URLSearchParams` body for both `exchange()` and token refresh
- Add `User-Agent: claude-cli/2.1.2 (external, cli)` header to
  token exchange and refresh requests (already used for API calls)
- Update tests to validate form-urlencoded body and new headers

Fixes: anomalyco/opencode#18329, anomalyco/opencode#15562

Co-Authored-By: Claude Opus 4.6 (1M context) <[email protected]>
@MoerAI

MoerAI commented Mar 20, 2026

Copy link
Copy Markdown
Author

Closing this PR as OpenCode has officially dropped Claude OAuth support following Anthropic's legal request (March 20, 2026).

While the technical fix here was valid (Content-Type and User-Agent issues), Anthropic's policy decision to block all third-party OAuth clients at the token endpoint makes this moot going forward.

Thank you for maintaining this plugin.

@eXamadeus

eXamadeus commented Mar 26, 2026 •

Copy link
Copy Markdown
Member

@MoerAI Thanks for taking the time to open a PR!

While it's correct that Anthropic is no longer able to support OAuth directly, this plugin was forked to maintain that support. There is no direct block from the Anthropic side for third-party OAuth, they have (to my knowledge) only limited direct integration from OpenCode since they're a for-profit business. I have not seen any credible reports of anyone being banned for using third-party authentication.

We have merged in a solution to the utilize the authentication methodologies (#14) and with this plugin you can continue to use Claude via OAuth. I am not an attorney, so I can't guarantee the legal side of things, but from what I've read of their TOS and policies this isn't an issue.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants