oauthflow

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Aug 28, 2026 License: MIT Imports: 12 Imported by: 0

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

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

func OpenAIAuthorize() (authURL string, wait func(ctx context.Context) (Token, error), err error)

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

func RefreshFunc(acctID string) func(ctx context.Context, refreshToken string) (Token, error)

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

type Token struct {
	Access    string
	Refresh   string
	ExpiresAt time.Time
}

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.

func RefreshOpenAI

func RefreshOpenAI(ctx context.Context, refreshToken string) (Token, error)

RefreshOpenAI exchanges a still-valid refresh token for a new access token, without involving the browser again.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL