Documentation
¶
Index ¶
- Constants
- func GetAuthTypeFromAPIContext(ctx context.Context) string
- func GetAuthorizationDetailsFromContext(ctx context.Context) []core.AuthorizationDetail
- func GetCustomClaimsFromContext(ctx context.Context) map[string]any
- func GetScopesFromAPIContext(ctx context.Context) []string
- func GetSubjectFromAPIContext(ctx context.Context) string
- func MountASMetadata(mux *http.ServeMux, meta *ASServerMetadata)
- func MountAuthorize(mux *http.ServeMux, cfg AuthorizeMountConfig)
- func MountDeviceFlow(mux *http.ServeMux, cfg DeviceFlowMountConfig)
- func MountProtectedResource(mux *http.ServeMux, meta *ProtectedResourceMetadata, proxyASMetadata bool, ...)
- func NewASMetadataHandler(meta *ASServerMetadata) http.Handler
- func NewProtectedResourceHandler(meta *ProtectedResourceMetadata) http.Handler
- type APIKeysHandler
- type APIMiddleware
- func (m *APIMiddleware) Optional(next http.Handler) http.Handler
- func (m *APIMiddleware) RequireAuthorizationDetails(requiredTypes ...string) func(http.Handler) http.Handler
- func (m *APIMiddleware) RequireScopes(requiredScopes ...string) func(http.Handler) http.Handler
- func (m *APIMiddleware) ValidateToken(next http.Handler) http.Handler
- type ASMetadataProxy
- type ASServerMetadata
- type AppRegistrationLookup
- type AuthHooks
- type AuthenticateClientRequest
- type AuthenticateClientResponse
- type AuthorizationCodeGrantRequest
- type AuthorizationCodeGrantResponse
- type AuthorizationCodeGranter
- type AuthorizationHandler
- func (h *AuthorizationHandler) ErrorRedirect(req *AuthorizationRequest, errCode, errDescription string) (string, error)
- func (h *AuthorizationHandler) IssueCode(ctx context.Context, req *AuthorizationRequest, subject string, ...) (string, error)
- func (h *AuthorizationHandler) ParseAndValidate(r *http.Request) (req *AuthorizationRequest, displayErr bool, errCode, errDescription string)
- func (h *AuthorizationHandler) SuccessRedirect(req *AuthorizationRequest, code string) (string, error)
- type AuthorizationRequest
- type AuthorizeMountConfig
- type AuthorizeTemplates
- type AuthorizeVerificationHandler
- type BCLDispatcher
- type CheckAuthorizationDetailsRequest
- type CheckAuthorizationDetailsResponse
- type CheckScopesRequest
- type CheckScopesResponse
- type ClientAuthenticator
- type ClientCredentialsRequest
- type ClientCredentialsResponse
- type ClientHooks
- type CreateAccessTokenRequest
- type CreateAccessTokenResponse
- type CreateIDJAGRequest
- type CreateIDJAGResponse
- type CreateLogoutTokenRequest
- type CreateLogoutTokenResponse
- type CredentialsValidator
- type CustomClaimsFunc
- type DeviceAuthorizationHandler
- type DeviceCodeGrantRequest
- type DeviceCodeGrantResponse
- type DeviceCodeGranter
- type DeviceFlowMountConfig
- type DeviceTemplates
- type DeviceVerificationHandler
- func (h *DeviceVerificationHandler) Consent(w http.ResponseWriter, r *http.Request)
- func (h *DeviceVerificationHandler) Decide(w http.ResponseWriter, r *http.Request)
- func (h *DeviceVerificationHandler) Form(w http.ResponseWriter, r *http.Request)
- func (h *DeviceVerificationHandler) Submit(w http.ResponseWriter, r *http.Request)
- type DispatchRequest
- type DispatchResponse
- type GrantError
- type Hooks
- type IDJAGIssuer
- type IDJAGIssuerConfig
- type IntrospectRequest
- type IntrospectResponse
- type IntrospectionHandler
- type IntrospectionResult
- type IntrospectionValidator
- func (v *IntrospectionValidator) Validate(token string) (*IntrospectionResult, error)
- func (v *IntrospectionValidator) ValidateForMiddleware(token string) (userID string, scopes []string, authType string, customClaims map[string]any, ...)
- func (v *IntrospectionValidator) ValidateForMiddlewareWithContext(ctx context.Context, token string) (userID string, scopes []string, authType string, customClaims map[string]any, ...)
- func (v *IntrospectionValidator) ValidateWithContext(ctx context.Context, token string) (*IntrospectionResult, error)
- type JTIStore
- type JWTIssuerConfig
- type JWTLogoutTokenIssuerConfig
- type JWTValidatorConfig
- type JwtBearerGrantRequest
- type JwtBearerGrantResponse
- type JwtBearerGranter
- type JwtBearerGranterConfig
- type LogoutTokenIssuer
- type OneAuth
- func (oa *OneAuth) APIKeysHTTPHandler(store core.APIKeyStore, getSubjectScopes core.GetSubjectScopesFunc) *APIKeysHandler
- func (oa *OneAuth) HTTPMiddleware() *APIMiddleware
- func (oa *OneAuth) IntrospectionHTTPHandler() *IntrospectionHandler
- func (oa *OneAuth) RevocationHTTPHandler() *RevocationHandler
- func (oa *OneAuth) SessionsHTTPHandler() *SessionsHandler
- func (oa *OneAuth) TokenEndpointHTTPHandler() *TokenEndpointHandler
- type OneAuthConfig
- type PasswordGrantRequest
- type PasswordGrantResponse
- type PasswordGrantResultdeprecated
- type PasswordGranter
- type PasswordGranterConfig
- type ProtectedResourceMetadata
- type RefreshGrantRequest
- type RefreshGrantResponse
- type RevocationHandler
- type RevokeRequest
- type RevokeResponse
- type SecurityHooks
- type SessionsHandler
- type TokenEndpointHandler
- type TokenExchangeRequest
- type TokenExchangeResponse
- type TokenExchanger
- type TokenExchangerConfig
- type TokenHooks
- type TokenInfo
- type TokenIntrospector
- type TokenIssuer
- type TokenRevoker
- type TokenRevokerConfig
- type TokenValidator
- type TrustedAssertionIssuer
- type ValidateTokenRequest
- type ValidateTokenResponse
Constants ¶
const ( // DefaultAuthorizationCodeExpiry is the lifetime applied to minted // authorization codes when AuthorizationHandler.Expiry is unset. DefaultAuthorizationCodeExpiry = 60 * time.Second // AuthorizationCodeGrantType is the OAuth 2.0 grant type identifier // for the authorization-code grant. The token endpoint dispatches // `grant_type=authorization_code` to handleAuthorizationCodeGrant. AuthorizationCodeGrantType = "authorization_code" )
Authorization code lifetime + entropy. RFC 6749 §4.1.2 recommends codes expire in 10 minutes or less; OAuth 2.1 narrows this further. We default to 60 seconds — generous for a same-tab redirect flow, tight enough that a stolen code is rarely usable.
const ( // DefaultDeviceAuthorizationExpiry is the RFC 8628 §3.4 recommended // 15-minute deadline for a user to complete the verification flow. DefaultDeviceAuthorizationExpiry = 15 * time.Minute // DefaultDeviceAuthorizationInterval is the RFC 8628 §3.5 default // polling interval; clients MUST wait at least this long between // token requests. DefaultDeviceAuthorizationInterval = 5 )
Spec defaults for RFC 8628. Callers tune via APIAuth fields.
const ( TokenTypeAccessToken = "urn:ietf:params:oauth:token-type:access_token" TokenTypeRefreshToken = "urn:ietf:params:oauth:token-type:refresh_token" TokenTypeIDToken = "urn:ietf:params:oauth:token-type:id_token" TokenTypeJWT = "urn:ietf:params:oauth:token-type:jwt" TokenTypeSAML2 = "urn:ietf:params:oauth:token-type:saml2" )
RFC 8693 §3 token type URIs.
const BCLEventType = "http://schemas.openid.net/event/backchannel-logout"
BCLEventType is the single event-identifier URI defined by OIDC Back-Channel Logout 1.0 §2.4. Every logout_token MUST carry an `events` claim whose only key is this URI; the value is the empty JSON object.
See: https://openid.net/specs/openid-connect-backchannel-1_0.html#LogoutToken
const ClientAssertionTypeJWTBearer = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
ClientAssertionTypeJWTBearer is the OAuth assertion-type URN that MUST appear in the `client_assertion_type` form parameter when authenticating via private_key_jwt (RSA/ECDSA-keyed) or client_secret_jwt (HMAC-keyed) per RFC 7521 §4.2. Any other value is rejected. The two methods share the same wire protocol — the assertion-validation pipeline picks the verification path from the registered KeyRecord's Algorithm.
const DeviceCodeGrantType = "urn:ietf:params:oauth:grant-type:device_code"
DeviceCodeGrantType is the OAuth grant type URI for the RFC 8628 device authorization grant. The device polls the token endpoint with this grant_type and a device_code obtained from /device/authorize.
See: https://www.rfc-editor.org/rfc/rfc8628#section-3.4
const IDJAGTypeHeader = "oauth-id-jag+jwt"
IDJAGTypeHeader is the JWS `typ` header value carried by an ID-JAG. The stage-2 redeemer asserts this exact value to distinguish an ID-JAG from a plain RFC 7523 jwt-bearer assertion before applying ID-JAG-specific hardening (single-use jti, client_id binding).
const JwtBearerGrantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
JwtBearerGrantType is the OAuth grant type URI for the JWT Bearer authorization grant defined in RFC 7523 §2.1.
A client trades a signed JWT (typically issued by an upstream IdP about a subject) for an access token. The assertion's `iss` MUST match a trusted issuer in TrustedAssertionIssuers; the JWT signature MUST verify against that issuer's public key; standard claims (aud/exp/nbf/sub) MUST validate.
Distinct from RFC 7523 §2.2 (JWT for *client* authentication via client_assertion + client_assertion_type at the token endpoint), which is tracked separately as the `private_key_jwt` / `client_secret_jwt` token endpoint auth methods.
See: https://www.rfc-editor.org/rfc/rfc7523#section-2.1
const MaxClientAssertionLifetime = 5 * time.Minute
MaxClientAssertionLifetime caps how far in the future a client assertion's `exp` may be from `iat`. RFC 7523 §3 item 4 leaves the upper bound to the AS; OIDC Core §9 hints "short-lived". 5 minutes is the conventional ceiling matching most major IdPs and bounds the JTIStore memory footprint.
const TokenExchangeGrantType = "urn:ietf:params:oauth:grant-type:token-exchange"
TokenExchangeGrantType is the OAuth grant type URI for OAuth 2.0 Token Exchange (RFC 8693 §2.1). A client presents a `subject_token` representing the party on whose behalf the request is made and the AS issues a new token (typically narrower in scope or audience).
Common use case: enterprise-managed identity chains. A federated IdP issues a JWT about an employee; the employee's MCP client trades that JWT for an MCP-scoped access token via this grant.
See: https://www.rfc-editor.org/rfc/rfc8693
const TokenTypeIDJAG = "urn:ietf:params:oauth:token-type:id-jag"
TokenTypeIDJAG is the RFC 8693 token-type URN identifying an ID-JAG. It is the value of `requested_token_type` on the stage-1 exchange request and of `issued_token_type` on the response. Exported so demos and tests reference the constant rather than string-literaling the URN.
const TokenTypeNA = "N_A"
TokenTypeNA is the RFC 8693 `token_type` returned for non-access-token output. An ID-JAG is not a bearer token — it is redeemed, not presented to a resource — so the exchange response reports `token_type: N_A`.
Variables ¶
This section is empty.
Functions ¶
func GetAuthTypeFromAPIContext ¶
GetAuthTypeFromAPIContext retrieves the auth type ("jwt" or "api_key") from context.
func GetAuthorizationDetailsFromContext ¶ added in v0.0.76
func GetAuthorizationDetailsFromContext(ctx context.Context) []core.AuthorizationDetail
GetAuthorizationDetailsFromContext retrieves the RFC 9396 authorization_details from context. Returns nil if no details are present (e.g., API key auth or token without RAR).
func GetCustomClaimsFromContext ¶
GetCustomClaimsFromContext retrieves the custom (non-standard) JWT claims from context. Returns nil if no custom claims are present.
func GetScopesFromAPIContext ¶
GetScopesFromAPIContext retrieves the granted scopes from the API middleware context.
func GetSubjectFromAPIContext ¶ added in v0.1.4
GetSubjectFromAPIContext retrieves the authenticated subject (RFC 7519 `sub` — user ID for human-driven flows, client_id for client_credentials) from the API middleware context.
func MountASMetadata ¶ added in v0.0.81
func MountASMetadata(mux *http.ServeMux, meta *ASServerMetadata)
MountASMetadata registers AS metadata at both well-known paths required by the OAuth/OIDC ecosystem:
- /.well-known/oauth-authorization-server (RFC 8414 §3, MUST)
- /.well-known/openid-configuration (OIDC Discovery 1.0 §4)
Both paths serve the same handler, so the documents are byte-identical. OAuth-only clients (which know about RFC 8414 but not OIDC Discovery) and OIDC clients (which look up openid-configuration) can both auto-discover the AS without falling back.
Callers that want only one path can register NewASMetadataHandler directly. This helper is the recommended default.
See:
- RFC 8414 §3 (https://www.rfc-editor.org/rfc/rfc8414#section-3)
- OIDC Discovery 1.0 §4
func MountAuthorize ¶ added in v0.1.30
func MountAuthorize(mux *http.ServeMux, cfg AuthorizeMountConfig)
MountAuthorize stamps the two RFC 6749 §4.1 authorize routes onto mux:
GET /authorize → AuthorizeVerificationHandler.Consent (browser form) POST /authorize → AuthorizeVerificationHandler.Decide (browser form)
Both routes share the same backing AuthorizeVerificationHandler and, when cfg.BrowserMiddleware is non-nil, are wrapped with it.
Panics if cfg.OneAuth is nil, cfg.OneAuth.AuthorizationCodeStore is nil, cfg.IssuerURL is empty, or the required function fields are nil — those are programming errors the caller cannot meaningfully recover from at request time.
Does NOT advertise authorization_endpoint, response_types_supported, or code_challenge_methods_supported in AS metadata — the caller supplies those on the ASServerMetadata struct passed to MountASMetadata.
func MountDeviceFlow ¶ added in v0.1.30
func MountDeviceFlow(mux *http.ServeMux, cfg DeviceFlowMountConfig)
MountDeviceFlow stamps the five RFC 8628 routes onto mux:
POST /device/authorize — DeviceAuthorizationHandler (machine endpoint, no middleware) GET /device — DeviceVerificationHandler.Form (browser form) POST /device — DeviceVerificationHandler.Submit (browser form) GET /device/approve — DeviceVerificationHandler.Consent (browser form) POST /device/approve — DeviceVerificationHandler.Decide (browser form)
The four /device + /device/approve routes share the same backing DeviceVerificationHandler and, when cfg.VerifierMiddleware is non-nil, are wrapped with it. /device/authorize is intentionally unwrapped — it is a device-driven JSON endpoint, not a browser form.
Panics if cfg.OneAuth is nil, cfg.OneAuth.DeviceAuthStore is nil, or cfg.VerificationURI is empty — those are programming errors the caller cannot meaningfully recover from at request time.
Does NOT advertise device_authorization_endpoint in AS metadata; the caller is responsible for that on the ASServerMetadata struct passed to MountASMetadata.
func MountProtectedResource ¶ added in v0.0.75
func MountProtectedResource(mux *http.ServeMux, meta *ProtectedResourceMetadata, proxyASMetadata bool, pathPrefix ...string)
MountProtectedResource mounts the PRM endpoint on the given mux and optionally proxies AS metadata at the RFC 8414 well-known path. This ensures that clients which only try RFC 8414 discovery (and don't fall back to OIDC discovery) can find the AS metadata via the resource server.
Spec references:
- RFC 9728 §3: PRM at /.well-known/oauth-protected-resource https://www.rfc-editor.org/rfc/rfc9728#section-3
- RFC 8414 §3: AS metadata at /.well-known/oauth-authorization-server https://www.rfc-editor.org/rfc/rfc8414#section-3
- MCP Auth (2025-11-25): clients discover AS via PRM → RFC 8414 https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
When proxyASMetadata is true, for each URL in AuthorizationServers:
- Fetches the AS's OIDC discovery document (with RFC 8414 fallback)
- Caches and serves it at /.well-known/oauth-authorization-server on the resource server
- Also serves path-based RFC 8414 (e.g., /.well-known/oauth-authorization-server/realms/foo)
This bridges OIDC-only providers (Keycloak, Auth0) that don't natively serve RFC 8414 metadata.
Usage:
MountProtectedResource(mux, meta, true) // PRM at: /.well-known/oauth-protected-resource // AS metadata at: /.well-known/oauth-authorization-server (proxied)
func NewASMetadataHandler ¶ added in v0.0.56
func NewASMetadataHandler(meta *ASServerMetadata) http.Handler
NewASMetadataHandler returns an http.Handler that serves Authorization Server metadata JSON. The handler is path-agnostic — register it at whichever well-known path you need, or use MountASMetadata to register at both required paths at once.
The handler:
- Responds to GET only (405 for other methods)
- Sets Content-Type: application/json
- Sets Cache-Control: public, max-age=<CacheMaxAge>
- Pre-serializes the response (metadata is static)
func NewProtectedResourceHandler ¶ added in v0.0.55
func NewProtectedResourceHandler(meta *ProtectedResourceMetadata) http.Handler
NewProtectedResourceHandler returns an http.Handler that serves the Protected Resource Metadata JSON at GET /.well-known/oauth-protected-resource.
The handler:
- Responds to GET only (405 for other methods)
- Sets Content-Type: application/json
- Sets Cache-Control: public, max-age=<CacheMaxAge>
- Serializes the metadata as JSON with omitempty on optional fields
Types ¶
type APIKeysHandler ¶ added in v0.1.30
type APIKeysHandler struct {
// Store backs the keys. Required.
Store core.APIKeyStore
// GetSubjectScopes returns the scope set the caller may grant to
// keys they create. Nil falls back to a permissive default of
// {read, write, profile} — suitable for first-light deployments.
GetSubjectScopes core.GetSubjectScopesFunc
}
APIKeysHandler serves the API-key management endpoints:
GET /api/keys → list the caller's keys (HandleAPIKeys)
POST /api/keys → create a key (HandleAPIKeys)
DELETE /api/keys/{id} → revoke a key (HandleRevokeAPIKey)
The caller's identity is read from the request context, populated upstream by APIMiddleware. APIKeysHandler does not authenticate the request itself.
Moving API-key auth out of apiauth/ entirely is tracked as a follow-up — it isn't OAuth. For now it lives here with a focused dependency set rather than on the soon-to-be-deleted APIAuth god struct.
func NewAPIKeysHandler ¶ added in v0.1.30
func NewAPIKeysHandler(store core.APIKeyStore, getSubjectScopes core.GetSubjectScopesFunc) *APIKeysHandler
NewAPIKeysHandler constructs an APIKeysHandler.
func (*APIKeysHandler) HandleAPIKeys ¶ added in v0.1.30
func (h *APIKeysHandler) HandleAPIKeys(w http.ResponseWriter, r *http.Request)
HandleAPIKeys routes GET → list, POST → create.
func (*APIKeysHandler) HandleRevokeAPIKey ¶ added in v0.1.30
func (h *APIKeysHandler) HandleRevokeAPIKey(w http.ResponseWriter, r *http.Request)
HandleRevokeAPIKey serves DELETE /api/keys/{keyID}. Verifies the key belongs to the caller before revoking.
type APIMiddleware ¶
type APIMiddleware struct {
// JWT validation (uses same config as APIAuth)
JWTSecretKey string
JWTIssuer string
JWTAudience string
JWTSigningAlg string
// KeyStore for multi-tenant JWT validation. When set, the middleware uses
// GetKeyByKid (for tokens with kid header) or GetKey (for client_id claim).
// When nil, falls back to JWTSecretKey (single-tenant, backwards-compatible).
KeyStore keys.KeyLookup
// API key validation (optional)
APIKeyStore core.APIKeyStore
// Token header configuration
AuthHeader string // Defaults to "Authorization"
// LegacyQueryParamBearer is the query parameter name to check for a
// bearer token when the Authorization header is missing (e.g., "token"
// for ?token=...). Empty disables the path (default).
//
// OAuth 2.1 §5.4 retired query-param bearer carry; RFC 6750 §2.3 had
// deprecated it in 2012. Query-carried tokens leak into browser
// history, access logs, Referer headers, and caches. The path is
// retained for OAuth 2.0 deployments that genuinely cannot move the
// token to the Authorization header (the WebSocket upgrade case is
// the typical one — see docs/DEMOS.md for the three alternatives).
// Operators take responsibility for the leak surface; a one-time
// warning logs at first use.
//
// Tracked under capability-gating umbrella #344.
LegacyQueryParamBearer string
// Error handling
OnAuthError func(w http.ResponseWriter, r *http.Request, err error)
// Blacklist enables immediate access token revocation. When set,
// validateJWT checks the blacklist after signature verification.
// If nil, no revocation check (stateless validation only).
Blacklist core.TokenBlacklist
// Introspection enables token validation via a remote introspection
// endpoint (RFC 7662) as an alternative to local JWT/JWKS validation.
// When set, tokens that fail local validation are sent to the
// introspection endpoint. When local validation is not configured
// (no JWTSecretKey, no KeyStore), introspection is the only validation path.
// If nil, only local validation is used.
Introspection *IntrospectionValidator
// Validator is the transport-independent token validator (Phase 2).
// When set, validateJWT delegates to it instead of using inline logic.
// When nil, a validator is lazily built from the existing fields
// (JWTSecretKey, KeyStore, Blacklist, etc.) on first use.
Validator TokenValidator
// TracerProvider opts the resource-server validation path into
// SEP-414 tracing. When set, the lazily-built validator emits
// `oneauth.signature_verify` spans, and the lookup-by-kid call
// against KeyStore inherits the same trace context. Nil keeps
// validation on the no-op fast path.
TracerProvider trace.TracerProvider
// contains filtered or unexported fields
}
APIMiddleware validates Bearer tokens (JWT or API key) and exposes the validated subject + scopes + claims on the request context for downstream handlers.
Wire one of:
- Validator (preferred) — the new gRPC-shape TokenValidator. Cleanest path; KeyStore/JWTSecretKey become opt-in fallbacks.
- KeyStore — multi-tenant JWT validation via GetKeyByKid / GetKey. A jwtValidator is lazily built on first use.
- JWTSecretKey — single-tenant fallback (HS256). Inline validation.
Optional add-ons: APIKeyStore (for "oa_..." API keys), Introspection (RFC 7662 fallback when local validation fails), Blacklist (jti-based revocation).
func (*APIMiddleware) Optional ¶
func (m *APIMiddleware) Optional(next http.Handler) http.Handler
Optional allows requests without auth but sets user info when present.
func (*APIMiddleware) RequireAuthorizationDetails ¶ added in v0.0.76
func (m *APIMiddleware) RequireAuthorizationDetails(requiredTypes ...string) func(http.Handler) http.Handler
RequireAuthorizationDetails ensures the token carries authorization_details matching all required types. For each required type there must be at least one entry with that type.
func (*APIMiddleware) RequireScopes ¶
RequireScopes ensures the authenticated user has all required scopes.
func (*APIMiddleware) ValidateToken ¶
func (m *APIMiddleware) ValidateToken(next http.Handler) http.Handler
ValidateToken validates Bearer tokens (JWT or API key) and sets user info in the request context for downstream handlers.
type ASMetadataProxy ¶ added in v0.0.75
type ASMetadataProxy struct {
// contains filtered or unexported fields
}
ASMetadataProxy fetches AS metadata from an authorization server's OIDC discovery endpoint and serves it at the RFC 8414 well-known path. This bridges the gap for OIDC-only providers (like Keycloak) that don't serve RFC 8414 metadata natively.
Background:
- RFC 8414 §3 defines AS metadata at /.well-known/oauth-authorization-server https://www.rfc-editor.org/rfc/rfc8414#section-3
- OIDC Discovery §4 defines it at /.well-known/openid-configuration https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
- RFC 9728 §3 (PRM) lists authorization_servers — clients then need to discover the AS's endpoints via RFC 8414 or OIDC https://www.rfc-editor.org/rfc/rfc9728#section-3
- MCP Auth spec (2025-11-25) requires clients to discover AS via RFC 8414 with OIDC fallback. Some clients (VS Code) only try RFC 8414. https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization
The proxy:
- Fetches lazily on first request (not at construction time)
- Caches the response with a configurable TTL (default 1 hour)
- Tries RFC 8414 first, then OIDC discovery (same fallback as client.DiscoverAS)
- Serves GET only (405 for other methods)
func NewASMetadataProxy ¶ added in v0.0.75
func NewASMetadataProxy(issuerURL string, cacheTTL time.Duration) *ASMetadataProxy
NewASMetadataProxy creates a proxy that fetches AS metadata from the given issuer URL. The proxy tries both RFC 8414 and OIDC discovery paths.
func (*ASMetadataProxy) ServeHTTP ¶ added in v0.0.75
func (p *ASMetadataProxy) ServeHTTP(w http.ResponseWriter, r *http.Request)
type ASServerMetadata ¶ added in v0.0.56
type ASServerMetadata struct {
// Required
Issuer string `json:"issuer"`
TokenEndpoint string `json:"token_endpoint"`
// Recommended
JWKSURI string `json:"jwks_uri,omitempty"`
// Optional endpoints
AuthorizationEndpoint string `json:"authorization_endpoint,omitempty"`
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
RevocationEndpoint string `json:"revocation_endpoint,omitempty"`
RegistrationEndpoint string `json:"registration_endpoint,omitempty"`
UserinfoEndpoint string `json:"userinfo_endpoint,omitempty"`
DeviceAuthorizationEndpoint string `json:"device_authorization_endpoint,omitempty"` // RFC 8628 §4
// Supported features
AuthorizationDetailsTypesSupported []string `json:"authorization_details_types_supported,omitempty"` // RFC 9396
ScopesSupported []string `json:"scopes_supported,omitempty"`
// ClaimsSupported lists the claim names this AS may include in
// issued tokens (and, once a userinfo endpoint exists, the userinfo
// response). OIDC Discovery 1.0 §3 declares this RECOMMENDED; the
// OpenID Foundation conformance suite emits a warning when the list
// is absent. Advertise only claims the AS actually emits — listing
// claims the AS does not produce misleads relying parties.
//
// See: OpenID Connect Discovery 1.0 §3
ClaimsSupported []string `json:"claims_supported,omitempty"`
ResponseTypesSupported []string `json:"response_types_supported,omitempty"`
GrantTypesSupported []string `json:"grant_types_supported,omitempty"`
TokenEndpointAuthMethods []string `json:"token_endpoint_auth_methods_supported,omitempty"`
// TokenEndpointAuthSigningAlgValuesSupported lists the JWT alg
// values the AS accepts on a private_key_jwt or client_secret_jwt
// `client_assertion`. Per OIDC Discovery 1.0 §3 / RFC 8414 §2 this
// list is REQUIRED whenever TokenEndpointAuthMethods includes
// "private_key_jwt" or "client_secret_jwt"; "none" MUST NOT appear.
// Typical values: "RS256", "ES256" (private_key_jwt); "HS256",
// "HS384", "HS512" (client_secret_jwt).
TokenEndpointAuthSigningAlgValuesSupported []string `json:"token_endpoint_auth_signing_alg_values_supported,omitempty"`
CodeChallengeMethodsSupported []string `json:"code_challenge_methods_supported,omitempty"`
SubjectTypesSupported []string `json:"subject_types_supported,omitempty"`
// AuthorizationResponseIssParameterSupported advertises RFC 9207
// support — when true, the AS includes an `iss` query parameter on
// every authorization response (both successful redirects with `code`
// and error redirects). Pointer semantics distinguish absence (omit
// from JSON) from explicit `false` (advertised as not supported).
//
// RFC 9207 §3:
// https://www.rfc-editor.org/rfc/rfc9207#section-3
//
// Setting this true on an AS that does NOT actually emit `iss` in
// authorization responses is a spec violation — clients keying off
// the advertisement will fail to validate.
AuthorizationResponseIssParameterSupported *bool `json:"authorization_response_iss_parameter_supported,omitempty"`
// BackchannelLogoutSupported advertises OIDC Back-Channel Logout 1.0 §3.1
// — when true, the AS sends a signed logout_token to a client's registered
// backchannel_logout_uri at session-revoke time. Pointer semantics: nil
// omits the field; an explicit `false` advertises non-support to clients.
// Set this true only when a BCLDispatcher is actually wired; otherwise
// clients that key off the advertisement will silently miss logouts.
BackchannelLogoutSupported *bool `json:"backchannel_logout_supported,omitempty"`
// BackchannelLogoutSessionSupported advertises OIDC BCL 1.0 §3.1 — when
// true, logout_tokens issued by this AS include a `sid` claim that the
// receiver can use to revoke a single OIDC session rather than every
// session for the subject. Pointer semantics match
// BackchannelLogoutSupported.
BackchannelLogoutSessionSupported *bool `json:"backchannel_logout_session_supported,omitempty"`
// CacheMaxAge controls the Cache-Control max-age in seconds.
// Defaults to 3600 (1 hour). Not serialized to JSON.
CacheMaxAge int `json:"-"`
}
ASServerMetadata describes an OAuth 2.0 Authorization Server per RFC 8414 and OpenID Connect Discovery 1.0 §4. RFC 8414 §3 mandates this metadata be served at /.well-known/oauth-authorization-server; OIDC Discovery places the same document at /.well-known/openid-configuration. Use MountASMetadata to register both paths in one call.
This is metadata-only — serving this does NOT make the server a full OIDC provider. It simply advertises what endpoints exist (token, JWKS, introspection, etc.) so standard client libraries can discover them.
type AppRegistrationLookup ¶ added in v0.1.20
type AppRegistrationLookup interface {
// GetAppRegistration returns the persisted registration for clientID, or
// (nil, false) if no such client exists or its registration is being
// torn down. Returning a clone is recommended so callers cannot mutate
// store state through the returned pointer.
GetAppRegistration(ctx context.Context, clientID string) (*core.AppRegistration, bool)
}
AppRegistrationLookup is the narrow read-only slice of admin.AppRegistrar that BCLDispatcher needs. Defined here (rather than importing admin/) to avoid a circular dependency — admin/ already imports apiauth/ transitively via the hooks layer in some configurations.
Implementations: *admin.AppRegistrar satisfies this via RLockApps; tests can inject an in-memory map.
type AuthHooks ¶ added in v0.0.77
type AuthHooks struct {
// OnLoginSuccess fires after a user successfully authenticates
// (password grant, auth code exchange).
OnLoginSuccess func(userID string)
// OnLoginFailure fires after a failed authentication attempt.
OnLoginFailure func(username string, err error)
// OnScopeStepUp fires when additional scopes are requested and granted.
// from is the original scope set, to is the expanded set.
OnScopeStepUp func(subject string, from, to []string)
}
AuthHooks fires on authentication events.
type AuthenticateClientRequest ¶ added in v0.0.83
type AuthenticateClientRequest struct {
// ClientID is the OAuth client identifier. For private_key_jwt this
// may be empty; the authenticator extracts it from the assertion's
// `iss` / `sub` claims.
ClientID string
// ClientSecret is the shared secret for client_secret_basic /
// client_secret_post. Empty when authenticating via assertion.
ClientSecret string
// ClientAssertionType is the OAuth assertion type URN. For
// private_key_jwt this MUST be
// "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"
// (RFC 7521 §4.2). Empty when authenticating via secret.
ClientAssertionType string
// ClientAssertion is the signed JWT bearing the client's identity
// (RFC 7523 §2.2 / OIDC Core §9). Validated against the client's
// registered public key. Empty when authenticating via secret.
ClientAssertion string
// Audiences is the set of URLs the AS will accept as the assertion's
// `aud` claim — typically the token endpoint URL AND the AS issuer
// URL. Per OIDC Core §9 the audience SHOULD be the token endpoint
// URL, but real-world clients (Auth0, Keycloak, Authlete) send one
// or the other; accept both for interop. The assertion's `aud`
// MUST match at least one entry. Required when ClientAssertion is
// set.
Audiences []string
}
AuthenticateClientRequest is the input to ClientAuthenticator.AuthenticateClient.
Callers populate either the secret pair (ClientID + ClientSecret) or the assertion pair (ClientAssertionType + ClientAssertion). When both are populated the implementation prefers the assertion (it is the stronger credential per RFC 6749 §2.3.1).
type AuthenticateClientResponse ¶ added in v0.0.83
type AuthenticateClientResponse struct {
// ClientID is the authenticated client identifier. Equals the
// request ClientID for the secret path; extracted from the
// assertion `iss` / `sub` for the assertion path.
ClientID string
// Method names the auth method that succeeded — one of
// "client_secret_basic", "client_secret_post", "private_key_jwt".
// Transport bindings populate Method on the way in (basic vs post)
// when they know which channel carried the secret; the assertion
// path always sets it to "private_key_jwt".
Method string
}
AuthenticateClientResponse reports the authenticated client and the auth method that succeeded. The method is informational (telemetry, logging) — handlers that only need success/failure can ignore it.
type AuthorizationCodeGrantRequest ¶ added in v0.1.30
type AuthorizationCodeGrantRequest struct {
Code string
CodeVerifier string
RedirectURI string
ClientID string
// Client authentication credentials (only one channel populated
// per RFC 7521 / OIDC Core §9):
ClientSecret string
ClientAssertionType string
ClientAssertion string
// AcceptedAudiences is the set of URLs the assertion's `aud` claim
// MUST match (used for private_key_jwt / client_secret_jwt).
// Empty falls back to the request URL.
AcceptedAudiences []string
}
AuthorizationCodeGrantRequest holds the inputs the token endpoint receives for grant_type=authorization_code.
type AuthorizationCodeGrantResponse ¶ added in v0.1.30
AuthorizationCodeGrantResponse wraps the issued token pair.
type AuthorizationCodeGranter ¶ added in v0.1.30
type AuthorizationCodeGranter interface {
// AuthorizationCodeGrant trades an authorization code for an
// access token (and optionally a refresh token).
AuthorizationCodeGrant(ctx context.Context, req *AuthorizationCodeGrantRequest) (*AuthorizationCodeGrantResponse, error)
}
AuthorizationCodeGranter redeems an RFC 6749 §4.1 authorization code at the token endpoint. Separate from TokenIssuer so the authorization-code dependencies (AuthorizationCodeStore + AppStore + ClientAuthenticator + PKCE) don't leak into callers that only need the base TokenIssuer.
The implementation looks up the stored binding, re-verifies every promise the AS made at /authorize time (client_id, redirect_uri, S256 PKCE), consumes the code (single-use per RFC 6749 §4.1.2), authenticates confidential clients via the wired ClientAuthenticator, and delegates the actual token mint to TokenIssuer.CreateAccessToken.
func NewAuthorizationCodeGranter ¶ added in v0.1.30
func NewAuthorizationCodeGranter(store core.AuthorizationCodeStore, appStore core.AppRegistrationStore, authenticator ClientAuthenticator, issuer TokenIssuer, refreshStore core.RefreshTokenStore) AuthorizationCodeGranter
NewAuthorizationCodeGranter constructs an AuthorizationCodeGranter with the supplied dependencies. Store and Issuer are required; AppStore + Authenticator enable confidential-client enforcement; RefreshStore enables refresh-token issuance on redemption.
type AuthorizationHandler ¶ added in v0.1.30
type AuthorizationHandler struct {
// Store persists minted codes. Required.
Store core.AuthorizationCodeStore
// AppStore validates the (client_id, redirect_uri) tuple. When nil
// the handler accepts any (client_id, redirect_uri) pair — suitable
// for in-process tests, NOT for production. RedirectURIValidator
// overrides this lookup when set.
AppStore core.AppRegistrationStore
// RedirectURIValidator, when non-nil, owns the (client_id,
// redirect_uri) allowlist check. Overrides AppStore-based
// validation. Use for deployments whose redirect_uri registry
// lives outside AppStore. Return nil to accept; return an error to
// reject (the error message goes to the user — keep it generic).
RedirectURIValidator func(ctx context.Context, clientID, redirectURI string) error
// IssuerURL is the AS's issuer identifier. When EmitIssParameter is
// true the handler appends `iss=<IssuerURL>` to the redirect per
// RFC 9207 §2. Required when EmitIssParameter is true; otherwise
// optional.
IssuerURL string
// EmitIssParameter toggles RFC 9207 `iss` emission on the redirect.
// Pair with ASServerMetadata.AuthorizationResponseIssParameterSupported
// so the AS's advertisement matches the wire behavior.
EmitIssParameter bool
// RedirectOverride, when non-nil, is called with the redirect's
// query values just before they are URL-encoded and the redirect
// is sent. Lets conformance scenarios mutate / strip / corrupt
// individual values (e.g. drop iss to test client behavior when
// the AS advertises but does not emit). Production deployments
// leave this nil.
RedirectOverride func(values url.Values)
// Expiry overrides DefaultAuthorizationCodeExpiry.
Expiry time.Duration
// AllowPlainPKCE permits `code_challenge_method=plain` per
// RFC 7636 §4.4. OAuth 2.1 §7.5 retired plain; leaving this false
// (default) keeps OneAuth's /authorize strict-2.1 — only S256 is
// accepted, advertised, and verified at redemption. Operators
// setting this true take on responsibility for the leak surface
// the §7.5 retirement was meant to close: a plain `code_challenge`
// IS the verifier, so any leaked authorization request equals a
// leaked verifier. Set on OneAuthConfig.AllowPlainPKCE so the AS
// metadata derivation and the redemption path can read the same
// value.
//
// Per capability-gating umbrella #344.
AllowPlainPKCE bool
// contains filtered or unexported fields
}
AuthorizationHandler validates RFC 6749 §4.1 authorization requests and emits the §4.1.2 redirect response. The handler is request-only — it does NOT render a consent screen. The verification handler (AuthorizeVerificationHandler) drives the human-side surface and calls AuthorizationHandler.IssueCode on Approve.
Most production deployments mount this via MountAuthorize, which wires the verification handler in front so the flow is end-to-end. Tests that only need the wire-shape (e.g. RFC 9207 iss emission) can call IssueCode directly.
func (*AuthorizationHandler) ErrorRedirect ¶ added in v0.1.30
func (h *AuthorizationHandler) ErrorRedirect(req *AuthorizationRequest, errCode, errDescription string) (string, error)
ErrorRedirect composes the RFC 6749 §4.1.2.1 error redirect URL:
<redirect_uri>?error=<code>[&error_description=...][&state=<state>][&iss=<iss>]
Used by the verification handler to report user-deny and post-validation failures.
func (*AuthorizationHandler) IssueCode ¶ added in v0.1.30
func (h *AuthorizationHandler) IssueCode(ctx context.Context, req *AuthorizationRequest, subject string, grantedScopes []string) (string, error)
IssueCode mints + persists a fresh authorization code bound to the supplied (request, subject) tuple. The caller (the consent verification handler) calls this on Approve. Returns the code on success.
func (*AuthorizationHandler) ParseAndValidate ¶ added in v0.1.30
func (h *AuthorizationHandler) ParseAndValidate(r *http.Request) (req *AuthorizationRequest, displayErr bool, errCode, errDescription string)
ParseAndValidate parses an HTTP request's query / form into an AuthorizationRequest, applies the RFC 6749 §4.1.1 syntax checks, and validates the (client_id, redirect_uri) tuple against the configured AppStore / RedirectURIValidator.
Two kinds of error are distinguished:
"MUST 400" errors (`client_id` / `redirect_uri` syntactically missing or unregistered) — these are returned with displayErr=true and MUST NOT be redirected back to the client per RFC 6749 §4.1.2.1 ("the authorization server SHOULD inform the resource owner of the error… do NOT redirect"). The caller renders an HTML error page.
"MAY redirect" errors (everything else: invalid response_type, missing PKCE, etc.) — these are returned with displayErr=false and an OAuth error code that the caller propagates to the redirect_uri per RFC 6749 §4.1.2.1.
On success returns the parsed request and (nil, "", false).
func (*AuthorizationHandler) SuccessRedirect ¶ added in v0.1.30
func (h *AuthorizationHandler) SuccessRedirect(req *AuthorizationRequest, code string) (string, error)
SuccessRedirect composes the RFC 6749 §4.1.2 success redirect URL:
<redirect_uri>?code=<code>[&state=<state>][&iss=<iss>]
Honors EmitIssParameter (RFC 9207) and RedirectOverride (conformance hook).
type AuthorizationRequest ¶ added in v0.1.30
type AuthorizationRequest struct {
ClientID string
RedirectURI string
ResponseType string
Scope string
State string
CodeChallenge string
CodeChallengeMethod string
}
AuthorizationRequest is the parsed RFC 6749 §4.1.1 authorization request shape. The handler validates it once on GET (to render consent) and again on POST (to defend against tampered hidden inputs), so the type is exported for the verification handler to share.
func (*AuthorizationRequest) Scopes ¶ added in v0.1.30
func (r *AuthorizationRequest) Scopes() []string
Scopes returns the request's scope claim split on the RFC 6749 space-delimited form.
type AuthorizeMountConfig ¶ added in v0.1.30
type AuthorizeMountConfig struct {
// OneAuth is the source of AuthorizationCodeStore (required for
// the authorize handler and the redemption path) and AppStore
// (used by the consent screen to render client_name and by the
// handler to validate redirect_uri allowlists). Required.
OneAuth *OneAuth
// IssuerURL is the AS's issuer identifier. Echoed on the redirect
// as `iss` per RFC 9207 when EmitIssParameter is true. Required.
IssuerURL string
// EmitIssParameter toggles RFC 9207 `iss` emission on the
// authorization-response redirect. Pair with
// ASServerMetadata.AuthorizationResponseIssParameterSupported so
// the AS's advertisement matches the wire behavior.
EmitIssParameter bool
// RedirectURIValidator, when non-nil, overrides AppStore-based
// (client_id, redirect_uri) validation. See
// AuthorizationHandler.RedirectURIValidator for details.
RedirectURIValidator func(ctx context.Context, clientID, redirectURI string) error
// RedirectOverride, when non-nil, lets conformance scenarios
// mutate / strip / corrupt the redirect query values before
// they are encoded. Production deployments leave this nil. See
// AuthorizationHandler.RedirectOverride.
RedirectOverride func(values url.Values)
// Expiry overrides DefaultAuthorizationCodeExpiry. Zero keeps the
// default.
Expiry time.Duration
// SubjectFromRequest returns the authenticated subject ("" if the
// user is unauthenticated). Wire `httpauth.Middleware.GetLoggedInSubject`
// or a cookie-reading equivalent. Required.
SubjectFromRequest func(r *http.Request) string
// CSRFTokenFromRequest returns the per-request CSRF token to
// embed in the rendered HTML form. Wire `httpauth.CSRFToken`.
// Required.
CSRFTokenFromRequest func(r *http.Request) string
// LoginRedirectURL is where Consent / Decide redirect
// unauthenticated users. Empty defaults to "/auth/login".
LoginRedirectURL string
// LoginNextParam names the query parameter used to round-trip the
// post-login return URL. Empty defaults to "next".
LoginNextParam string
// Templates overrides the built-in HTML pages. Nil uses the
// package defaults — fine for first-light deployments and tests.
Templates *AuthorizeTemplates
// AutoApproveSubject short-circuits the consent screen for
// conformance fixtures. Empty disables auto-approve. NEVER set
// in production deployments.
AutoApproveSubject string
// BrowserMiddleware, when non-nil, wraps both /authorize routes.
// Production wires `httpauth.CSRFMiddleware.Protect` here so POST
// submissions are CSRF-protected; tests pass nil because they
// drive the routes directly. The GET path is wrapped too so the
// CSRF cookie is set on first render.
BrowserMiddleware func(http.Handler) http.Handler
}
AuthorizeMountConfig configures MountAuthorize. Mirrors the MountDeviceFlow pattern: the helper owns route placement and handler construction; callers supply dependencies + the host-specific integration points (session lookup, CSRF token source, optional middleware wrapping the browser routes).
A minimal valid configuration sets APIAuth (with AuthorizationCodeStore populated), IssuerURL, SubjectFromRequest, and CSRFTokenFromRequest. Everything else has sensible defaults — the helper deliberately keeps the knob count low so the reference server and tests both reach for it instead of hand-wiring the two routes.
type AuthorizeTemplates ¶ added in v0.1.30
type AuthorizeTemplates struct {
// Consent renders GET /authorize. Data: authorizeConsentData.
Consent *template.Template
// Error renders the §4.1.2.1 "MUST NOT redirect" error page (bad
// client_id / redirect_uri). Data: authorizeErrorData.
Error *template.Template
}
AuthorizeTemplates lets callers override the built-in HTML pages with their own branded versions. Each field is a parsed `*template.Template` whose Execute call receives the data shape described on the corresponding default constant below.
type AuthorizeVerificationHandler ¶ added in v0.1.30
type AuthorizeVerificationHandler struct {
// Authorization is the parse + validate + IssueCode + redirect
// engine. Required.
Authorization *AuthorizationHandler
// AppStore resolves the client_id to its registered metadata so
// the consent screen can render the human-readable client_name.
// Nil renders the raw client_id; never a hard failure. When
// AuthorizationHandler.AppStore is set this is typically the same
// store.
AppStore core.AppRegistrationStore
// SubjectFromRequest returns the authenticated subject ("" if the
// user is unauthenticated). Wire `httpauth.Middleware.GetLoggedInSubject`.
// Required.
SubjectFromRequest func(r *http.Request) string
// CSRFTokenFromRequest returns the per-request CSRF token to
// embed in the rendered form. Wire `httpauth.CSRFToken`. Required.
CSRFTokenFromRequest func(r *http.Request) string
// LoginRedirectURL is the URL Consent / Decide redirects
// unauthenticated users to. The handler appends `?next=<this URL>`
// so the user returns to the consent screen after logging in.
// Empty defaults to `/auth/login`.
LoginRedirectURL string
// LoginNextParam is the query parameter name used to round-trip
// the post-login return URL. Empty defaults to `next`.
LoginNextParam string
// Templates overrides the built-in HTML pages. Set any subset of
// fields; nil fields fall back to the package defaults.
Templates *AuthorizeTemplates
// AutoApproveSubject, when non-empty, short-circuits the consent
// screen and approves any well-formed request with this subject.
// Intended for conformance fixtures and in-process tests — never
// set this in production deployments. Pairs with
// testutil.WithAuthorizeAutoApproveSubject.
AutoApproveSubject string
}
AuthorizeVerificationHandler serves the RFC 6749 §4.1 user-facing consent flow — the HTML page that bridges between the client's /authorize redirect and the AS minting an authorization code. Routes:
GET /authorize → Consent — validates request, shows scope + client name, Approve/Deny buttons POST /authorize → Decide — applies the decision, redirects back to the client
Validation runs on BOTH methods because a malicious user could tamper with the hidden inputs round-tripped through the form. The handler keeps apiauth's no-httpauth-import invariant by taking session + CSRF lookups as plain functions, mirroring DeviceVerificationHandler.
func (*AuthorizeVerificationHandler) Consent ¶ added in v0.1.30
func (h *AuthorizeVerificationHandler) Consent(w http.ResponseWriter, r *http.Request)
Consent serves GET /authorize. Validates the request, decides whether to bounce the user to login, and renders the consent screen.
On §4.1.2.1 "display, do not redirect" errors (missing or unregistered client_id / redirect_uri) renders an HTML error page. On other validation failures redirects to redirect_uri with the RFC 6749 §4.1.2.1 error code.
func (*AuthorizeVerificationHandler) Decide ¶ added in v0.1.30
func (h *AuthorizeVerificationHandler) Decide(w http.ResponseWriter, r *http.Request)
Decide serves POST /authorize — the user clicked Approve or Deny. Re-validates the request (defense against tampered hidden inputs), then either redirects with the code (Approve) or with `error=access_denied` (Deny). CSRF protection is the caller's responsibility — wrap this route with `httpauth.CSRFMiddleware.Protect`.
type BCLDispatcher ¶ added in v0.1.20
type BCLDispatcher struct {
// Issuer mints the signed logout_token. Required.
Issuer LogoutTokenIssuer
// Apps resolves a client_id to its registration so we can read
// backchannel_logout_uri and backchannel_logout_session_required.
// Required.
Apps AppRegistrationLookup
// RefreshStore enumerates a subject's active grants. Required for
// subject-scoped Dispatch calls; ignored for explicit-client calls.
RefreshStore core.RefreshTokenStore
// HTTPClient is used for outbound POSTs. Defaults to an http.Client with
// a 5-second per-request timeout so a stuck receiver cannot pin a
// goroutine indefinitely.
HTTPClient *http.Client
// Logger receives non-fatal dispatch errors (4xx/5xx from receiver,
// network failures, signing errors). If nil, log.Default() is used.
Logger *log.Logger
// SyncForTest makes Dispatch block until every POST completes. Tests use
// this to assert the receiver was hit before they read its captured
// state; production callers should leave it false so revocation latency
// is not coupled to receiver responsiveness.
SyncForTest bool
// AllowPrivateHosts opts in to dialing receiver URIs whose resolved IP
// is loopback / RFC1918 / link-local / unspecified / multicast. Off by
// default — the default HTTPClient's dialer rejects such connections so
// a (mis-configured or malicious) `backchannel_logout_uri` cannot be
// used to make the AS POST to internal services on its behalf, even if
// the hostname's DNS answer at dial time differs from registration time
// (rebinding). Closed-network deployments (AS + RS in the same VPC) can
// flip this on; admin.AppRegistrar.AllowPrivateBCLHosts is the matching
// registration-side knob.
AllowPrivateHosts bool
// contains filtered or unexported fields
}
BCLDispatcher pushes OIDC Back-Channel Logout 1.0 notifications to every client that registered a backchannel_logout_uri and has an active refresh-token grant for the affected subject.
The dispatcher is intentionally narrow: callers tell it a session ended (Dispatch), it looks up the clients to notify, mints one logout_token per client, and POSTs each token to the registered URI. Dispatch is fire-and- forget by default — a slow or broken receiver MUST NOT stall the revocation path. Set SyncForTest=true to wait for all POSTs before returning (used by e2e tests; not recommended for production wiring).
Retry / backoff: the spec is silent (§3.2 ¶3); this dispatcher does not retry on failure. Follow-ups can wrap HTTPClient with a retry transport.
func (*BCLDispatcher) Dispatch ¶ added in v0.1.20
func (d *BCLDispatcher) Dispatch(ctx context.Context, req *DispatchRequest) (*DispatchResponse, error)
Dispatch resolves the affected client set, mints a logout_token per client, and POSTs each token to its registered backchannel_logout_uri.
Returns nil immediately when no eligible client exists (no clients in the union, or none of them registered a BCL URI). Per-client errors are logged via Logger and otherwise swallowed: BCL is best-effort notification, not transactional with revocation.
func (*BCLDispatcher) Wait ¶ added in v0.1.20
func (d *BCLDispatcher) Wait()
Wait blocks until every async POST started by Dispatch has finished. Tests use it as a barrier; production code that wants synchronous behavior should set SyncForTest instead.
type CheckAuthorizationDetailsRequest ¶ added in v0.0.83
CheckAuthorizationDetailsRequest is the input to TokenValidator.CheckAuthorizationDetails.
type CheckAuthorizationDetailsResponse ¶ added in v0.0.83
type CheckAuthorizationDetailsResponse struct{}
CheckAuthorizationDetailsResponse — see CheckScopesResponse rationale.
type CheckScopesRequest ¶ added in v0.0.83
CheckScopesRequest is the input to TokenValidator.CheckScopes.
type CheckScopesResponse ¶ added in v0.0.83
type CheckScopesResponse struct{}
CheckScopesResponse is intentionally empty — the operation is a pure success/failure signal expressed via the error return. Wrapped struct preserves the convention shape and gives forward-compat headroom.
type ClientAuthenticator ¶ added in v0.0.77
type ClientAuthenticator interface {
// AuthenticateClient verifies the supplied credentials. Returns the
// authenticated client_id and the auth method used on success.
AuthenticateClient(ctx context.Context, req *AuthenticateClientRequest) (*AuthenticateClientResponse, error)
}
ClientAuthenticator verifies client credentials. Used by transport bindings to authenticate callers of protected endpoints (token, introspection, revocation, DCR).
Supported authentication methods (RFC 6749 §2.3.1, OIDC Core §9):
- client_secret_basic / client_secret_post: ClientID + ClientSecret
- private_key_jwt (RFC 7521 §3 + RFC 7523 §2.2): ClientAssertionType + ClientAssertion. The implementation routes by which fields are set.
func NewClientAuthenticator ¶ added in v0.0.77
func NewClientAuthenticator(kl keys.KeyLookup) ClientAuthenticator
NewClientAuthenticator creates a ClientAuthenticator backed by a KeyLookup, with an in-memory JTIStore for assertion replay protection. For multi-node deployments use NewClientAuthenticatorWithJTIStore to wire a distributed JTI store.
func NewClientAuthenticatorWithJTIStore ¶ added in v0.0.83
func NewClientAuthenticatorWithJTIStore(kl keys.KeyLookup, jti JTIStore) ClientAuthenticator
NewClientAuthenticatorWithJTIStore is like NewClientAuthenticator but lets callers supply a custom JTIStore (Redis-backed, etc.).
type ClientCredentialsRequest ¶ added in v0.0.83
type ClientCredentialsRequest struct {
ClientID string
ClientSecret string
Scopes []string
AuthorizationDetails []core.AuthorizationDetail
}
ClientCredentialsRequest is the input to TokenIssuer.ClientCredentials.
type ClientCredentialsResponse ¶ added in v0.0.83
ClientCredentialsResponse wraps the issued token pair. Wrapped (rather than returning *core.TokenPair directly) for symmetry across the interface and forward-compat headroom — same rationale as ClientRegistrationManager response types in 168.
type ClientHooks ¶ added in v0.0.77
type ClientHooks struct {
// OnRegistered fires after a new client is registered (DCR or proprietary).
// method is "dcr" or "register".
OnRegistered func(clientID, method string)
// OnDeleted fires after a client is deleted.
OnDeleted func(clientID string)
// OnKeyRotated fires after a client's signing key is rotated.
OnKeyRotated func(clientID string)
}
ClientHooks fires on client management events.
type CreateAccessTokenRequest ¶ added in v0.0.83
type CreateAccessTokenRequest struct {
Subject string
Scopes []string
AuthorizationDetails []core.AuthorizationDetail
// ClientID, when non-empty, is emitted as a `client_id` claim on the
// access token. Used by the jwt-bearer grant to bind an access token
// issued from an ID-JAG to the client_id the ID-JAG names, per the
// MCP EMA flow. Empty leaves the claim off.
ClientID string
}
CreateAccessTokenRequest is the input to TokenIssuer.CreateAccessToken.
type CreateAccessTokenResponse ¶ added in v0.0.83
CreateAccessTokenResponse is the output of TokenIssuer.CreateAccessToken.
type CreateIDJAGRequest ¶ added in v0.1.33
type CreateIDJAGRequest struct {
// Subject is the user identifier (RFC 7519 `sub`), taken from the
// validated stage-1 subject_token. Required.
Subject string
// Audience is the ID-JAG `aud` claim — the issuer identifier of the
// resource app's authorization server that will redeem this ID-JAG.
// Bound from the exchange request `audience` param (RFC 8693 §2.1),
// which is why the issuer does NOT pin an audience at construction.
// Required: an ID-JAG with no target AS is not redeemable.
Audience string
// ClientID is the MCP client identifier the redeeming AS knows the
// client by (`client_id` claim). Carried so the stage-2 redeemer can
// bind the issued access token to the client per the draft §5
// CIMD/DCR constraint. Optional.
ClientID string
// Scopes, when non-empty, is emitted as a space-delimited `scope`
// claim so the redeeming AS can scope the eventual access token.
Scopes []string
// Resource is the RFC 8707 resource indicator (the MCP server URL).
// Carried as a `resource` claim so the redeeming AS can bind the
// eventual access token to the right MCP server. Optional.
Resource string
// AuthorizationDetails carries RFC 9396 detail objects through the
// exchange when present. Optional.
AuthorizationDetails []core.AuthorizationDetail
}
CreateIDJAGRequest is the input to IDJAGIssuer.CreateIDJAG.
type CreateIDJAGResponse ¶ added in v0.1.33
type CreateIDJAGResponse struct {
// Token is the serialized `oauth-id-jag+jwt`. It becomes the
// `access_token` field of the RFC 8693 exchange response (that is the
// field RFC 8693 §2.2 uses to carry the issued token regardless of
// its type).
Token string
// ExpiresIn is the ID-JAG lifetime in seconds.
ExpiresIn int64
}
CreateIDJAGResponse wraps the minted ID-JAG.
type CreateLogoutTokenRequest ¶ added in v0.1.20
type CreateLogoutTokenRequest struct {
// Audience is the receiving client's client_id. Per §2.4 this becomes the
// `aud` claim and identifies which client the logout applies to. Required.
Audience string
// Subject is the user identifier (RFC 7519 `sub`) whose session ended.
// Optional iff SID is set; required otherwise.
Subject string
// SID is the session identifier (`sid`). When the receiving client
// registered with backchannel_logout_session_required=true the AS MUST
// include this; otherwise SHOULD include when known. oneauth maps SID to
// the refresh-token family ID — the closest analogue to an OIDC session
// the library tracks today.
SID string
}
CreateLogoutTokenRequest is the input to LogoutTokenIssuer.CreateLogoutToken.
At least one of Subject and SID MUST be populated. Per OIDC BCL §2.4 a logout_token without both `sub` and `sid` is invalid and MUST be rejected by the receiving client; the constructor enforces this so callers cannot silently mint a token that every conforming RS will reject.
type CreateLogoutTokenResponse ¶ added in v0.1.20
type CreateLogoutTokenResponse struct {
// Token is the serialized JWS. It is ready to POST as the value of the
// `logout_token` form field per §2.5.
Token string
}
CreateLogoutTokenResponse wraps the minted logout_token JWT.
type CredentialsValidator ¶ added in v0.1.0
type CredentialsValidator = accounts.CredentialsValidator
CredentialsValidator is re-exported from accounts as a single shared function shape so apiauth's password-grant validator field and localauth's host-supplied validator are assignable to each other without conversion.
type CustomClaimsFunc ¶ added in v0.1.8
CustomClaimsFunc is called during access-token issuance to inject additional non-standard claims. Returning a claim key that collides with a standard JWT claim (sub, iss, aud, exp, iat, type, scopes, jti, authorization_details) is ignored with a log warning — standard claims are owned by the issuer.
type DeviceAuthorizationHandler ¶ added in v0.1.23
type DeviceAuthorizationHandler struct {
// Store persists the device authorization. Required.
Store core.DeviceAuthorizationStore
// VerificationURI is the absolute URL the device displays to the
// user. RFC 8628 §3.2 makes this REQUIRED on the response. Production
// deployments point it at the AS's HTML user-code form (a follow-up
// PR ships this UI); tests can point it anywhere — the field is
// echoed verbatim.
VerificationURI string
// VerificationURICompleteTemplate, when non-empty, is appended with
// the user_code substituted into "%s" to produce the
// verification_uri_complete field (RFC 8628 §3.3.1). The convenience
// URL lets the device generate a QR code that pre-fills the form.
// Example: "https://auth.example.com/device?user_code=%s".
VerificationURICompleteTemplate string
// Expiry overrides the default 15-minute authorization window.
Expiry time.Duration
// Interval overrides the default 5-second polling interval.
Interval int
// ClientAuthenticator, when non-nil, authenticates the calling client
// before minting codes. When nil the handler accepts any client_id —
// suitable for public-client-only deployments and tests, NOT for
// production with confidential clients.
ClientAuthenticator ClientAuthenticator
}
DeviceAuthorizationHandler serves POST /device/authorize per RFC 8628 §3.1 / §3.2. The handler validates the client, mints fresh device_code + user_code, persists a pending authorization, and returns the polling parameters the device needs.
The handler authenticates the client via the same ClientAuthenticator the token endpoint uses (when set) so public clients (`token_endpoint_ auth_method=none`) and confidential clients are handled uniformly.
func (*DeviceAuthorizationHandler) ServeHTTP ¶ added in v0.1.23
func (h *DeviceAuthorizationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP implements the RFC 8628 §3.1 device authorization request endpoint. Body MUST be application/x-www-form-urlencoded. Form fields:
- client_id (REQUIRED for public clients; confidential clients authenticate via client_secret_basic, client_secret_post, or client_assertion, resolved by extractClientCredentials)
- scope (OPTIONAL)
- audience (OPTIONAL, RFC 8707 extension)
On success returns 200 with the JSON shape defined by §3.2.
type DeviceCodeGrantRequest ¶ added in v0.1.30
type DeviceCodeGrantRequest struct {
DeviceCode string
ClientID string
// Client authentication credentials (confidential clients only).
ClientSecret string
ClientAssertionType string
ClientAssertion string
AcceptedAudiences []string
}
DeviceCodeGrantRequest holds the inputs the token endpoint receives for grant_type=urn:ietf:params:oauth:grant-type:device_code.
type DeviceCodeGrantResponse ¶ added in v0.1.30
DeviceCodeGrantResponse wraps the issued token pair.
type DeviceCodeGranter ¶ added in v0.1.30
type DeviceCodeGranter interface {
// DeviceCodeGrant trades a device_code (after the user has
// approved on the verification URI) for an access token.
DeviceCodeGrant(ctx context.Context, req *DeviceCodeGrantRequest) (*DeviceCodeGrantResponse, error)
}
DeviceCodeGranter redeems an RFC 8628 device_code at the token endpoint. Separate from TokenIssuer so the device-flow dependencies (DeviceAuthorizationStore, polling-clock bookkeeping) don't leak into callers that only need the base TokenIssuer.
The implementation maps the stored DeviceAuthorization's status + polling clock to the RFC 8628 §3.5 error taxonomy (authorization_pending / slow_down / access_denied / expired_token), enforces confidential-client authentication via AppStore + ClientAuthenticator when configured (issue 266), and delegates the token mint to TokenIssuer.CreateAccessToken on Approved status.
func NewDeviceCodeGranter ¶ added in v0.1.30
func NewDeviceCodeGranter(store core.DeviceAuthorizationStore, appStore core.AppRegistrationStore, authenticator ClientAuthenticator, issuer TokenIssuer, refreshStore core.RefreshTokenStore) DeviceCodeGranter
NewDeviceCodeGranter constructs a DeviceCodeGranter. Store and Issuer are required; AppStore + Authenticator enable confidential- client enforcement; RefreshStore enables refresh-token issuance on redemption.
type DeviceFlowMountConfig ¶ added in v0.1.30
type DeviceFlowMountConfig struct {
// OneAuth is the source of DeviceAuthStore (required for the
// device-grant handler and the verification UI) and AppStore
// (used by the consent screen to render client_name and by the
// device-code redemption handler to enforce RFC 8628 §3.4
// confidential-client authentication, issue 266). The Approve /
// Deny helpers wire directly to the DeviceAuthStore.
OneAuth *OneAuth
// VerificationURI is the absolute URL the device displays to the
// user — RFC 8628 §3.2 makes it REQUIRED on the /device/authorize
// response. Production: <public-base-url>/device. Tests: the
// httptest.Server URL + /device. The value is echoed verbatim to
// the device. Required.
VerificationURI string
// VerificationURICompleteTemplate, when set, generates the optional
// RFC 8628 §3.3.1 verification_uri_complete field. The user_code is
// substituted into "%s". Example: "https://auth.example/device?user_code=%s".
// Empty omits the field; the device falls back to verification_uri
// and prompts the user to type the code.
VerificationURICompleteTemplate string
// Expiry overrides the RFC 8628 §3.4 recommended 15-minute window.
// Zero keeps the default.
Expiry time.Duration
// Interval overrides the RFC 8628 §3.5 default 5-second polling
// interval. Zero keeps the default.
Interval int
// ClientAuthenticator authenticates clients at POST /device/authorize.
// Nil accepts any client_id (test-suitable). For deployments that
// register confidential clients, wire APIAuth's ClientAuthenticator
// here so the device-authorize call rejects unauthenticated
// confidential clients before a code is even minted. Confidential-
// client enforcement on the REDEMPTION path (POST /api/token) is
// independent and driven by APIAuth.AppStore (issue 266).
ClientAuthenticator ClientAuthenticator
// SubjectFromRequest returns the authenticated subject ("" if the
// user is unauthenticated). The verification UI uses this to decide
// whether to redirect to login before showing the consent screen.
// Wire `httpauth.Middleware.GetLoggedInSubject` or a cookie-reading
// equivalent. Required.
SubjectFromRequest func(r *http.Request) string
// CSRFTokenFromRequest returns the per-request CSRF token to embed
// in the rendered HTML forms (code-entry and consent). Wire
// `httpauth.CSRFToken`. Required — the consent forms are rendered
// with a hidden csrf_token input even when CSRF middleware is off
// at the route level.
CSRFTokenFromRequest func(r *http.Request) string
// LoginRedirectURL is where Submit redirects unauthenticated users.
// Empty defaults to "/auth/login" (the convention used elsewhere
// in the library).
LoginRedirectURL string
// LoginNextParam names the query parameter used to round-trip the
// post-login return URL. Empty defaults to "next".
LoginNextParam string
// Templates overrides the built-in HTML pages (form / consent /
// done). Nil uses the package defaults — fine for first-light
// deployments and tests.
Templates *DeviceTemplates
// VerifierMiddleware, when non-nil, wraps each of the four
// /device and /device/approve routes. Production wires
// `httpauth.CSRFMiddleware.Protect` here so POST submissions are
// CSRF-protected; tests pass nil because they drive the routes
// directly and skip CSRF validation. The /device/authorize route
// is NOT wrapped — it is a machine-to-machine endpoint called by
// the device, not a browser form, and CSRF does not apply.
VerifierMiddleware func(http.Handler) http.Handler
}
DeviceFlowMountConfig configures MountDeviceFlow. Mirrors the MountASMetadata pattern: the helper owns route placement and handler construction; callers supply dependencies + the host-specific integration points (session lookup, CSRF token source, optional middleware wrapping the verifier routes).
A minimal valid configuration sets OneAuth (with DeviceAuthStore populated), VerificationURI, SubjectFromRequest, and CSRFTokenFromRequest. Everything else has sensible defaults — the helper deliberately keeps the knob count low so the reference server and tests both reach for it instead of hand-wiring the four routes.
type DeviceTemplates ¶ added in v0.1.26
type DeviceTemplates struct {
// Form renders the GET /device code entry page. Data: deviceFormData.
Form *template.Template
// Consent renders the GET /device/approve consent screen. Data:
// deviceConsentData.
Consent *template.Template
// Done renders the post-decision "return to your device" page.
// Data: deviceDoneData.
Done *template.Template
}
DeviceTemplates lets callers override any of the three built-in HTML pages with their own branded versions. Each field is a parsed `*template.Template` whose Execute call receives the data shape described on the corresponding default constant below.
type DeviceVerificationHandler ¶ added in v0.1.26
type DeviceVerificationHandler struct {
// Store enumerates pending device authorizations by user_code.
// Required.
Store core.DeviceAuthorizationStore
// AppStore resolves the client_id bound to a device authorization
// to its registered metadata so the consent screen can render the
// human-readable client_name and scope list. Nil renders the raw
// client_id; never a hard failure.
AppStore core.AppRegistrationStore
// Approve transitions a pending authorization to approved and binds
// the subject. Wire `APIAuth.ApproveDeviceAuthorization` here.
Approve func(r *http.Request, userCode, subject string, scopes []string) error
// Deny transitions a pending authorization to denied. Wire
// `APIAuth.DenyDeviceAuthorization` here.
Deny func(r *http.Request, userCode string) error
// SubjectFromRequest returns the authenticated subject ("" if the
// user is unauthenticated). Wire `httpauth.Middleware.GetLoggedInSubject`.
SubjectFromRequest func(r *http.Request) string
// CSRFTokenFromRequest returns the per-request CSRF token to embed
// in the rendered form. Wire `httpauth.CSRFToken`.
CSRFTokenFromRequest func(r *http.Request) string
// LoginRedirectURL is the URL Submit redirects unauthenticated users
// to. The handler appends a `?next=<consent_url>` query so the user
// returns to the consent screen after logging in. Empty defaults to
// `/auth/login`, the convention used elsewhere in the library.
LoginRedirectURL string
// LoginNextParam is the query parameter name used to round-trip the
// post-login return URL. Empty defaults to `next`.
LoginNextParam string
// Templates overrides the built-in HTML templates. Set any subset of
// fields; nil fields fall back to the package defaults.
Templates *DeviceTemplates
}
DeviceVerificationHandler serves the RFC 8628 §3.3 user-facing verification flow — the four HTML pages that bridge between the device polling at /api/token and the user authorizing on a separate device. Routes:
GET /device → Form — code entry form POST /device → Submit — verifies user_code, redirects to login or consent GET /device/approve → Consent — shows client_name + scopes, approve/deny buttons POST /device/approve → Decide — calls Approve/Deny + renders "return to device" page
The handler keeps apiauth's no-httpauth-import invariant by taking session + CSRF lookups as plain functions. Callers wire `httpauth.Middleware.GetLoggedInSubject` and `httpauth.CSRFToken` into the corresponding fields and mount each route behind the same middleware that protects their other login pages.
func (*DeviceVerificationHandler) Consent ¶ added in v0.1.26
func (h *DeviceVerificationHandler) Consent(w http.ResponseWriter, r *http.Request)
Consent serves GET /device/approve — the consent screen. The user MUST be authenticated; mount behind `httpauth.Middleware.EnsureUser` (or equivalent) so an unauthenticated user is bounced to login.
func (*DeviceVerificationHandler) Decide ¶ added in v0.1.26
func (h *DeviceVerificationHandler) Decide(w http.ResponseWriter, r *http.Request)
Decide serves POST /device/approve — the user clicked Approve or Deny. Wrap with `httpauth.CSRFMiddleware.Protect` AND `httpauth.Middleware.EnsureUser`; the handler trusts both have run.
func (*DeviceVerificationHandler) Form ¶ added in v0.1.26
func (h *DeviceVerificationHandler) Form(w http.ResponseWriter, r *http.Request)
Form serves GET /device — the code entry page.
func (*DeviceVerificationHandler) Submit ¶ added in v0.1.26
func (h *DeviceVerificationHandler) Submit(w http.ResponseWriter, r *http.Request)
Submit serves POST /device — verifies the submitted user_code and either redirects the user to the login flow (if unauthenticated) or to the consent screen.
CSRF protection is the caller's responsibility — wrap this route with `httpauth.CSRFMiddleware.Protect` so the token is enforced before this method runs.
type DispatchRequest ¶ added in v0.1.20
type DispatchRequest struct {
// Subject is the user identifier whose session ended.
Subject string
// SID is the OIDC session ID. oneauth maps this to the refresh-token
// family ID; pass empty when no family-scoped revoke happened.
SID string
// ClientIDs names clients to notify directly. Useful when the trigger is
// a single-token revoke and the AS already knows the client_id from the
// token claims.
ClientIDs []string
}
DispatchRequest is the input to BCLDispatcher.Dispatch.
At least one of Subject and ClientIDs MUST be set:
- Subject only: enumerate every client with an active grant for that subject via RefreshStore.GetSubjectTokens.
- ClientIDs only: notify exactly those clients; used for single-token revoke where the affected client is known from the token itself.
- Both: union (notify the explicit clients plus any others holding active grants for the subject).
type DispatchResponse ¶ added in v0.1.20
type DispatchResponse struct{}
DispatchResponse is empty — dispatch errors are logged, not returned, so a failing client cannot block the revocation path. The struct is preserved for forward-compat headroom (e.g., to expose per-client outcomes for telemetry without changing the method signature).
type GrantError ¶ added in v0.1.30
GrantError carries the RFC 6749 §5.2 error code + description + the HTTP status the token endpoint should return. The new grant impls (AuthorizationCodeGranter / DeviceCodeGranter / JwtBearerGranter / TokenExchanger) return this typed error so the HTTP shim in TokenEndpointHandler can `errors.As` and map to the proper application/json + status response without the impls themselves owning HTTP concerns.
Code is one of the canonical RFC 6749 §5.2 / RFC 8628 §3.5 strings (`invalid_request`, `invalid_grant`, `unsupported_grant_type`, `invalid_client`, `slow_down`, `authorization_pending`, `access_denied`, `expired_token`, `server_error`).
func (*GrantError) Error ¶ added in v0.1.30
func (e *GrantError) Error() string
Error implements the error interface. The message is intentionally short — RFC 6749 §5.2 reports the code + description in the JSON body; this string is for logs.
type Hooks ¶ added in v0.0.77
type Hooks struct {
Token TokenHooks
Auth AuthHooks
Client ClientHooks
Security SecurityHooks
}
Hooks provides lifecycle callbacks for OneAuth operations. Grouped by concern — each implementation receives only its relevant group. All callbacks are optional — nil callbacks are no-ops.
Configure all hooks in one place on OneAuth.Hooks. Callers set only what they need:
oa := NewOneAuth(OneAuthConfig{
Hooks: Hooks{
Token: TokenHooks{
OnRevoked: func(token, hint string) { audit.Log("revoked", token) },
},
},
})
type IDJAGIssuer ¶ added in v0.1.33
type IDJAGIssuer interface {
// CreateIDJAG mints a signed `oauth-id-jag+jwt` for the validated
// subject, bound to the target AS (`aud`) supplied by the caller.
CreateIDJAG(ctx context.Context, req *CreateIDJAGRequest) (*CreateIDJAGResponse, error)
}
IDJAGIssuer mints signed ID-JAG assertions. It follows the gRPC-shape convention (175): a single method taking a request struct.
It is a peer interface, wired only when a deployment opts the token-exchange grant into ID-JAG issuance (TokenExchangerConfig.IDJAGIssuer or OneAuthConfig.IDJAGIssuer). Minting an ID-JAG mints a cross-domain authorization grant that a downstream AS will trust, so issuance is off by default per the capability-gating convention (#344).
func NewJWTIDJAGIssuer ¶ added in v0.1.33
func NewJWTIDJAGIssuer(cfg IDJAGIssuerConfig) IDJAGIssuer
NewJWTIDJAGIssuer returns an IDJAGIssuer that signs ID-JAGs with the supplied IdP key. Wire it into TokenExchangerConfig.IDJAGIssuer (or OneAuthConfig.IDJAGIssuer) to opt the token-exchange grant into ID-JAG issuance.
type IDJAGIssuerConfig ¶ added in v0.1.33
type IDJAGIssuerConfig struct {
// SigningKey is the IdP signing key. For HS256 pass []byte; for
// RS256/ES256 pass the private key.
SigningKey any
// SigningAlg names the JWS algorithm (e.g. "RS256"). Empty falls back
// to HS256 when SigningKey is a []byte.
SigningAlg string
// Issuer is the IdP issuer URL — the ID-JAG `iss` claim, and the key
// under which the redeeming AS looks up the IdP JWKS.
Issuer string
// TTL bounds the ID-JAG lifetime. An ID-JAG is single-use and redeemed
// immediately, so this stays short — long enough for two network hops
// plus clock skew, short enough that a leaked (unredeemed) ID-JAG is
// useless quickly. Defaults to 2 minutes when unset.
TTL time.Duration
}
IDJAGIssuerConfig configures the default IDJAGIssuer. The signing material is the IdP's key — an ID-JAG is signed by the IdP (stage 1), not by the resource AS that redeems it (stage 2).
type IntrospectRequest ¶ added in v0.0.83
type IntrospectRequest struct {
Token string
}
IntrospectRequest is the input to TokenIntrospector.Introspect.
type IntrospectResponse ¶ added in v0.0.83
type IntrospectResponse struct {
Result *IntrospectionResult
}
IntrospectResponse wraps the RFC 7662 introspection result.
type IntrospectionHandler ¶ added in v0.0.56
type IntrospectionHandler struct {
// Introspector performs the actual token introspection (transport-independent).
Introspector TokenIntrospector
// Authenticator verifies the caller's client credentials.
Authenticator ClientAuthenticator
// AcceptedAudiences are the URLs the AS will accept as the
// `aud` claim of a private_key_jwt / client_secret_jwt client
// assertion (OIDC Core §9). Typically the introspection endpoint
// URL plus the AS issuer URL. When empty the URL of the request
// is used as a fallback, which works for single-host deployments
// but breaks behind proxies that rewrite the path — populate
// explicitly in production.
AcceptedAudiences []string
// TracerProvider opts the introspection endpoint into SEP-414
// tracing. When set, ServeHTTP extracts an inbound `traceparent`
// header and emits an `oneauth.introspect` span carrying the
// resulting `token_active` boolean. Nil keeps tracing on the
// no-op fast path.
TracerProvider trace.TracerProvider
}
IntrospectionHandler implements OAuth 2.0 Token Introspection (RFC 7662). Resource servers POST tokens to this endpoint to check validity, as an alternative to local JWT validation via JWKS.
The handler is a thin HTTP wrapper over TokenIntrospector (core logic) and ClientAuthenticator (caller verification).
See: https://www.rfc-editor.org/rfc/rfc7662
func (*IntrospectionHandler) ServeHTTP ¶ added in v0.0.56
func (h *IntrospectionHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP handles POST /oauth/introspect per RFC 7662.
type IntrospectionResult ¶ added in v0.0.62
type IntrospectionResult struct {
Active bool `json:"active"`
Sub string `json:"sub,omitempty"`
Scope string `json:"scope,omitempty"`
ClientID string `json:"client_id,omitempty"`
TokenType string `json:"token_type,omitempty"`
Exp int64 `json:"exp,omitempty"`
Iat int64 `json:"iat,omitempty"`
Iss string `json:"iss,omitempty"`
Jti string `json:"jti,omitempty"`
Aud any `json:"aud,omitempty"`
}
IntrospectionResult holds the parsed introspection response.
type IntrospectionValidator ¶ added in v0.0.62
type IntrospectionValidator struct {
// IntrospectionURL is the auth server's introspection endpoint.
// Required.
IntrospectionURL string
// ClientID and ClientSecret authenticate this resource server to the
// introspection endpoint via HTTP Basic auth (client_secret_basic).
// Required.
ClientID string
ClientSecret string
// HTTPClient is used for introspection requests. If nil, uses
// http.DefaultClient.
HTTPClient *http.Client
// CacheTTL enables response caching. If > 0, introspection responses
// are cached for this duration. A revoked token may remain "active"
// in the cache for up to CacheTTL after revocation.
// Default: 0 (no caching).
CacheTTL time.Duration
// TracerProvider opts the introspection client into SEP-414
// tracing. When set, ValidateWithContext emits an
// `oneauth.introspection_client.request` span around the outbound
// HTTP call AND injects a W3C `traceparent` header so the
// upstream introspection endpoint can stitch its `oneauth.introspect`
// span into the same trace. Nil keeps the path on the no-op fast
// path with no allocation cost.
TracerProvider trace.TracerProvider
// contains filtered or unexported fields
}
IntrospectionValidator validates tokens by calling a remote introspection endpoint (RFC 7662), as an alternative to local JWT validation via JWKS.
Use this when:
- The resource server can't access the KeyStore or JWKS endpoint
- Centralized blacklist checking is needed
- Opaque (non-JWT) tokens need validation
The validator authenticates to the introspection endpoint using client credentials (client_secret_basic).
Optional response caching reduces load on the auth server. Cache entries expire after CacheTTL (default: no cache).
See: https://www.rfc-editor.org/rfc/rfc7662
func (*IntrospectionValidator) Validate ¶ added in v0.0.62
func (v *IntrospectionValidator) Validate(token string) (*IntrospectionResult, error)
Validate is the context-free convenience form of ValidateWithContext. SEP-414 trace propagation requires a context — callers that have one (any path serving an HTTP request) should prefer ValidateWithContext so the outbound introspection call inherits the inbound trace.
func (*IntrospectionValidator) ValidateForMiddleware ¶ added in v0.0.62
func (v *IntrospectionValidator) ValidateForMiddleware(token string) (userID string, scopes []string, authType string, customClaims map[string]any, err error)
ValidateForMiddleware is the context-free convenience form of ValidateForMiddlewareWithContext. Prefer the context-bearing variant from any HTTP-serving path so SEP-414 trace propagation works.
func (*IntrospectionValidator) ValidateForMiddlewareWithContext ¶ added in v0.1.14
func (v *IntrospectionValidator) ValidateForMiddlewareWithContext(ctx context.Context, token string) (userID string, scopes []string, authType string, customClaims map[string]any, err error)
ValidateForMiddlewareWithContext validates a token via the introspection endpoint and returns the fields APIMiddleware.validateRequest needs: userID, scopes, authType, customClaims. Returns an error if the token is inactive or introspection fails. ctx is forwarded into the outbound HTTP call so trace context propagates to the upstream introspection server (see ValidateWithContext).
func (*IntrospectionValidator) ValidateWithContext ¶ added in v0.1.14
func (v *IntrospectionValidator) ValidateWithContext(ctx context.Context, token string) (*IntrospectionResult, error)
ValidateWithContext calls the introspection endpoint to check if a token is active. Returns the introspection result with parsed claims, or an error if the introspection request itself failed (network error, auth failure, etc.).
An inactive token is NOT an error — it returns IntrospectionResult{Active: false}. Only transport/auth failures return errors.
When TracerProvider is set, the call emits an `oneauth.introspection_client.request` span and injects a W3C `traceparent` on the outbound HTTP request so the upstream /oauth/introspect endpoint can stitch its own span into the trace.
type JTIStore ¶ added in v0.0.83
type JTIStore interface {
// SeenWithin reports whether `jti` was already marked seen within
// the given lifetime. When false is returned the implementation
// MUST also record `jti` as seen for at least `lifetime`. Single
// call, atomic check-and-set — callers do not invoke a separate
// Mark method.
SeenWithin(jti string, lifetime time.Duration) bool
}
JTIStore tracks JWT IDs (the `jti` claim) of recently-validated client assertions to prevent replay (RFC 7523 §3 item 7, OIDC Core §10.1).
The store has at-most-once semantics: SeenWithin(jti, ttl) returns true if `jti` has been seen within the lifetime, false otherwise. The caller atomically marks `jti` as seen on the false branch.
Implementations must be goroutine-safe.
func NewInMemoryJTIStore ¶ added in v0.0.83
func NewInMemoryJTIStore() JTIStore
NewInMemoryJTIStore returns an in-memory JTIStore backed by a map with lazy eviction. Safe default for single-process deployments.
type JWTIssuerConfig ¶ added in v0.0.77
type JWTIssuerConfig struct {
SigningKey any
SigningAlg string
Issuer string
Audience string
// AudienceFunc, when non-nil, is consulted on each access-token
// mint. A non-empty return takes precedence over Audience. See
// OneAuthConfig.AudienceFunc for the canonical use case.
AudienceFunc func() string
AccessExpiry time.Duration
ClientKeyLookup keys.KeyLookup // for client_credentials authentication
RefreshStore core.RefreshTokenStore // for refresh_token grant
ValidateCredentials CredentialsValidator // for password grant
GetSubjectScopes core.GetSubjectScopesFunc // for password grant (optional)
CustomClaims CustomClaimsFunc // optional per-token custom-claim injection
Hooks TokenHooks
}
JWTIssuerConfig configures a jwtIssuer.
type JWTLogoutTokenIssuerConfig ¶ added in v0.1.20
type JWTLogoutTokenIssuerConfig struct {
// SigningKey is the AS signing key. For HS256 pass []byte; for RS256/ES256
// pass the private key.
SigningKey any
// SigningAlg names the JWS algorithm (e.g. "RS256"). Empty falls back to
// HS256 when SigningKey is a []byte.
SigningAlg string
// Issuer is the AS issuer URL — the value the receiver expects as the
// `iss` claim and the key under which it looked up the AS JWKS.
Issuer string
// TokenTTL bounds the validity window for replay protection. The OIDC BCL
// spec does not require `exp` but receivers are encouraged to reject
// stale tokens; we default to 2 minutes — long enough for slow networks,
// short enough that a leaked token isn't useful for long.
TokenTTL time.Duration
}
JWTLogoutTokenIssuerConfig configures a jwtLogoutTokenIssuer.
type JWTValidatorConfig ¶ added in v0.0.77
type JWTValidatorConfig struct {
KeyLookup keys.KeyLookup
// SigningKey is an optional fallback used when KeyLookup is nil or the
// token's kid / client_id does not resolve through it. For HS256, supply
// []byte; for RS256/ES256, supply the verification key (*rsa.PublicKey,
// *ecdsa.PublicKey) directly.
SigningKey any
SigningAlg string
Blacklist core.TokenBlacklist
Issuer string
Audience string
// AudienceFunc, when non-nil, is consulted on every ValidateToken
// call. A non-empty return takes precedence over Audience. See
// OneAuthConfig.AudienceFunc for the canonical use case.
AudienceFunc func() string
Hooks SecurityHooks
// TracerProvider opts the signature-verify hot path into SEP-414
// tracing. When set, ValidateToken emits an `oneauth.signature_verify`
// span (attributes: `jwt.alg`, `jwt.kid`). Nil keeps the path on the
// no-op fast path. Typically populated from APIAuth.TracerProvider
// so spans nest under `oneauth.token.issue` (issuer) or under the
// caller's span (resource server).
TracerProvider trace.TracerProvider
}
JWTValidatorConfig configures a jwtValidator.
type JwtBearerGrantRequest ¶ added in v0.1.30
type JwtBearerGrantRequest struct {
Assertion string
Scopes []string
AuthorizationDetails []core.AuthorizationDetail
// Client-authentication material. RFC 7523 §3 makes client auth
// OPTIONAL for this grant (the assertion is itself the credential),
// so these may be empty for public clients. When present and the
// client is a registered confidential client, the granter
// authenticates it; on the ID-JAG redemption path the authenticated
// client_id is additionally bound to the ID-JAG's client_id claim.
ClientID string
ClientSecret string
ClientAssertionType string
ClientAssertion string
// AcceptedAudiences bounds the `aud` a private_key_jwt /
// client_secret_jwt client assertion may carry (OIDC Core §9). The
// token endpoint fills it from its configured audience list.
AcceptedAudiences []string
}
JwtBearerGrantRequest holds the inputs the token endpoint receives for grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer.
type JwtBearerGrantResponse ¶ added in v0.1.30
JwtBearerGrantResponse wraps the issued token pair.
type JwtBearerGranter ¶ added in v0.1.30
type JwtBearerGranter interface {
// JwtBearerGrant validates the assertion against the configured
// TrustedAssertionIssuers and issues an access token.
JwtBearerGrant(ctx context.Context, req *JwtBearerGrantRequest) (*JwtBearerGrantResponse, error)
}
JwtBearerGranter validates an upstream-IdP assertion and mints an access token bound to the assertion's subject (RFC 7523 §2.1). Separate from TokenIssuer so the assertion-side validation (trusted-issuer registry, key lookup, audience checks) doesn't leak into callers that only need the base TokenIssuer.
func NewJwtBearerGranter ¶ added in v0.1.30
func NewJwtBearerGranter(cfg JwtBearerGranterConfig) JwtBearerGranter
NewJwtBearerGranter constructs a JwtBearerGranter. cfg.TrustedIssuers MUST be non-empty for the grant to do anything.
type JwtBearerGranterConfig ¶ added in v0.1.33
type JwtBearerGranterConfig struct {
// TrustedIssuers lists upstream IdPs whose assertions the grant
// accepts. MUST be non-empty for the grant to do anything.
TrustedIssuers []TrustedAssertionIssuer
// DefaultAudience / DefaultIssuer supply the RFC 7523 §3 audience
// fallback when a TrustedAssertionIssuer entry doesn't pin one. For
// ID-JAG redemption these identify this AS so the ID-JAG `aud`
// (= the resource AS the client redeems at) is checked against it.
DefaultAudience string
DefaultIssuer string
// Issuer mints the access token. Required.
Issuer TokenIssuer
// ReplayStore enforces single-use on redeemed ID-JAG assertions. Nil
// defaults to an in-process in-memory store — swap in a distributed
// implementation for multi-node deployments. Plain (non-ID-JAG)
// jwt-bearer assertions are not replay-checked; the assertion is the
// renewable credential per RFC 7523.
ReplayStore JTIStore
// AppStore + Authenticator enable confidential-client authentication
// (issue 356). When wired, a request whose client_id names a
// registered confidential client is authenticated before issuance;
// public / unregistered clients keep the RFC 7523 §3 assertion-only
// path. Both nil (default) preserves the pre-356 behavior of never
// authenticating the client on this grant.
AppStore core.AppRegistrationStore
Authenticator ClientAuthenticator
}
JwtBearerGranterConfig wires the dependencies the jwt-bearer grant needs. Build via NewJwtBearerGranter.
type LogoutTokenIssuer ¶ added in v0.1.20
type LogoutTokenIssuer interface {
// CreateLogoutToken mints a signed logout_token. The returned response
// carries the serialized JWT only; the dispatcher is responsible for
// transporting it.
CreateLogoutToken(ctx context.Context, req *CreateLogoutTokenRequest) (*CreateLogoutTokenResponse, error)
}
LogoutTokenIssuer mints signed `logout_token` JWTs per OIDC Back-Channel Logout 1.0 §2.4 for delivery to a client's registered backchannel_logout_uri.
It follows the gRPC-shape convention adopted in 175 — a single method that takes a request struct and returns a wrapped response. Implementations reuse the AS signing key already in use by TokenIssuer so RSes can verify logout_tokens against the same JWKS they use for access tokens.
func NewJWTLogoutTokenIssuer ¶ added in v0.1.20
func NewJWTLogoutTokenIssuer(cfg JWTLogoutTokenIssuerConfig) LogoutTokenIssuer
NewJWTLogoutTokenIssuer returns a LogoutTokenIssuer that signs logout_tokens with the supplied key.
type OneAuth ¶ added in v0.0.77
type OneAuth struct {
// Core operation interfaces — each has minimal dependencies.
Issuer TokenIssuer
Validator TokenValidator
Introspector TokenIntrospector
Revoker TokenRevoker
Authenticator ClientAuthenticator
// Per-grant interfaces. Nil when the corresponding store / config
// is not wired — the TokenEndpointHandler returns
// unsupported_grant_type for any grant whose Granter is nil.
AuthorizationCodeGranter AuthorizationCodeGranter
DeviceCodeGranter DeviceCodeGranter
JwtBearerGranter JwtBearerGranter
TokenExchanger TokenExchanger
PasswordGranter PasswordGranter
// Shared state — available for transport bindings that need direct access.
KeyStore keys.KeyStorage
Blacklist core.TokenBlacklist
RefreshStore core.RefreshTokenStore
// Stores backing the new grant flows. Exposed on OneAuth so
// transport bindings (DeviceVerificationHandler, the consent
// screens) can write to them — the granter implementations only
// read.
AuthorizationCodeStore core.AuthorizationCodeStore
DeviceAuthStore core.DeviceAuthorizationStore
AppStore core.AppRegistrationStore
// AllowPlainPKCE mirrors OneAuthConfig.AllowPlainPKCE. Exposed so
// transport bindings (MountAuthorize, AS metadata derivation) can
// read the AS-wide policy without re-plumbing the flag through
// every config call site.
AllowPlainPKCE bool
// AcceptedAudiences is the set of URLs the token endpoint will
// accept as the `aud` claim of a private_key_jwt / client_secret_jwt
// client assertion (OIDC Core §9). Echoed into the granters so they
// can validate confidential-client assertions.
AcceptedAudiences []string
// Hooks — lifecycle callbacks grouped by concern.
Hooks Hooks
}
OneAuth is the transport-independent core of the authentication system. It composes focused interfaces (Option A — no god object) and wires hooks for lifecycle callbacks.
Use NewOneAuth to create an instance with all dependencies wired. Transport bindings (HTTP handlers, gRPC interceptors, MCP auth) are thin wrappers over these interfaces.
Library usage (no HTTP):
oa := apiauth.NewOneAuth(apiauth.OneAuthConfig{...})
token, _, _ := oa.Issuer.CreateAccessToken("alice", []string{"read"}, nil)
info, _ := oa.Validator.ValidateToken(token)
result, _ := oa.Introspector.Introspect(token)
oa.Revoker.Revoke(token, "access_token")
See: https://github.com/panyam/oneauth/issues/110
func NewOneAuth ¶ added in v0.0.77
func NewOneAuth(cfg OneAuthConfig) *OneAuth
NewOneAuth creates a fully wired OneAuth instance. All implementations receive only the interfaces they need.
func (*OneAuth) APIKeysHTTPHandler ¶ added in v0.1.30
func (oa *OneAuth) APIKeysHTTPHandler(store core.APIKeyStore, getSubjectScopes core.GetSubjectScopesFunc) *APIKeysHandler
APIKeysHTTPHandler returns an APIKeysHandler for /api/keys{,/{id}}. The supplied APIKeyStore is wired into the returned handler; OneAuthConfig does not carry it because it isn't an OAuth concern.
func (*OneAuth) HTTPMiddleware ¶ added in v0.0.78
func (oa *OneAuth) HTTPMiddleware() *APIMiddleware
HTTPMiddleware returns an APIMiddleware wired to the OneAuth TokenValidator. Use this for protecting resource endpoints.
func (*OneAuth) IntrospectionHTTPHandler ¶ added in v0.0.78
func (oa *OneAuth) IntrospectionHTTPHandler() *IntrospectionHandler
IntrospectionHTTPHandler returns an http.Handler for POST /oauth/introspect.
func (*OneAuth) RevocationHTTPHandler ¶ added in v0.0.78
func (oa *OneAuth) RevocationHTTPHandler() *RevocationHandler
RevocationHTTPHandler returns an http.Handler for POST /oauth/revoke.
func (*OneAuth) SessionsHTTPHandler ¶ added in v0.1.30
func (oa *OneAuth) SessionsHTTPHandler() *SessionsHandler
SessionsHTTPHandler returns a SessionsHandler for /api/logout, /api/logout-all, and /api/sessions. RefreshStore must be set on the OneAuth instance.
func (*OneAuth) TokenEndpointHTTPHandler ¶ added in v0.1.30
func (oa *OneAuth) TokenEndpointHTTPHandler() *TokenEndpointHandler
HTTPHandler returns an http.Handler bound to OneAuth's TokenEndpointHandler. Convenience for callers wiring oa.TokenEndpointHTTPHandler() into a mux.
type OneAuthConfig ¶ added in v0.0.77
type OneAuthConfig struct {
// Key management
KeyStore keys.KeyStorage // required — stores client keys
// Signing configuration
SigningKey any // []byte for HS256, *rsa.PrivateKey for RS256, etc.
SigningAlg string // "HS256", "RS256", "ES256" — default "HS256"
// VerifyKey is the public key for asymmetric token validation.
// When SigningKey is a private key (RS256 / ES256), VerifyKey is
// the matching public key handed to the validator. When SigningKey
// is a []byte HS256 secret, VerifyKey is ignored. Nil + asymmetric
// SigningKey causes the validator to fall back to KeyStore-based
// key lookup.
VerifyKey any
// JWT configuration
Issuer string // JWT iss claim
Audience string // JWT aud claim (optional)
AccessExpiry time.Duration // default 15 minutes
// AudienceFunc, when non-nil, is consulted per token mint and per
// validation in addition to Audience. The returned string takes
// precedence over Audience whenever it is non-empty; an empty
// return falls back to Audience.
//
// Use this when the audience cannot be known at construction
// time. The canonical case is an in-process AS that needs to mint
// tokens whose aud equals a resource server URL that is itself
// only allocated later (e.g., by `httptest.NewServer` on the RS
// side). Build the AS once, then teach the closure to look up
// the current value when the RS comes online.
//
// Production deployments where the audience is a stable
// configuration value should leave AudienceFunc nil and set
// Audience directly. Lazy resolution adds one function call per
// mint / validation — small but not free.
//
// Concurrency: AudienceFunc is invoked from the request-serving
// goroutine. Callers backing it with mutable state must guard
// the state themselves.
AudienceFunc func() string
// Token lifecycle
Blacklist core.TokenBlacklist // for access token revocation (optional)
RefreshStore core.RefreshTokenStore // for refresh token management (optional)
// Password grant callbacks (optional — only needed if password grant is used)
ValidateCredentials CredentialsValidator // validates username/password
GetSubjectScopes core.GetSubjectScopesFunc // returns allowed scopes for the subject (user ID or client_id)
// CustomClaims is called during access-token issuance to inject additional
// non-standard claims into the JWT. Standard JWT claims (sub, iss, aud,
// exp, iat, type, scopes, jti, authorization_details) cannot be overridden.
CustomClaims CustomClaimsFunc
// Authenticator overrides the default KeyStore-backed
// ClientAuthenticator. Nil keeps the default behavior
// (KeyStore-based secret + private_key_jwt assertion validation).
Authenticator ClientAuthenticator
// AcceptedAudiences is the set of URLs the token endpoint will
// accept as the `aud` claim of a private_key_jwt / client_secret_jwt
// client assertion. Typically the token endpoint URL plus the AS
// issuer URL. Empty falls back to the request URL.
AcceptedAudiences []string
// PasswordGranter, when non-nil, opts the AS into OAuth 2.0 RFC 6749
// §4.3 Resource Owner Password Credentials grant. OAuth 2.1 §7.6
// retired ROPC (clients see the user's primary credentials; defeats
// federation, blocks MFA and step-up auth); leaving this nil is
// strict-2.1 compliance — the token endpoint returns
// `unsupported_grant_type` for grant_type=password. Wire
// apiauth.NewPasswordGranter to opt in for legacy OAuth 2.0 fleets
// that must keep ROPC. Per capability-gating umbrella #344.
//
// AS metadata `grant_types_supported` must include "password" only
// when this slot is wired; the caller wires the advertised list
// when building ASServerMetadata.
PasswordGranter PasswordGranter
// Authorization code grant (RFC 6749 §4.1). When AuthorizationCodeStore
// is non-nil the token endpoint accepts grant_type=authorization_code.
AuthorizationCodeStore core.AuthorizationCodeStore
// AllowPlainPKCE permits `code_challenge_method=plain` per RFC 7636
// §4.4 on the /authorize endpoint. OAuth 2.1 §7.5 retired plain;
// leaving this false (default) keeps OneAuth strict-2.1. Set true to
// support OAuth 2.0 fleets that have legacy clients unable to
// compute SHA-256. AS metadata derivation should reflect the flag
// (`code_challenge_methods_supported` = ["S256"] when false,
// ["S256", "plain"] when true) — callers wire this when building
// the ASServerMetadata struct.
//
// Per capability-gating umbrella #344.
AllowPlainPKCE bool
// Device authorization grant (RFC 8628). When DeviceAuthStore is
// non-nil the token endpoint accepts grant_type=...:device_code.
DeviceAuthStore core.DeviceAuthorizationStore
// AppStore drives confidential-client enforcement on the device-
// code (issue 266) and authorization-code redemption paths.
AppStore core.AppRegistrationStore
// TrustedAssertionIssuers lists upstream IdPs whose JWT assertions
// the token endpoint accepts for the jwt-bearer (RFC 7523 §2.1)
// and token-exchange (RFC 8693) grants. Empty disables both.
TrustedAssertionIssuers []TrustedAssertionIssuer
// IDJAGIssuer, when non-nil, opts the token-exchange grant into
// issuing ID-JAGs (requested_token_type=...:id-jag) for the MCP
// Enterprise-Managed Authorization flow (SEP-990). Nil (default)
// rejects id-jag requests with invalid_request — minting a
// cross-domain authorization grant is a sensitive capability, off by
// default per the capability-gating convention (#344). Wire
// apiauth.NewJWTIDJAGIssuer (signed with the IdP key) to opt in. Only
// consulted when TrustedAssertionIssuers is non-empty.
IDJAGIssuer IDJAGIssuer
// IDJAGReplayStore enforces single-use on redeemed ID-JAGs (they are
// single-use per draft-ietf-oauth-identity-assertion-authz-grant).
// Nil falls back to an in-process in-memory store; supply a
// distributed implementation for multi-node deployments. Only
// consulted when TrustedAssertionIssuers is non-empty.
IDJAGReplayStore JTIStore
// TracerProvider opts the validator's signature-verify hot path
// into SEP-414 tracing. Nil keeps it on the no-op fast path.
TracerProvider trace.TracerProvider
// Hooks — lifecycle callbacks
Hooks Hooks
}
OneAuthConfig holds the dependencies for creating a OneAuth instance.
type PasswordGrantRequest ¶ added in v0.0.78
type PasswordGrantRequest struct {
Username string
Password string
Scopes []string // requested (intersected with allowed)
AuthorizationDetails []core.AuthorizationDetail // RFC 9396
ClientID string // optional — associated client
}
PasswordGrantRequest holds the inputs for a password grant.
type PasswordGrantResponse ¶ added in v0.0.83
type PasswordGrantResponse struct {
Subject string // RFC 7519 sub — user ID for password grant
AccessToken string
ExpiresIn int64
GrantedScopes []string
AuthorizationDetails []core.AuthorizationDetail
}
PasswordGrantResponse holds the output of a successful password grant. The caller uses Subject + GrantedScopes to create a refresh token if needed.
type PasswordGrantResult
deprecated
added in
v0.0.78
type PasswordGrantResult = PasswordGrantResponse
PasswordGrantResult is the previous name of PasswordGrantResponse.
Deprecated: use PasswordGrantResponse. Will be removed in a future release.
type PasswordGranter ¶ added in v0.1.32
type PasswordGranter interface {
// PasswordGrant authenticates the resource owner and returns an
// access token. Does NOT create a refresh token — the caller
// (TokenEndpointHandler) is responsible for that with transport
// metadata (device info, IP) the granter cannot see.
PasswordGrant(ctx context.Context, req *PasswordGrantRequest) (*PasswordGrantResponse, error)
}
PasswordGranter implements RFC 6749 §4.3 Resource Owner Password Credentials. Peer to JwtBearerGranter / TokenExchanger / AuthorizationCodeGranter / DeviceCodeGranter — the TokenEndpointHandler dispatches grant_type=password to this slot when it is non-nil, and returns unsupported_grant_type when it is nil. ROPC was retired in OAuth 2.1 §7.6; leaving the slot nil is strict-2.1 compliance. Wire NewPasswordGranter to opt in for OAuth 2.0 deployments that must keep ROPC for legacy clients.
Per capability-gating umbrella #344.
func NewPasswordGranter ¶ added in v0.1.32
func NewPasswordGranter(cfg PasswordGranterConfig) PasswordGranter
NewPasswordGranter returns a PasswordGranter wired with the supplied dependencies. Fires a one-time-per-process log when constructed so operators see the OAuth 2.0 escape hatch is in play — OAuth 2.1 §7.6 retired ROPC, and a deployment wiring the granter is taking deliberate responsibility for the password-handling surface 2.1 was designed to eliminate (clients see the user's primary credentials; MFA / federation are blocked).
type PasswordGranterConfig ¶ added in v0.1.32
type PasswordGranterConfig struct {
// Issuer is the access-token minting backend. Required.
Issuer TokenIssuer
// ValidateCredentials authenticates the (username, password) pair.
// Required. Returns the User on success, an error on failure;
// the granter maps "invalid credentials" to the RFC 6749 §5.2
// `invalid_grant` error.
ValidateCredentials CredentialsValidator
// GetSubjectScopes returns the scopes the authenticated user is
// permitted to grant. Optional — when nil, a built-in default
// scope set (read / write / profile / offline_access) is used.
GetSubjectScopes core.GetSubjectScopesFunc
// Hooks are the token-lifecycle callbacks fired on issuance.
// Zero value is fine.
Hooks TokenHooks
}
PasswordGranterConfig wires the dependencies the default PasswordGranter needs. Build via NewPasswordGranter.
type ProtectedResourceMetadata ¶ added in v0.0.55
type ProtectedResourceMetadata struct {
// Resource is the resource server's identifier (its base URL).
// REQUIRED per RFC 9728 §3.
Resource string `json:"resource"`
// AuthorizationServers lists the authorization servers that the resource
// server trusts to issue tokens. REQUIRED per RFC 9728 §3.
AuthorizationServers []string `json:"authorization_servers"`
// ScopesSupported lists the OAuth 2.0 scopes that this resource server
// understands. Optional.
ScopesSupported []string `json:"scopes_supported,omitempty"`
// BearerMethodsSupported lists how this resource server accepts bearer
// tokens, using the method names from RFC 6750 ("header", "body", "query").
// Optional per RFC 9728 §2; omitted from JSON when empty. OneAuth's
// middleware only accepts the Authorization request header field
// (RFC 6750 §2.1), so a OneAuth-backed resource server advertises
// ["header"] — the reference server in cmd/demo-resource-server sets this.
BearerMethodsSupported []string `json:"bearer_methods_supported,omitempty"`
// TokenFormatsSupported lists the token formats accepted (e.g., "jwt").
// Optional.
TokenFormatsSupported []string `json:"token_formats_supported,omitempty"`
// SigningAlgsSupported lists the JWS signing algorithms the resource server
// supports for validating tokens (e.g., "RS256", "ES256", "HS256").
// Optional.
SigningAlgsSupported []string `json:"resource_signing_alg_values_supported,omitempty"`
// DocumentationURI points to human-readable documentation for the resource
// server's API. Optional.
DocumentationURI string `json:"resource_documentation,omitempty"`
// IntrospectionEndpoint is the URL of the token introspection endpoint
// (RFC 7662) that can be used to validate tokens for this resource.
// Optional — included when the resource server supports introspection.
IntrospectionEndpoint string `json:"introspection_endpoint,omitempty"`
// CacheMaxAge controls the Cache-Control max-age header in seconds.
// Defaults to 3600 (1 hour) if zero. Not serialized to JSON.
CacheMaxAge int `json:"-"`
}
ProtectedResourceMetadata describes an OAuth 2.0 Protected Resource per RFC 9728. Resource servers serve this at GET /.well-known/oauth-protected-resource so clients can auto-discover which authorization servers are trusted, what scopes are supported, what token formats are accepted, and which signing algorithms are used.
Required fields: Resource and AuthorizationServers. All other fields are optional and omitted from JSON when empty.
type RefreshGrantRequest ¶ added in v0.0.83
type RefreshGrantRequest struct {
RefreshToken string
}
RefreshGrantRequest is the input to TokenIssuer.RefreshGrant.
type RefreshGrantResponse ¶ added in v0.0.83
RefreshGrantResponse wraps the rotated token pair.
type RevocationHandler ¶ added in v0.0.77
type RevocationHandler struct {
// Revoker performs the actual token revocation (transport-independent).
Revoker TokenRevoker
// Authenticator verifies the caller's client credentials.
Authenticator ClientAuthenticator
// AcceptedAudiences are the URLs the AS will accept as the
// `aud` claim of a private_key_jwt / client_secret_jwt client
// assertion (OIDC Core §9). When empty the URL of the request
// is used as a fallback.
AcceptedAudiences []string
// TracerProvider opts the revocation endpoint into SEP-414 tracing.
// When set, ServeHTTP extracts an inbound `traceparent` header and
// emits an `oneauth.revoke` span. Nil keeps the handler on the
// no-op fast path.
TracerProvider trace.TracerProvider
}
RevocationHandler implements OAuth 2.0 Token Revocation (RFC 7009). It is a thin HTTP wrapper over TokenRevoker (core logic) and ClientAuthenticator (caller verification).
See: https://www.rfc-editor.org/rfc/rfc7009
func (*RevocationHandler) ServeHTTP ¶ added in v0.0.77
func (h *RevocationHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP handles POST /oauth/revoke per RFC 7009.
type RevokeRequest ¶ added in v0.0.83
RevokeRequest is the input to TokenRevoker.Revoke.
type RevokeResponse ¶ added in v0.0.83
type RevokeResponse struct{}
RevokeResponse — empty (success/failure via error). Wrapped per convention.
type SecurityHooks ¶ added in v0.0.77
type SecurityHooks struct {
// OnTokenRejected fires when a token fails validation.
// reason describes why (expired, bad signature, revoked, etc.).
OnTokenRejected func(reason string)
// OnBlacklistHit fires when a revoked token is presented.
// This indicates token reuse after revocation — potential theft.
OnBlacklistHit func(jti string)
// OnAlgorithmMismatch fires when a token's alg header doesn't match
// the stored key's algorithm. This is the CVE-2015-9235 attack vector.
OnAlgorithmMismatch func(expected, got string)
}
SecurityHooks fires on security-relevant events. Use these for alerting, audit logging, and intrusion detection.
type SessionsHandler ¶ added in v0.1.30
type SessionsHandler struct {
// Store backs the refresh tokens. Required.
Store core.RefreshTokenStore
// Hooks fires the lifecycle callbacks. The zero value is a no-op
// dispatcher.
Hooks TokenHooks
}
SessionsHandler serves the per-user session-management endpoints:
POST /api/logout → revoke a single refresh token (HandleLogout) POST /api/logout-all → revoke every refresh token for the caller (HandleLogoutAll) GET /api/sessions → list the caller's active sessions (HandleListSessions)
The caller's identity is read from the request context, populated upstream by APIMiddleware. HandleLogout reads the refresh token from the JSON body and does not require authentication; the other two endpoints require an authenticated subject.
TokenHooks fire on revoke so the OIDC Back-Channel Logout dispatcher (and any other subscriber) can fan out notifications.
func NewSessionsHandler ¶ added in v0.1.30
func NewSessionsHandler(store core.RefreshTokenStore, hooks TokenHooks) *SessionsHandler
NewSessionsHandler constructs a SessionsHandler.
func (*SessionsHandler) HandleListSessions ¶ added in v0.1.30
func (h *SessionsHandler) HandleListSessions(w http.ResponseWriter, r *http.Request)
HandleListSessions serves GET /api/sessions — returns the caller's active sessions with device metadata.
func (*SessionsHandler) HandleLogout ¶ added in v0.1.30
func (h *SessionsHandler) HandleLogout(w http.ResponseWriter, r *http.Request)
HandleLogout serves POST /api/logout — revokes one refresh token. Does not require authentication; the token in the body is the proof. Errors are not surfaced (don't reveal whether the token existed).
func (*SessionsHandler) HandleLogoutAll ¶ added in v0.1.30
func (h *SessionsHandler) HandleLogoutAll(w http.ResponseWriter, r *http.Request)
HandleLogoutAll serves POST /api/logout-all — revokes every refresh token for the authenticated caller. The subject MUST be present in the request context (set by upstream middleware).
type TokenEndpointHandler ¶ added in v0.1.30
type TokenEndpointHandler struct {
// OneAuth provides the granters and shared state. Required.
OneAuth *OneAuth
// RateLimiter throttles password-grant attempts by client IP +
// username. Nil disables rate limiting.
RateLimiter core.RateLimiter
// TracerProvider opts the handler into SEP-414 tracing. Nil keeps
// the no-op fast path.
TracerProvider trace.TracerProvider
}
TokenEndpointHandler serves POST /api/token (RFC 6749 §3.2). It is a peer to IntrospectionHandler and RevocationHandler — a thin HTTP shim over the gRPC-shape interfaces on OneAuth (TokenIssuer for password/refresh/client_credentials, AuthorizationCodeGranter, DeviceCodeGranter, JwtBearerGranter, TokenExchanger).
HTTP-side concerns the granters do not own — form parsing, rate limiting, refresh-token creation with device info, AuthHooks firing, tracing — live here. Logic-side concerns are entirely delegated.
func NewTokenEndpointHandler ¶ added in v0.1.30
func NewTokenEndpointHandler(oa *OneAuth) *TokenEndpointHandler
NewTokenEndpointHandler constructs a handler over the supplied OneAuth instance. RateLimiter and TracerProvider are optional.
func (*TokenEndpointHandler) ServeHTTP ¶ added in v0.1.30
func (h *TokenEndpointHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
ServeHTTP routes the token-endpoint request to the granter matching the form's grant_type. Supports both application/x-www-form-urlencoded (RFC 6749 standard) and application/json request bodies.
type TokenExchangeRequest ¶ added in v0.1.30
type TokenExchangeRequest struct {
SubjectToken string
SubjectTokenType string
RequestedTokenType string
Resource string
Audience string
Scopes []string
AuthorizationDetails []core.AuthorizationDetail
// ClientID is the requesting client's identifier. For the id-jag
// output path it becomes the ID-JAG `client_id` claim — the client
// identity the downstream (resource-app) AS knows the client by.
ClientID string
// Client-authentication material. Optional; when present and ClientID
// names a registered confidential client, the granter authenticates
// it (rejecting invalid_client on failure) before issuing.
ClientSecret string
ClientAssertionType string
ClientAssertion string
// AcceptedAudiences bounds a client-assertion `aud` (OIDC Core §9);
// filled by the token endpoint from its configured audience list.
AcceptedAudiences []string
}
TokenExchangeRequest holds the inputs the token endpoint receives for grant_type=urn:ietf:params:oauth:grant-type:token-exchange.
type TokenExchangeResponse ¶ added in v0.1.30
TokenExchangeResponse wraps the issued token pair. The wire response carries the RFC 8693 issued_token_type field; the implementation populates Tokens.IssuedTokenType.
type TokenExchanger ¶ added in v0.1.30
type TokenExchanger interface {
// TokenExchange trades subject_token for a token with the
// requested type / audience / resource.
TokenExchange(ctx context.Context, req *TokenExchangeRequest) (*TokenExchangeResponse, error)
}
TokenExchanger trades one token for another per RFC 8693. Distinct from JwtBearerGranter because the audience / resource / actor semantics are token-exchange-specific.
func NewTokenExchanger ¶ added in v0.1.30
func NewTokenExchanger(cfg TokenExchangerConfig) TokenExchanger
NewTokenExchanger constructs a TokenExchanger. cfg.TrustedIssuers MUST be non-empty for the grant to do anything.
type TokenExchangerConfig ¶ added in v0.1.33
type TokenExchangerConfig struct {
// TrustedIssuers lists upstream IdPs whose subject_tokens the grant
// accepts. MUST be non-empty for the grant to do anything.
TrustedIssuers []TrustedAssertionIssuer
// DefaultAudience / DefaultIssuer supply the RFC 7523 §3 audience
// fallback used when validating the subject_token and a
// TrustedAssertionIssuer entry doesn't pin its own audience.
DefaultAudience string
DefaultIssuer string
// Issuer mints the access token for the access_token output path.
// Required.
Issuer TokenIssuer
// IDJAGIssuer, when non-nil, opts the grant into
// requested_token_type=id-jag issuance for the MCP EMA flow. Nil
// (default) rejects id-jag requests — minting a cross-domain
// authorization grant is off by default per #344.
IDJAGIssuer IDJAGIssuer
// AppStore + Authenticator enable confidential-client authentication
// (issue 356). When wired, a request whose client_id names a
// registered confidential client is authenticated before issuance;
// public / unregistered clients keep the assertion-only path. Both
// nil (default) preserves the pre-356 behavior of never
// authenticating the client on this grant.
AppStore core.AppRegistrationStore
Authenticator ClientAuthenticator
}
TokenExchangerConfig wires the dependencies the token-exchange grant needs. Build via NewTokenExchanger.
type TokenHooks ¶ added in v0.0.77
type TokenHooks struct {
// OnIssued fires after an access token is successfully created.
// subject is the token's sub claim, grantType is the OAuth grant used.
OnIssued func(subject, grantType string)
// OnRefreshed fires after a refresh token is rotated and a new access token issued.
OnRefreshed func(subject string)
// OnRevoked fires after a token is successfully revoked.
// hint is the token_type_hint ("access_token", "refresh_token", or "").
OnRevoked func(token, hint string)
// OnSubjectRevoked fires when every active grant for a subject has been
// revoked (logout-all). Subject is the affected user; sid is the OIDC
// session ID when one is associated with the revocation (empty for
// subject-wide revokes that are not session-scoped). clientIDs lists
// every client that held an active grant at revoke time — captured
// BEFORE the revoke so subscribers (notably the BCL dispatcher) can
// notify them without having to re-query a store that, post-revoke,
// returns nothing. Empty when no client identifiers were available.
OnSubjectRevoked func(subject, sid string, clientIDs []string)
// OnTokenRevoked fires when a single refresh token (or all tokens in a
// family) is revoked, after subject + client_id are known. Provides the
// affected subject, sid (family ID, when known), and clientID. Distinct
// from OnRevoked so subscribers (notably BCL) get the routing identifiers
// without having to re-parse the token. clientID is empty when the
// trigger lacks a known client (e.g., admin-side revoke by jti).
OnTokenRevoked func(subject, sid, clientID string)
}
TokenHooks fires on token lifecycle events.
type TokenInfo ¶ added in v0.0.77
type TokenInfo struct {
// Subject is the principal the token represents (RFC 7519 sub) — a
// user ID for human-driven flows or a client_id for
// client_credentials.
Subject string
// Scopes are the granted scopes from the token.
Scopes []string
// AuthorizationDetails are the RFC 9396 authorization_details from the token.
// Nil if the token has no authorization_details.
AuthorizationDetails []core.AuthorizationDetail
// CustomClaims are non-standard JWT claims (everything not in standardClaims).
CustomClaims map[string]any
// AuthType is "jwt" or "api_key".
AuthType string
}
TokenInfo holds the validated claims extracted from a token. Returned wrapped inside ValidateTokenResponse.
type TokenIntrospector ¶ added in v0.0.77
type TokenIntrospector interface {
// Introspect checks a token's validity and returns its claims.
// Returns {Active: false} on the wrapped result for any invalid token
// (never reveals why — RFC 7662 §2.2 confidentiality).
Introspect(ctx context.Context, req *IntrospectRequest) (*IntrospectResponse, error)
}
TokenIntrospector performs RFC 7662 token introspection.
func NewTokenIntrospector ¶ added in v0.0.77
func NewTokenIntrospector(v TokenValidator) TokenIntrospector
NewTokenIntrospector creates a TokenIntrospector backed by a TokenValidator.
type TokenIssuer ¶ added in v0.0.77
type TokenIssuer interface {
// CreateAccessToken mints a JWT with the given subject, scopes, and
// optional RFC 9396 authorization_details.
CreateAccessToken(ctx context.Context, req *CreateAccessTokenRequest) (*CreateAccessTokenResponse, error)
// ClientCredentials performs the full client_credentials grant:
// authenticates the client, validates scopes/details, and returns a token pair.
ClientCredentials(ctx context.Context, req *ClientCredentialsRequest) (*ClientCredentialsResponse, error)
// RefreshGrant rotates a refresh token and returns a new access + refresh
// token pair. Handles theft detection (revoked token → revoke entire family).
RefreshGrant(ctx context.Context, req *RefreshGrantRequest) (*RefreshGrantResponse, error)
}
TokenIssuer mints access tokens via the OAuth 2.0 grants that ship on by default: client_credentials, refresh_token, and the raw access-token mint. Password grant (ROPC) was retired in OAuth 2.1 §7.6 and is no longer part of the default issuer surface — wire PasswordGranter (a peer interface) to opt in for OAuth 2.0 deployments that need it. Per capability-gating umbrella #344.
func NewJWTIssuer ¶ added in v0.0.77
func NewJWTIssuer(cfg JWTIssuerConfig) TokenIssuer
NewJWTIssuer creates a TokenIssuer that signs JWTs.
type TokenRevoker ¶ added in v0.0.77
type TokenRevoker interface {
// Revoke invalidates a token. The TokenTypeHint ("access_token" or
// "refresh_token") guides which store to check first; empty hint tries both.
Revoke(ctx context.Context, req *RevokeRequest) (*RevokeResponse, error)
}
TokenRevoker invalidates tokens per RFC 7009.
func NewTokenRevoker ¶ added in v0.0.77
func NewTokenRevoker(cfg TokenRevokerConfig) TokenRevoker
NewTokenRevoker creates a TokenRevoker.
type TokenRevokerConfig ¶ added in v0.0.77
type TokenRevokerConfig struct {
Blacklist core.TokenBlacklist
RefreshStore core.RefreshTokenStore
Hooks TokenHooks
}
TokenRevokerConfig configures a tokenRevoker.
type TokenValidator ¶ added in v0.0.77
type TokenValidator interface {
// ValidateToken parses and validates a token (signature, expiry, issuer,
// audience, blacklist) and returns its claims.
ValidateToken(ctx context.Context, req *ValidateTokenRequest) (*ValidateTokenResponse, error)
// CheckScopes validates a token and verifies it carries every required scope.
CheckScopes(ctx context.Context, req *CheckScopesRequest) (*CheckScopesResponse, error)
// CheckAuthorizationDetails validates a token and verifies it carries
// authorization_details entries for every required type (RFC 9396).
CheckAuthorizationDetails(ctx context.Context, req *CheckAuthorizationDetailsRequest) (*CheckAuthorizationDetailsResponse, error)
}
TokenValidator parses tokens, verifies their signatures and standard claims, and checks scope / RFC 9396 authorization_details requirements.
func NewJWTValidator ¶ added in v0.0.77
func NewJWTValidator(cfg JWTValidatorConfig) TokenValidator
NewJWTValidator creates a TokenValidator that validates JWTs locally.
type TrustedAssertionIssuer ¶ added in v0.0.81
type TrustedAssertionIssuer struct {
// Issuer is the expected `iss` claim value (e.g.,
// "https://corp-idp.example.com"). REQUIRED.
Issuer string
// PublicKey is a static public key for signature verification.
// Either this or KeyFunc MUST be set. Suitable for tests and
// single-key issuers; for production with key rotation use KeyFunc.
PublicKey crypto.PublicKey
// KeyFunc resolves a public key from the JWT header (typically
// looking up `kid` against a cached JWKS). When set it takes
// precedence over PublicKey. The token argument is the parsed
// (but not yet signature-verified) JWT.
KeyFunc func(token *jwt.Token) (crypto.PublicKey, error)
// Audiences lists acceptable `aud` claim values for assertions
// signed by this issuer. When empty, defaults to the AS's
// JWTAudience (or its IssuerURL if no audience is configured).
// RFC 7523 §3 requires the AS to identify itself by the audience
// claim — the default makes the token endpoint URL implicit.
Audiences []string
// AcceptedAlgorithms restricts the JWT alg values accepted for
// assertions from this issuer (e.g., {"RS256", "ES256"}). Empty
// = accept any non-`none` algorithm advertised by the JWT library.
// Set this in production to lock out alg-confusion attacks.
AcceptedAlgorithms []string
}
TrustedAssertionIssuer describes an upstream IdP whose JWT assertions the AS will accept for the jwt-bearer grant (RFC 7523 §2.1) and the token-exchange grant with subject_token_type=urn:ietf:params:oauth:token-type:jwt (RFC 8693 §2.1.1).
At least one of PublicKey or KeyFunc must be set so signatures can be verified. KeyFunc takes precedence when both are set; it lets callers resolve keys from a JWKS by `kid` header.
The Issuer field is matched verbatim against the JWT's `iss` claim (case-sensitive, no trailing-slash normalization — match what the upstream IdP actually emits).
func ValidateAssertion ¶ added in v0.1.30
func ValidateAssertion( trustedIssuers []TrustedAssertionIssuer, defaultAudience, defaultIssuerURL, rawAssertion string, ) (jwt.MapClaims, *TrustedAssertionIssuer, error)
validateAssertion parses + signature-validates a JWT assertion against the configured trusted issuers, then validates the standard registered claims (aud, exp, nbf). Returns the validated claims on success.
Per RFC 7523 §3:
- `iss` MUST match a configured trusted issuer.
- Signature MUST verify using a key supplied by that issuer.
- `aud` MUST contain a value identifying the AS (its token endpoint URL, issuer URL, or configured audience).
- `exp` MUST be in the future; `nbf` (if present) in the past.
- `sub` is required and identifies the principal the assertion speaks for.
Errors returned here use OAuth 2.0 error codes by convention; callers (the grant handlers) translate them to invalid_grant / invalid_request HTTP responses.
ValidateAssertion is the public form used by the gRPC-shape grant impls (jwtBearerGranter, tokenExchanger). trustedIssuers is the configured registry; defaultAudience / defaultIssuerURL supply the fallback audience when a TrustedAssertionIssuer entry doesn't pin one. Returns the validated claims + the matched issuer entry, or an error message suitable for the OAuth invalid_grant error_description.
type ValidateTokenRequest ¶ added in v0.0.83
type ValidateTokenRequest struct {
Token string
}
ValidateTokenRequest is the input to TokenValidator.ValidateToken.
type ValidateTokenResponse ¶ added in v0.0.83
type ValidateTokenResponse struct {
Info *TokenInfo
}
ValidateTokenResponse wraps the parsed token claims.
Source Files
¶
- apikeys.go
- as_metadata.go
- as_metadata_proxy.go
- authorize.go
- authorize_grant.go
- authorize_granter.go
- authorize_mount.go
- authorize_verification_handler.go
- bcl_dispatcher.go
- client_authenticator.go
- client_credentials.go
- confidential_client_auth.go
- consent_helpers.go
- device_auth_grant.go
- device_flow_mount.go
- device_granter.go
- device_verification_handler.go
- grant_errors.go
- hooks.go
- id_jag.go
- interfaces.go
- introspection.go
- introspection_client.go
- jti_store.go
- jwt_bearer_grant.go
- jwt_bearer_granter.go
- jwt_helpers.go
- logout_token.go
- middleware.go
- oneauth.go
- password_granter.go
- protected_resource.go
- rar_claims.go
- revocation.go
- sessions_http.go
- token_endpoint.go
- token_exchange_grant.go
- token_exchange_granter.go
- token_introspector.go
- token_revoker.go
- token_validator.go