Documentation
¶
Overview ¶
Package oauth provides minimal, vendor-neutral OAuth 2.0 flow helpers for acquiring and refreshing provider credentials. It is a thin convenience layer over golang.org/x/oauth2 covering the authorization-code grant with PKCE (RFC 7636) and the device authorization grant (RFC 8628).
It is shared by SDK consumers driving an interactive login and by the relay server doing server-side refresh. It owns flow *machinery* only — acquiring and refreshing tokens on demand — never lifecycle: it starts no background goroutines and persists nothing. Storage and refresh scheduling are the caller's responsibility.
Vendor specifics (endpoints, client id, scopes) live in sdk/adapters/<vendor>/; construct an *oauth2.Config there and pass it here. This package imports nothing from app/ or internal/ (SDK module purity).
Index ¶
- type Flow
- func (f *Flow) AuthorizeURL(state string, opts ...oauth2.AuthCodeOption) (url, verifier string)
- func (f *Flow) DeviceAuth(ctx context.Context, opts ...oauth2.AuthCodeOption) (*oauth2.DeviceAuthResponse, error)
- func (f *Flow) DeviceToken(ctx context.Context, da *oauth2.DeviceAuthResponse, ...) (*Token, error)
- func (f *Flow) Exchange(ctx context.Context, code, verifier string, opts ...oauth2.AuthCodeOption) (*Token, error)
- func (f *Flow) Refresh(ctx context.Context, tok *Token) (*Token, error)
- func (f *Flow) TokenSource(tok *Token, p Persister) *TokenSource
- type PersistFunc
- type Persister
- type ProviderConfig
- type Token
- type TokenSource
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
This section is empty.
Types ¶
type Flow ¶
type Flow struct {
// contains filtered or unexported fields
}
Flow drives OAuth grants for a single provider configuration. It holds no per-request state and is safe to reuse and share across goroutines.
func (*Flow) AuthorizeURL ¶
func (f *Flow) AuthorizeURL(state string, opts ...oauth2.AuthCodeOption) (url, verifier string)
AuthorizeURL builds the authorization-code URL to send the user to (browser / redirect contexts) and returns the PKCE verifier the caller must retain and hand to Exchange. A fresh verifier is generated per call, per RFC 7636.
func (*Flow) DeviceAuth ¶
func (f *Flow) DeviceAuth(ctx context.Context, opts ...oauth2.AuthCodeOption) (*oauth2.DeviceAuthResponse, error)
DeviceAuth begins the device authorization grant (RFC 8628), for headless / CLI contexts with no browser to redirect. The returned response carries the user code and verification URI to display to the user.
func (*Flow) DeviceToken ¶
func (f *Flow) DeviceToken(ctx context.Context, da *oauth2.DeviceAuthResponse, opts ...oauth2.AuthCodeOption) (*Token, error)
DeviceToken polls the token endpoint until the user completes authorization or the device code expires. It blocks; x/oauth2 runs the RFC 8628 polling loop (honouring authorization_pending / slow_down) internally.
func (*Flow) Exchange ¶
func (f *Flow) Exchange(ctx context.Context, code, verifier string, opts ...oauth2.AuthCodeOption) (*Token, error)
Exchange swaps an authorization code, plus the matching PKCE verifier from AuthorizeURL, for a token.
func (*Flow) Refresh ¶
Refresh returns a non-expired token. A still-valid token is returned unchanged with no network call; an expired one is exchanged via its refresh token (x/oauth2 carries the refresh token forward if the response omits a new one). It never mutates the input and never persists — the caller owns storage. Errors if the token is expired and has no refresh token.
func (*Flow) TokenSource ¶
func (f *Flow) TokenSource(tok *Token, p Persister) *TokenSource
TokenSource builds a source seeded with the consumer's stored token. Pass a Persister to have refreshes (and their refresh-token rotations) written back; pass nil for in-memory only (refreshes are lost on restart).
type PersistFunc ¶
PersistFunc adapts a plain function to Persister.
type Persister ¶
Persister stores a token after it changes — the initial exchange or a refresh, which commonly rotates the refresh token. A standalone consumer implements it over its own storage (file, DB, keychain) so a process restart, and the next refresh, still work. Save runs under the TokenSource's lock and should be quick and must not call back into the same TokenSource.
type ProviderConfig ¶
type ProviderConfig struct {
ClientID string `json:"clientId" yaml:"clientId"`
// Issuer is the provider's OAuth 2.0 issuer URL. When set, Discover fetches
// its authorization-server metadata (RFC 8414) to fill any endpoint left
// empty below. Optional — leave it empty and set the endpoints explicitly if
// the provider publishes no discovery document.
Issuer string `json:"issuer,omitempty" yaml:"issuer,omitempty"`
AuthURL string `json:"authUrl" yaml:"authUrl"`
TokenURL string `json:"tokenUrl" yaml:"tokenUrl"`
DeviceAuthURL string `json:"deviceAuthUrl,omitempty" yaml:"deviceAuthUrl,omitempty"`
RedirectURI string `json:"redirectUri,omitempty" yaml:"redirectUri,omitempty"`
Scopes []string `json:"scopes,omitempty" yaml:"scopes,omitempty"`
// AuthParams are extra authorization-URL query params (e.g. {"code":"true"}
// for Anthropic's hosted copy/paste callback). Surfaced via AuthCodeOptions.
AuthParams map[string]string `json:"authParams,omitempty" yaml:"authParams,omitempty"`
}
ProviderConfig is a serializable description of an OAuth provider. It is the shape loaded from a config file — relay seeds it as a `oauth:<provider>` settings section (operator-overridable, not the catalog), and an SDK consumer can unmarshal its own YAML/JSON into it. Vendor specifics (client id, endpoints, scopes) are config data, never values baked into this module, so a provider rotating its client id or endpoints is a config edit, not a release.
func (ProviderConfig) AuthCodeOptions ¶
func (c ProviderConfig) AuthCodeOptions() []oauth2.AuthCodeOption
AuthCodeOptions converts AuthParams into authorize-URL options for (*Flow).AuthorizeURL.
func (ProviderConfig) Discover ¶
func (c ProviderConfig) Discover(ctx context.Context, hc *http.Client) (ProviderConfig, error)
Discover fills any endpoint the caller left empty (AuthURL, TokenURL, DeviceAuthURL) from the provider's OAuth 2.0 Authorization Server Metadata (RFC 8414) at Issuer + "/.well-known/oauth-authorization-server". Endpoints the caller already set are preserved — explicit config always wins. Client id, scopes, redirect URI, and authorize params are never touched (app intent, not server metadata).
It is a no-op (no network call) when AuthURL and TokenURL are both already set. Otherwise Issuer must be set. hc is the HTTP client (nil → http.DefaultClient); the request honours ctx. Returns a new ProviderConfig; the receiver is unchanged.
func (ProviderConfig) OAuth2 ¶
func (c ProviderConfig) OAuth2() *oauth2.Config
OAuth2 builds the x/oauth2 config. AuthStyle is forced to in-params: these are public PKCE clients that carry client_id in the request, with no client secret to send via Basic auth.
type Token ¶
Token is the serializable credential a flow produces. It is the wire contract between an interactive-login client and whatever stores the credential (e.g. the relay oauth host-key value). It is an alias of oauth2.Token so a stored token round-trips back into Refresh without conversion; its JSON carries access_token, refresh_token, and expiry.
type TokenSource ¶
type TokenSource struct {
// contains filtered or unexported fields
}
TokenSource hands out valid access tokens for one credential, refreshing on expiry and persisting the (possibly rotated) result via its Persister. It is the standalone path — own your token lifecycle in-process, no relay required.
It is safe for concurrent use: refreshes are serialized so only one network refresh happens per expiry, and the rotated token is persisted exactly once. This is the relay-free counterpart to relay's server-side resolver; both lean on the same Flow.Refresh machinery, so neither party is privileged.
func (*TokenSource) AccessToken ¶
func (ts *TokenSource) AccessToken(ctx context.Context) (string, error)
AccessToken is a convenience for callers that only need the bearer string.