Documentation
¶
Overview ¶
Anthropic's browser-login flow, unlike OpenAI's (openai.go), is not something extracted from a real shipping client — no local reference implementation of it was found anywhere (see DECISIONS.md). The client_id below is publicly known only because it has been reverse-engineered from Anthropic's own official `claude` CLI by outside projects; Anthropic does not document a public OAuth client for third-party tools. It is real and has been used successfully elsewhere, but it is unofficial: it can break without warning if Anthropic ever rotates it, and using it is closer to "wearing the official CLI's clothes" than a sanctioned integration. Kram surfaces this honestly in the accounts screen rather than presenting it as first-class support — see accounts.go's "(beta, não oficial)" label on this option.
Unlike OpenAI's ChatGPT login (openai.go), the OAuth access token here is not itself a usable inference credential — live-tested against a real Claude Pro/Max account: sending it directly as a Bearer token to /v1/messages or /v1/models is rejected with a scope permission_error (missing user:inference), even though it was requested. What the org:create_api_key scope is actually for, confirmed live against the same account, is a one-shot call to POST https://api.anthropic.com/api/oauth/claude_cli/create_api_key (Bearer: the OAuth access token), which mints a real, permanent, standard `sk-ant-...` API key — the same kind of credential the accounts screen already handles by pasting one in. So this flow exchanges the code once, immediately mints that key, and hands back a permanent string exactly like OpenRouterAuthorize does — there is no refresh path here, unlike OpenAI's, because nothing short-lived is ever stored.
Package oauthflow runs each provider's browser-login PKCE flow end to end: generate a PKCE pair, send the user's browser to the provider's own login/consent page, catch the redirect on a local callback server, and exchange the code for a credential.
OpenRouter's flow (this file) is the simple case: the exchange returns a permanent API key, used exactly like a pasted one. Anthropic's (anthropic.go) and OpenAI's (openai.go) flows instead return a short-lived access token plus a refresh token — see Token in token.go — because they authenticate a subscription (Claude Pro/Max, ChatGPT Plus/Pro), not a developer API key. Gemini and OpenCode Zen have no OAuth flow at all; the accounts screen falls back to pasting a key for those.
Index ¶
- func AnthropicAuthorize() (authURL string, wait func(ctx context.Context) (string, error), err error)
- func OpenAIAuthorize() (authURL string, wait func(ctx context.Context) (Token, error), err error)
- func OpenRouterAuthorize() (authURL string, wait func(ctx context.Context) (string, error), err error)
- func RefreshFunc(acctID string) func(ctx context.Context, refreshToken string) (Token, error)
- type Token
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func AnthropicAuthorize ¶
func AnthropicAuthorize() (authURL string, wait func(ctx context.Context) (string, error), err error)
AnthropicAuthorize starts the local callback listener and returns the authorization URL to open in a browser, plus a function that blocks until the flow completes and returns a real, permanent Anthropic API key. Same split-in-two shape as OpenRouterAuthorize.
func OpenAIAuthorize ¶
OpenAIAuthorize starts the local callback listener and returns the authorization URL to open in a browser, plus a function that blocks until the flow completes and returns the ChatGPT-subscription token pair. Mirrors OpenRouterAuthorize's split-in-two shape (see openrouter.go) for the same reason: the caller shows the URL immediately and only blocks on wait() from a background command.
func OpenRouterAuthorize ¶
func OpenRouterAuthorize() (authURL string, wait func(ctx context.Context) (string, error), err error)
OpenRouterAuthorize starts the local callback listener and returns the authorization URL to open in a browser, plus a function that blocks until the flow completes (or times out/fails) and returns the real API key. Split into two steps so the caller (the CLI) can show the URL immediately and only block on wait() from a background command.
func RefreshFunc ¶
RefreshFunc returns the refresh function for a providercatalog account ID, or nil if that account has no refreshable OAuth flow — true for OpenRouter and Anthropic both, which exchange for a permanent credential with nothing to refresh (see their own files' doc comments), so only OpenAI's ChatGPT login is present here. Centralizes the account-ID → refresh-function mapping so callers (the CLI's ping command, the gateway's provider wiring) don't each hardcode their own copy of it.
Types ¶
type Token ¶
Token is a refreshable OAuth credential: a short-lived access token plus a refresh token that can mint a new one once Access expires. This is the shape OpenAI's ChatGPT browser login returns (see openai.go) — unlike OpenRouter's and Anthropic's flows, both of which exchange once for a permanent credential (a developer API key either directly, or via one extra create-key call for Anthropic — see anthropic.go's doc comment) and have no ongoing use for this type.