oauth

package
v0.7.0 Latest Latest
Warning

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

Go to latest
Published: Jul 12, 2026 License: Apache-2.0 Imports: 7 Imported by: 0

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

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 New

func New(cfg *oauth2.Config) *Flow

New returns a Flow bound to cfg.

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

func (f *Flow) Refresh(ctx context.Context, tok *Token) (*Token, error)

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

type PersistFunc func(ctx context.Context, tok *Token) error

PersistFunc adapts a plain function to Persister.

func (PersistFunc) Save

func (f PersistFunc) Save(ctx context.Context, tok *Token) error

Save implements Persister.

type Persister

type Persister interface {
	Save(ctx context.Context, tok *Token) error
}

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

type Token = oauth2.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.

func (*TokenSource) Token

func (ts *TokenSource) Token(ctx context.Context) (*Token, error)

Token returns a non-expired token, refreshing and persisting first if the current one has expired. Concurrent callers share a single refresh.

Jump to

Keyboard shortcuts

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