Documentation
¶
Overview ¶
Package auth provides the generic TokenSource abstraction and shared OAuth2 primitives (JWKS, discovery, scope builder) used by every authenticated SDK call.
auth is intentionally provider-neutral. Concrete providers live in sub-packages: auth/smart (SMART-on-openEHR), auth/clientcreds (Client Credentials), auth/jwtbearer (JWT Bearer), auth/basic (HTTP Basic on openEHR REST). Further providers (plain OIDC, session-cookie) MAY be added without disturbing the TokenSource contract.
Implements REQ-060, REQ-066, REQ-068 (partial: clientcreds, jwtbearer), and REQ-069 (auth/basic) per docs/specifications/auth.md. SMART PKCE (REQ-061..064) lives in auth/smart (planned).
Index ¶
Constants ¶
const TokenTypeBasic = "Basic"
TokenTypeBasic is the HTTP Basic scheme; Value MUST be the base64-encoded user-pass payload per RFC 7617 (REQ-069).
const TokenTypeBearer = "Bearer"
TokenTypeBearer is the OAuth2 access-token scheme (default when Type is empty).
Variables ¶
var ( // ErrTokenExchangeFailed indicates the authorization server // rejected a token-exchange request (authorization_code, // client_credentials, jwt-bearer). ErrTokenExchangeFailed = errors.New("auth: token exchange failed") // ErrRefreshFailed indicates a refresh_token grant against the // token endpoint failed. ErrRefreshFailed = errors.New("auth: token refresh failed") // ErrReauthRequired indicates the cached token cannot be refreshed // without consumer intervention (refresh_token absent or rejected // terminally). Consumers MUST restart the launch flow. ErrReauthRequired = errors.New("auth: re-authentication required") // ErrInvalidConfig indicates a provider was constructed with // missing or contradictory required fields (e.g. no token endpoint, // no client id). ErrInvalidConfig = errors.New("auth: invalid configuration") // ErrJWKSValidationFailed indicates a JWT could not be validated // against the deployment's JWKS even after one refresh (REQ-062). ErrJWKSValidationFailed = errors.New("auth: JWKS validation failed") )
Sentinel auth errors. Detect classes with errors.Is; the underlying wire error is preserved via errors.Unwrap on the wrapping Error type.
Functions ¶
func BuildScope ¶
BuildScope composes an openEHR-formatted scope from its three parts per the SMART-on-openEHR convention: <compartment>/<resource>.<permission>.
Empty parts collapse to omitted segments — BuildScope("", "COMPOSITION", "read") returns "COMPOSITION.read". BuildScope is purely lexical and does NOT validate the parts against any scope grammar; the deployment is authoritative on which scopes it accepts (docs/specifications/auth.md § Scope handling).
The helper exists so consumers do not template scope strings by hand in the most common case; consumers MAY pass raw scopes to providers when they need shapes BuildScope does not cover.
func JoinScopes ¶
JoinScopes joins scope strings into the space-separated form the OAuth2 authorization request expects. Empty inputs are skipped.
func WithTokenSource ¶
func WithTokenSource(ctx context.Context, ts TokenSource) context.Context
WithTokenSource returns a derived context carrying ts as a per-request override. transport/ MUST consult TokenSourceFromContext on every outgoing request and prefer the per-request TokenSource over the client-default when present (docs/specifications/auth.md § Per-request TokenSource; PROBE-064).
Use case: an MCP server holds one transport.Client and forwards each incoming caller's token through ctx — the client itself does not own the user-level credentials.
Types ¶
type ExchangeError ¶
type ExchangeError struct {
// Sentinel is the categorical error class (one of the package
// sentinels). errors.Is returns true against this value.
Sentinel error
// StatusCode is the HTTP status of the token-endpoint response, or
// 0 when the failure was pre-flight (network, marshal, ctx).
StatusCode int
// OAuth2 is the parsed error envelope, if the response shape
// matched. Nil when the response was not parseable.
OAuth2 *OAuth2Error
// Inner is the underlying transport / parse / context error, if any.
Inner error
}
ExchangeError wraps a token-exchange or refresh failure with the parsed OAuth2 error (if any), the HTTP status, and the underlying transport error. Detection uses errors.Is against the sentinels above; extraction uses errors.As(err, &ex *auth.ExchangeError).
func (*ExchangeError) Unwrap ¶
func (e *ExchangeError) Unwrap() []error
Unwrap walks the wrapped errors. errors.Is(err, auth.ErrTokenExchangeFailed) and errors.As(err, &oauth *auth.OAuth2Error) both work.
type OAuth2Error ¶
type OAuth2Error struct {
Code string // "invalid_client", "invalid_grant", ...
Description string // human-readable description
URI string // optional URI describing the error
}
OAuth2Error is the parsed error response from an OAuth2 token endpoint. The error response shape is defined by RFC 6749 § 5.2.
func ParseOAuth2Error ¶
func ParseOAuth2Error(body []byte) *OAuth2Error
ParseOAuth2Error decodes the RFC 6749 § 5.2 error envelope from a token-endpoint response body. Returns nil when the body does not match the envelope shape.
type Token ¶
type Token struct {
// Value is the scheme-specific credential (bearer token or Basic payload).
Value string
// Type is the Authorization scheme ("Bearer", "Basic", …). Empty means Bearer.
Type string
// ExpiresAt is the absolute expiry instant. The zero value means
// "no expiry / unknown" — TokenSource implementations that cannot
// observe an expiry MUST surface zero here, not a synthesised future
// time.
ExpiresAt time.Time
// Scope carries the space-separated scope grant from the
// authorization-server response, verbatim. The SDK does not enforce
// scope as application policy (REQ-061 / scope handling) — it
// round-trips the string so consumers can audit it.
Scope string
// Issuer is the URL of the authorization server that minted the
// token. Used for audit and disambiguation across multi-issuer
// federation. Populated by providers that have access to the
// discovery document; otherwise empty.
Issuer string
}
Token is the credential delivered to the wire. Token is opaque to transport/, which emits Authorization: <Type> <Value> (REQ-060, REQ-069).
type TokenSource ¶
TokenSource produces tokens for outgoing authenticated requests.
Implementations MUST:
- Refresh transparently when ExpiresAt is near or past (REQ-063).
- Coalesce concurrent refresh attempts (REQ-026).
- Honour ctx for cancellation and deadlines (REQ-020).
- Be safe for concurrent use by multiple goroutines (REQ-026).
The TokenSource is the only sanctioned construction path for a Token outside its owning provider package — no other SDK package may build Token values directly. transport/ consumes a TokenSource through transport.WithTokenSource and per-request through auth.WithTokenSource(ctx, ts).
func AnonymousTokenSource ¶
func AnonymousTokenSource() TokenSource
AnonymousTokenSource returns a TokenSource that yields a zero-value Token. transport/ treats a zero Token as "do not emit an Authorization header" — this is the canonical way to make an unauthenticated request against an endpoint that accepts both authenticated and anonymous traffic (capabilities, health). The default transport TokenSource is AnonymousTokenSource (REQ-060 documents anonymous as the default).
func StaticTokenSource ¶
func StaticTokenSource(t Token) TokenSource
StaticTokenSource returns a TokenSource that always yields t.
Useful for tests, for short-lived ad-hoc clients, and for consumers who manage token lifecycle externally. The returned TokenSource is stateless and safe for concurrent use.
StaticTokenSource does NOT refresh; if t.ExpiresAt is in the past, callers will see authentication failures on the wire — not a typed refresh error.
func TokenSourceFromContext ¶
func TokenSourceFromContext(ctx context.Context) (TokenSource, bool)
TokenSourceFromContext returns the per-request TokenSource attached via WithTokenSource, or (nil, false) if none is attached. transport/ is the primary caller.
Directories
¶
| Path | Synopsis |
|---|---|
|
Package basic implements HTTP Basic authentication (RFC 7617) as an auth.TokenSource for openEHR REST deployments that accept a static username and password on each request.
|
Package basic implements HTTP Basic authentication (RFC 7617) as an auth.TokenSource for openEHR REST deployments that accept a static username and password on each request. |
|
Package clientcreds implements the OAuth2 Client Credentials grant (RFC 6749 § 4.4) as an auth.TokenSource — for service-to-service callers (benchmark, seeder, MCP server backend, federator) that do not run an interactive user flow.
|
Package clientcreds implements the OAuth2 Client Credentials grant (RFC 6749 § 4.4) as an auth.TokenSource — for service-to-service callers (benchmark, seeder, MCP server backend, federator) that do not run an interactive user flow. |
|
Package jwtbearer implements the OAuth2 JWT Bearer (RFC 7523) grant as an auth.TokenSource — for systems that already hold a signed assertion and want to exchange it for an access token without an interactive flow.
|
Package jwtbearer implements the OAuth2 JWT Bearer (RFC 7523) grant as an auth.TokenSource — for systems that already hold a signed assertion and want to exchange it for an access token without an interactive flow. |
|
Package smart implements the SMART-on-openEHR auth provider: PKCE, authorization-code launch flow, token refresh, and JWKS rotation, returning a TokenSource compatible with the parent auth package.
|
Package smart implements the SMART-on-openEHR auth provider: PKCE, authorization-code launch flow, token refresh, and JWKS rotation, returning a TokenSource compatible with the parent auth package. |