auth

package
v0.6.0 Latest Latest
Warning

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

Go to latest
Published: Jun 14, 2026 License: MIT Imports: 6 Imported by: 0

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

View Source
const TokenTypeBasic = "Basic"

TokenTypeBasic is the HTTP Basic scheme; Value MUST be the base64-encoded user-pass payload per RFC 7617 (REQ-069).

View Source
const TokenTypeBearer = "Bearer"

TokenTypeBearer is the OAuth2 access-token scheme (default when Type is empty).

Variables

View Source
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

func BuildScope(compartment, resource, permission string) string

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

func JoinScopes(scopes ...string) string

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) Error

func (e *ExchangeError) Error() string

Error implements error.

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.

func (*OAuth2Error) Error

func (e *OAuth2Error) Error() string

Error implements error.

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).

func (Token) IsZero

func (t Token) IsZero() bool

IsZero reports whether t is the zero Token (no value, no type). transport/ uses this to decide whether to suppress the Authorization header on an outgoing request.

type TokenSource

type TokenSource interface {
	Token(ctx context.Context) (Token, error)
}

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.

Jump to

Keyboard shortcuts

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