Documentation
¶
Overview ¶
ref: go-kratos/kratos middleware/auth/auth.go — auth middleware pattern Adopted: middleware wrapping pattern, Claims extraction from context. Deviated: separate TokenVerifier and Authorizer interfaces (GoCell splits authn from authz); no dependency on specific JWT library at this layer.
Package auth: Authenticator contract.
An Authenticator inspects an *http.Request and returns one of three outcomes:
(p, true, nil) — credential present and valid; caller stops the chain. (absentPrincipal(), false, nil) — credential absent; caller should try the next authenticator. (nil, false, err) — credential present but invalid; caller MUST NOT fall through.
ref: kubernetes/apiserver pkg/authentication/request/union/union.go (FailOnError=true)
Package auth defines authentication and authorization interfaces for the GoCell framework: JWT issuing/verification, key management, service tokens, and HTTP middleware. Concrete implementations live in cells/accesscore or adapters/.
Index ¶
- Constants
- Variables
- func AuthMiddleware(verifier IntentTokenVerifier, opts ...AuthOption) func(http.Handler) http.Handler
- func CompilePasswordResetExempts(entries []string) (func(method, urlPath string) bool, error)
- func GenerateServiceToken(ring *HMACKeyRing, callerCell, method, path, rawQuery string, ts time.Time) string
- func LoadKeysFromEnv() (privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey, err error)deprecated
- func LoadRSAKeyPairFromPEM(privPEM, pubPEM []byte) (*rsa.PrivateKey, *rsa.PublicKey, error)
- func Mount(mux cell.RouteHandler, r Route) error
- func MustGenerateTestKeyPair() (*rsa.PrivateKey, *rsa.PublicKey)
- func MustMount(mux cell.RouteHandler, r Route)
- func NewBootstrapMiddleware(creds BootstrapCredentials, limiter BootstrapRateLimiter, ...) func(http.Handler) http.Handler
- func RequireAnyRole(ctx context.Context, roles ...string) errordeprecated
- func RequirePolicy(p Policy) (func(http.Handler) http.Handler, error)
- func RequireRole(authorizer Authorizer, roles ...string) func(http.Handler) http.Handler
- func RequireSelfOrRole(ctx context.Context, targetID string, bypassRoles ...string) errordeprecated
- func ServiceTokenMiddleware(ring cell.HMACKeyring, clk clock.Clock, opts ...ServiceTokenOption) func(http.Handler) http.Handler
- func TestContext(subject string, roles []string) context.Context
- func TestServiceContext(callerCell string) context.Context
- func Thumbprint(pub *rsa.PublicKey) string
- func TypHeaderForIntent(intent TokenIntent) string
- func WithPrincipal(ctx context.Context, p *Principal) context.Context
- type AuthMetrics
- type AuthOption
- func WithAuthClock(clk clock.Clock) AuthOption
- func WithLogger(l *slog.Logger) AuthOption
- func WithMetrics(m *AuthMetrics) AuthOption
- func WithPasswordResetChangeEndpointHintFn(fn func() string) AuthOption
- func WithPasswordResetExemptMatcher(fn func(method, urlPath string) bool) AuthOption
- func WithPublicEndpointMatcher(fn func(*http.Request) bool) AuthOption
- type Authenticator
- type AuthenticatorFunc
- type Authorizer
- type BootstrapAuthFailObserver
- type BootstrapCredentials
- type BootstrapRateLimiter
- type Claims
- type EnvKeyProvider
- type EnvKeyProviderOption
- type HMACKeyRing
- type InMemoryNonceOption
- type InMemoryNonceStore
- type IntentTokenVerifier
- type IssueOptions
- type JWTIssuer
- type JWTIssuerOption
- type JWTVerifier
- type JWTVerifierOption
- type KeyProvider
- type KeySet
- func LoadKeySetFromEnv(clk clock.Clock) (*KeySet, error)
- func MustNewTestKeySet(clk clock.Clock) (*KeySet, *rsa.PrivateKey, *rsa.PublicKey)
- func NewKeySet(priv *rsa.PrivateKey, pub *rsa.PublicKey, clk clock.Clock, ...) (*KeySet, error)
- func NewKeySetWithVerificationKeys(priv *rsa.PrivateKey, pub *rsa.PublicKey, clk clock.Clock, ...) (*KeySet, error)
- type KeySetOption
- type NonceStore
- type NonceStoreKind
- type NoopNonceStore
- type Policy
- type Principal
- type PrincipalKind
- type Route
- type ServiceTokenOption
- type SigningKeyProvider
- type StaticKeyProvider
- type TokenIntent
- type UnionAuthenticator
- type VerificationKey
- type VerificationKeyStore
Constants ¶
const ( // EnvJWTPrivateKey is the environment variable for the PEM-encoded RSA private key. EnvJWTPrivateKey = "GOCELL_JWT_PRIVATE_KEY" // EnvJWTPublicKey is the environment variable for the PEM-encoded RSA public key. EnvJWTPublicKey = "GOCELL_JWT_PUBLIC_KEY" // EnvJWTPrevPublicKey is the environment variable for the previous (verification-only) public key. EnvJWTPrevPublicKey = "GOCELL_JWT_PREV_PUBLIC_KEY" // EnvJWTPrevKeyExpires is the environment variable for the expiry of the previous key (RFC 3339). EnvJWTPrevKeyExpires = "GOCELL_JWT_PREV_KEY_EXPIRES" )
const ( // NonceStoreKindNoop is the explicit disable-replay-check sentinel. // Rejected in adapter mode "real" by cmd/corebundle.SharedDeps.Validate. NonceStoreKindNoop = cell.NonceStoreKindNoop // NonceStoreKindInMemory is the single-process map-backed implementation. // Suitable for single-pod deployments; a shared store is required for // multi-pod replay protection. NonceStoreKindInMemory = cell.NonceStoreKindInMemory // NonceStoreKindDistributed is reserved for shared backends (Redis, consul, // etc.). Production multi-pod deployments must use this kind. NonceStoreKindDistributed = cell.NonceStoreKindDistributed )
const ( // EnvServiceSecret is the environment variable for the current HMAC secret. EnvServiceSecret = "GOCELL_SERVICE_SECRET" // EnvServiceSecretPrevious is the environment variable for the previous HMAC secret. EnvServiceSecretPrevious = "GOCELL_SERVICE_SECRET_PREVIOUS" )
const DefaultAccessTokenTTL = 15 * time.Minute
DefaultAccessTokenTTL is the default time-to-live for access tokens issued by JWTIssuer.
const MinHMACKeyBytes = cell.MinHMACKeyBytes
MinHMACKeyBytes aliases cell.MinHMACKeyBytes so runtime/auth and kernel/cell share a single canonical strength threshold (NIST SP 800-107: HMAC-SHA-256 requires ≥256-bit keys). Kept as a const for callers that reference the runtime/auth namespace; the source of truth lives in kernel/cell.
const MinRSAKeyBits = 2048
MinRSAKeyBits is the minimum RSA key size accepted by the auth package. Keys smaller than 2048 bits (e.g. 512 or 1024) are considered insecure and will be rejected during parsing and verifier/issuer construction.
const RoleAdmin = "admin"
RoleAdmin is the canonical role name for administrative privilege.
const ServiceTokenClockSkew = 30 * time.Second
ServiceTokenClockSkew is the maximum future timestamp skew accepted for service tokens. It is intentionally separate from ServiceTokenMaxAge: old tokens expire by age, while future-dated tokens are only tolerated for bounded clock drift.
const ServiceTokenMaxAge = 5 * time.Minute
ServiceTokenMaxAge is the maximum age of a service token before it is rejected. Tokens with timestamps at or beyond this window are refused.
const ServiceTokenNonceTTL = ServiceTokenMaxAge + ServiceTokenClockSkew
ServiceTokenNonceTTL is the required replay-retention window for nonce stores used with service tokens. It covers the full token validity window plus the accepted future clock skew.
const ( // TokenIntentAccess marks a short-lived credential for calling business // endpoints. Verifier rejects any access token replayed at /auth/refresh. TokenIntentAccess = cell.TokenIntentAccess )
Variables ¶
var ErrKeyMissing = errcode.ErrAuthKeyMissing
ErrKeyMissing indicates a required JWT key environment variable is not set.
var ErrNonceReused = errcode.New(errcode.KindUnauthenticated, errcode.ErrAuthNonceReused, "auth: nonce already used")
ErrNonceReused is returned by NonceStore.CheckAndMark when a nonce has already been consumed within its TTL window.
*errcode.Error implements errors.Is via pointer equality — callers use errors.Is(err, ErrNonceReused) unchanged. Callers distinguish replay (ErrNonceReused in the chain → 401) from store infrastructure failure (any other Cause → 500) via errors.Is on this exact sentinel; see writeServiceTokenError in servicetoken.go. The HTTP error envelope is constructed at the middleware layer.
var ErrNonceStoreFull = errcode.New(errcode.KindUnavailable, errcode.ErrNonceStoreFull,
"auth: nonce store is full; no expired entries to reclaim")
ErrNonceStoreFull is returned by InMemoryNonceStore.CheckAndMark when the nonce map has reached maxEntries and a forced prune found no expired entries to reclaim. Callers should treat this as a transient infrastructure failure (503 / Requeue) rather than a replay signal.
Functions ¶
func AuthMiddleware ¶
func AuthMiddleware(verifier IntentTokenVerifier, opts ...AuthOption) func(http.Handler) http.Handler
AuthMiddleware extracts a Bearer token from the Authorization header, verifies it using the provided IntentTokenVerifier (always enforcing token_use=access for business endpoints), and stores the resulting Claims in the request context. On failure, it returns a 401 JSON response.
The parameter is IntentTokenVerifier (not TokenVerifier) by design: the access-vs-refresh distinction is a hard safety invariant — any verifier plugged into business routes must be able to enforce it at the type level, so we refuse to compile call sites that pass an intent-unaware verifier.
Public-endpoint bypass is provided through AuthOption values (WithPublicEndpointMatcher). The Router installs this via a lazy closure during FinalizeAuth so that route declarations from all Cells are aggregated before the predicate is compiled.
Internal listener routes that authenticate via service-token or mTLS live on a physically separate listener+mux and never reach this middleware — no in-band bypass predicate is needed.
ref: go-kratos/kratos — public bypass via selector at composition layer ref: go-zero — JWT opt-in per route group, no hidden runtime defaults
func CompilePasswordResetExempts ¶
CompilePasswordResetExempts compiles a list of "METHOD /path" entries into a method-aware predicate used by WithPasswordResetExemptMatcher. Path segments of the form {xxx} match any single non-empty URL segment (mirroring the router's template semantics), e.g. "POST /api/v1/access/users/{id}/password".
Validation matches middleware.CompilePublicEndpoints fail-fast strength (reviewer finding — prior divergence allowed weaker exempt configs to slip through startup):
- entry must be in "METHOD /path" format, non-empty on both sides
- method must be one of GET/HEAD/POST/PUT/PATCH/DELETE/OPTIONS/CONNECT/TRACE
- path must start with '/'
- duplicate (method, template) pairs are rejected
Errors across the batch are aggregated via errors.Join so the operator sees every malformed entry in a single startup failure rather than whack-a-mole.
An empty input list returns a matcher that exempts nothing — the fail-closed default that middleware enforces before callers wire a matcher.
This helper exists so the composition root (main.go / bootstrap) owns the list of exempt endpoints — runtime/auth no longer hard-codes cell-specific paths (F6 decoupling).
func GenerateServiceToken ¶
func GenerateServiceToken(ring *HMACKeyRing, callerCell, method, path, rawQuery string, ts time.Time) string
GenerateServiceToken creates a service token for the given callerCell, method, path, optional rawQuery, and timestamp using the current secret from the key ring. The token format is "{timestamp}:{nonce}:{callerCell}:{hex_hmac}" where nonce is 16 cryptographically random bytes, hex-encoded. It returns an empty string if:
- ring is nil
- callerCell is empty (mandatory — identifies the originating cell)
- callerCell contains ':' (would corrupt the 4-part token structure)
rawQuery is canonicalized separately from path so the HMAC message remains stable across query parameter ordering. Pass "" when the request has no query parameters.
func LoadKeysFromEnv
deprecated
func LoadKeysFromEnv() (privateKey *rsa.PrivateKey, publicKey *rsa.PublicKey, err error)
Deprecated: Use LoadKeySetFromEnv instead, which returns a KeySet with kid support and optional verification-only key loading.
LoadKeysFromEnv reads PEM-encoded RSA keys from environment variables GOCELL_JWT_PRIVATE_KEY and GOCELL_JWT_PUBLIC_KEY. It returns an errcode error if either variable is missing or contains invalid PEM/key data.
func LoadRSAKeyPairFromPEM ¶
LoadRSAKeyPairFromPEM parses PEM-encoded RSA private and public keys.
func Mount ¶
func Mount(mux cell.RouteHandler, r Route) error
Mount registers the Route on mux. It:
- Validates the Route shape (Contract required, Contract.Kind=="http", bypass-flag mutual exclusivity).
- Derives the chi-relative registration path by stripping mux.Prefix() from Contract.Path (when the mux implements cell.Prefixer) so chi's own prefix composition produces the correct external URL.
- Wraps Handler with RequirePolicy(Policy) (inner) and wrapper.HTTPHandler(Contract) (outer) — the wrapper writes ctxkeys.ContractID + contributes span attrs to the outer middleware Tracing's single span, so policy denials (403) still emit gocell.contract.id.
- Registers the handler on mux via Handle("{METHOD} {relPath}").
- Forwards AuthRouteMeta to cell.AuthRouteDeclarer if mux implements it (chiRouterAdapter composes the prefix back to fully-qualified path before handing off to the top-level Router's declaration table).
Mount returns a non-nil error on invalid configurations so callers (cell.RouteGroup.Register) can propagate the failure to bootstrap phase5 without aborting the program. Composition-root call sites that want fail-fast wiring can wrap the error in a panic at the top of the call chain; cells that consult runtime config should propagate the error instead.
func MustGenerateTestKeyPair ¶
func MustGenerateTestKeyPair() (*rsa.PrivateKey, *rsa.PublicKey)
MustGenerateTestKeyPair generates a 2048-bit RSA key pair for testing and examples. It panics on error, following the Go test helper convention. Production code should use LoadKeySetFromEnv to load keys from configuration.
func MustMount ¶
func MustMount(mux cell.RouteHandler, r Route)
MustMount is the composition-root fail-fast variant of Mount. It panics when Mount returns an error. Suitable for top-level wiring where the caller has no error-return path; cells should use Mount inside their RouteGroup.Register closure and propagate the error.
func NewBootstrapMiddleware ¶
func NewBootstrapMiddleware( creds BootstrapCredentials, limiter BootstrapRateLimiter, onAuthFail BootstrapAuthFailObserver, ) func(http.Handler) http.Handler
NewBootstrapMiddleware constructs the HTTP middleware chain for bootstrap authentication. The chain is: RateLimit (per-IP) → Basic Auth header parse → constant-time username/password comparison → uniform 401 envelope on any mismatch (no field-level oracle).
onAuthFail is an optional observer invoked on every authentication failure (after the response is written). The reason string is one of: "missing_header", "wrong_credentials", "rate_limited". Callers use this hook to write audit log entries without importing cells/. Pass nil to disable.
Wire this middleware around the setup/admin handler to enforce D5 semantics: env credentials authenticate the operator; body credentials define the admin identity.
func RequireAnyRole
deprecated
RequireAnyRole checks that the authenticated subject holds at least one of the specified roles. Returns nil on success.
Deprecated: prefer constructing auth.AnyRole(...) / auth.SelfOr(...) Policy and passing it via auth.Route.Policy. The internal Require* helpers below are retained only as the implementation backbone.
Use this instead of RequireSelfOrRole for admin-only endpoints where there is no target resource owner to compare against.
Calling with zero roles always returns ErrAuthForbidden (no role can match).
Errors:
- ErrAuthUnauthorized: no Principal in context, or PrincipalUser with empty Subject
- ErrAuthForbidden: subject does not hold any of the required roles
func RequirePolicy ¶
RequirePolicy lifts a Policy into a middleware-shaped `func(http.Handler) http.Handler`. On policy failure it writes the domain error via httputil.WriteError and short-circuits the chain; on success it delegates to next.
ref: grpc-ecosystem/go-grpc-middleware auth.UnaryServerInterceptor — policy is declared at registration time, not inside the handler body. ref: go-chi/jwtauth Authenticator — short-circuit pattern with response written inside the guard.
func RequireRole ¶
RequireRole checks that the authenticated subject has at least one of the specified roles. The AuthMiddleware must run before this middleware. On failure, it returns a 403 JSON response.
func RequireSelfOrRole
deprecated
RequireSelfOrRole checks that the authenticated subject matches targetID or holds one of the specified bypass roles. Returns nil on success.
Deprecated: prefer constructing auth.AnyRole(...) / auth.SelfOr(...) Policy and passing it via auth.Route.Policy. The internal Require* helpers below are retained only as the implementation backbone.
ref: go-kratos/kratos middleware/auth/auth.go — adopted: subject-from-context pattern; deviated: combined self+role check instead of separate authz middleware.
Errors:
- ErrAuthUnauthorized: no Principal in context, or PrincipalUser with empty Subject
- ErrAuthForbidden: subject does not match targetID and lacks bypass roles
func ServiceTokenMiddleware ¶
func ServiceTokenMiddleware(ring cell.HMACKeyring, clk clock.Clock, opts ...ServiceTokenOption) func(http.Handler) http.Handler
ServiceTokenMiddleware validates requests using HMAC-SHA256 service tokens. The token is expected in the Authorization header as:
ServiceToken {unix_timestamp}:{nonce}:{hex_hmac}
The HMAC is computed over "{method} {path}[?{canonicalQuery}] {timestamp} {nonce}".
Verification tries each secret in the key ring in order (current, then previous). Tokens older than 5 minutes (exclusive boundary) are rejected.
Principal construction is fully delegated to NewServiceTokenAuthenticator so that the service identity shape is defined in a single place.
ring accepts cell.HMACKeyring (the kernel interface); *HMACKeyRing satisfies it structurally and remains the canonical production implementation.
Misconfiguration paths (nil ring, sub-strength HMAC, missing/Noop NonceStore, authenticator build failure) return an error middleware that serves 500 on every request. All misconfiguration paths share the same errorMiddlewareInternal helper so the 500 behavior is consistent and observable via the "internal" metric label.
func TestContext ¶
TestContext creates a context carrying the given subject and roles for use in handler tests across cells/. Follows the net/http/httptest naming pattern.
This helper is NOT deprecated; it is the recommended way to inject a Principal in handler tests.
For service-principal tests, use TestServiceContext.
func TestServiceContext ¶
TestServiceContext creates a context carrying a service Principal with the given callerCell for use in handler/mount tests. Follows the net/http/httptest naming pattern. Use this instead of TestContext for tests that exercise internal endpoints protected by RequireCallerCell.
See also: TestContext for user-principal tests.
func Thumbprint ¶
Thumbprint computes the RFC 7638 JSON Web Key (JWK) Thumbprint of an RSA public key using SHA-256. The result is a 43-character base64url-encoded (no padding) string derived from the SHA-256 hash of the canonical JWK representation. The same key always produces the same thumbprint.
ref: RFC 7638 §3.2 — required members for RSA in lexicographic order: "e", "kty", "n"
func TypHeaderForIntent ¶
func TypHeaderForIntent(intent TokenIntent) string
TypHeaderForIntent is the exported form of jwtTypForIntent, intended for test harnesses in sibling packages that need to build synthetic JWTs whose JOSE typ header matches what VerifyIntent expects. Production code should not call this — JWTIssuer.Issue writes the correct typ header automatically.
Types ¶
type AuthMetrics ¶
type AuthMetrics struct {
// contains filtered or unexported fields
}
AuthMetrics holds pre-registered metric instruments for auth operations.
func NewAuthMetrics ¶
func NewAuthMetrics(p metrics.Provider) (*AuthMetrics, error)
NewAuthMetrics registers auth metric instruments with the given provider.
type AuthOption ¶
type AuthOption func(*authConfig)
AuthOption configures auth middleware behavior.
func WithAuthClock ¶
func WithAuthClock(clk clock.Clock) AuthOption
WithAuthClock sets the clock for auth middleware timing (token verification latency metrics). Required; AuthMiddleware panics if not set.
func WithLogger ¶
func WithLogger(l *slog.Logger) AuthOption
WithLogger sets the logger for auth middleware.
func WithMetrics ¶
func WithMetrics(m *AuthMetrics) AuthOption
WithMetrics sets the AuthMetrics for auth middleware.
func WithPasswordResetChangeEndpointHintFn ¶
func WithPasswordResetChangeEndpointHintFn(fn func() string) AuthOption
WithPasswordResetChangeEndpointHintFn sets a getter closure that is called at request time to obtain the changePasswordEndpoint hint. Use this when the hint value is not known at middleware install time (e.g. it is derived by FinalizeAuth after RegisterRoutes completes).
When fn is nil the option is a no-op.
func WithPasswordResetExemptMatcher ¶
func WithPasswordResetExemptMatcher(fn func(method, urlPath string) bool) AuthOption
WithPasswordResetExemptMatcher installs a (method, path) → bool predicate used by the password-reset gate. When a token carries password_reset_required=true, only requests for which the matcher returns true are allowed through; everything else returns 403.
If no matcher is supplied, the gate rejects every authenticated request — fail-closed. Composition roots must opt in to exempt paths (typically the change-password and logout endpoints) so that runtime/auth stays free of cell-specific path knowledge (F6 decoupling).
func WithPublicEndpointMatcher ¶
func WithPublicEndpointMatcher(fn func(*http.Request) bool) AuthOption
WithPublicEndpointMatcher sets a compiled method-aware predicate for the auth middleware bypass check.
Router.FinalizeAuth uses this option to install a lazy closure that reads the compiled public-route matcher — aggregated from every Cell's auth.Mount(mux, Route{Public: true}) call — at request time.
ref: otelhttp WithPublicEndpointFn per-request predicate shape
type Authenticator ¶
Authenticator inspects an HTTP request and resolves the caller's identity.
func NewAnonymousAuthenticator ¶
func NewAnonymousAuthenticator() Authenticator
NewAnonymousAuthenticator returns an Authenticator that always succeeds with a fresh PrincipalAnonymous principal. It is the explicit, type-safe way to declare "this WebSocket endpoint accepts unauthenticated traffic" at the composition root — paired with UpgradeConfig.Authenticator's non-nil requirement to keep fail-closed semantics intact.
The returned Principal carries Kind=PrincipalAnonymous and zero ExpiresAt (anonymous principals never expire). Subject and Roles are intentionally empty; downstream authorization (auth.RequireSelfOrRole etc.) treats the anonymous Principal as having no privileges.
func NewContextAuthenticator ¶
func NewContextAuthenticator() Authenticator
NewContextAuthenticator returns an Authenticator that extracts a Principal from the request context. It is used by adapters that mount behind an already-authenticated listener (e.g. WebSocket upgrade routes mounted on a JWT listener); the listener middleware writes the Principal via WithPrincipal, and the adapter's Authenticator simply reads it back.
Outcomes:
(p, true, nil) — Principal found in ctx, Kind != PrincipalUnknown. (absentPrincipal(), false, nil) — no Principal in ctx (chain may continue).
This Authenticator never returns an error; callers that want a strict fail-closed mode should compose with a guard that rejects (false, nil) outcomes (the typical /api/v1/* listener already does this via JWT short-circuit).
警告:当挂载在已有 JWT listener 上时,ContextAuthenticator 应是 chain 中 唯一的 authenticator(不通过 UnionAuthenticator 组合);absent-credential 结果(false, nil)不会阻止 Union 继续尝试下一个 authenticator,可能造成 意料之外的 fall-through 路径。
func NewJWTAuthenticator ¶
func NewJWTAuthenticator(v IntentTokenVerifier) Authenticator
NewJWTAuthenticator returns an Authenticator that extracts a Bearer token from the Authorization header and verifies its "access" intent using v.
Outcomes:
(p, true, nil) — token present and valid; Principal populated from claims. (absentPrincipal(), false, nil) — no Authorization header, or non-Bearer scheme (let Union continue). (nil, false, err) — Bearer token present but VerifyIntent rejected it (short-circuit).
ref: kubernetes/apiserver pkg/authentication/request/bearertoken/bearertoken.go
func NewServiceTokenAuthenticator ¶
func NewServiceTokenAuthenticator(ring cell.HMACKeyring, clk clock.Clock, opts ...ServiceTokenOption) (Authenticator, error)
NewServiceTokenAuthenticator returns an Authenticator that validates HMAC service tokens (Authorization: ServiceToken <ts>:<nonce>:<mac>).
Returns an error when:
- ring is nil or a typed-nil interface;
- no NonceStore was supplied via WithServiceTokenNonceStore;
- a NoopNonceStore (Kind() == NonceStoreKindNoop) was supplied — replay protection is mandatory at every layer; dev/test wiring must use InMemoryNonceStore (NewInMemoryNonceStore(ServiceTokenNonceTTL)).
This aligns runtime/auth construction with the existing reject-Noop guards in kernel/cell.NewAuthServiceToken, runtime/bootstrap.auth_plan_validate, and cmd/corebundle.SharedDeps.Validate — all four layers fail-closed.
Outcomes (when construction succeeds):
(p, true, nil) — ServiceToken header present and valid. (absentPrincipal(), false, nil) — no Authorization header, or non-"ServiceToken" scheme. (nil, false, err) — ServiceToken present but validation failed (expired, bad MAC, replay).
ref: HashiCorp Vault server fail-closed defaults (no Noop-equivalent path). ref: kubernetes/apiserver pkg/authentication — typed (Authenticator, error).
type AuthenticatorFunc ¶
AuthenticatorFunc is a function that implements Authenticator.
func (AuthenticatorFunc) Authenticate ¶
Authenticate implements Authenticator.
type Authorizer ¶
type Authorizer interface {
// Authorize returns true if the subject is authorized to perform the
// given action on the resource.
Authorize(ctx context.Context, subject, resource, action string) (bool, error)
}
Authorizer checks whether a subject is allowed to perform an action on a resource. Implementations may use RBAC, ABAC, or external policy engines.
type BootstrapAuthFailObserver ¶
BootstrapAuthFailObserver is invoked after a 401 or 429 response is written. The reason string is one of:
- "missing_header" — Basic Auth header absent
- "wrong_credentials" — header present but credentials do not match
- "rate_limited" — per-IP token bucket exhausted (429)
Wiring an observer is how callers route bootstrap auth failures to audit logs without importing cells/ from runtime/auth.
type BootstrapCredentials ¶
BootstrapCredentials carries the env-driven HTTP Basic Auth credentials used to protect the first-admin setup endpoint.
ref: minio/minio internal/auth/credentials.go (length fail-fast at startup) ref: keycloak/keycloak KC_BOOTSTRAP_ADMIN_USERNAME/PASSWORD env model
type BootstrapRateLimiter ¶
BootstrapRateLimiter decides whether a request identified by key should be allowed. Implementations are injected by the caller so that runtime/auth does not import runtime/http/middleware (which imports runtime/auth, creating a cycle). Callers wire a concrete middleware.RateLimiter or an adapters/ratelimit.TokenBucket which satisfies this interface structurally.
There is no built-in "allow all" implementation — bootstrap routes must always carry a real per-IP limiter to defeat brute-force enumeration of operator credentials. Tests construct fakes locally if they need to bypass rate limiting (see runtime/auth/bootstrap_test.go fakeRateLimiter).
type Claims ¶
Claims is a type alias of cell.Claims so that runtime/auth and kernel/cell share a single canonical struct without conversion at package boundaries. All existing code that references auth.Claims continues to compile.
type EnvKeyProvider ¶
type EnvKeyProvider struct {
// contains filtered or unexported fields
}
EnvKeyProvider loads key material from environment variables at construction. It succeeds even if some key domains are not configured; individual domain errors surface when RSAKeySet() or HMACKeyRing() is called.
This provider loads keys once (fail-fast per domain). For hot-reload support, see future WM-34 integration.
func NewEnvKeyProvider ¶
func NewEnvKeyProvider(clk clock.Clock, opts ...EnvKeyProviderOption) *EnvKeyProvider
NewEnvKeyProvider loads all available key material from environment variables. It returns a provider even if some key types are not configured — call RSAKeySet() or HMACKeyRing() to check individual domain availability. clk is required; pass clock.Real() at the composition root.
func (*EnvKeyProvider) HMACKeyRing ¶
func (p *EnvKeyProvider) HMACKeyRing() (*HMACKeyRing, error)
HMACKeyRing returns the cached HMACKeyRing or the error from loading.
func (*EnvKeyProvider) RSAKeySet ¶
func (p *EnvKeyProvider) RSAKeySet() (*KeySet, error)
RSAKeySet returns the cached RSA KeySet or the error from loading.
type EnvKeyProviderOption ¶
type EnvKeyProviderOption func(*EnvKeyProvider)
EnvKeyProviderOption configures EnvKeyProvider behavior.
func WithEnvKeyProviderLogger ¶
func WithEnvKeyProviderLogger(l *slog.Logger) EnvKeyProviderOption
WithEnvKeyProviderLogger sets the logger for EnvKeyProvider.
type HMACKeyRing ¶
type HMACKeyRing struct {
// contains filtered or unexported fields
}
HMACKeyRing holds an ordered pair of HMAC secrets for service token operations. Position 0 (current) is used for signing; verification tries all secrets in order.
ref: zeromicro/go-zero rest/token/tokenparser.go — dual-key [current, previous] model ref: gorilla/securecookie — DecodeMulti try-all-keys pattern
func LoadHMACKeyRingFromEnv ¶
func LoadHMACKeyRingFromEnv() (*HMACKeyRing, error)
LoadHMACKeyRingFromEnv loads an HMACKeyRing from environment variables. GOCELL_SERVICE_SECRET is required; GOCELL_SERVICE_SECRET_PREVIOUS is optional.
Both variables are read as raw UTF-8 strings and used directly as HMAC key bytes (no base64 decoding is performed). The value must be at least 32 bytes long. To generate a suitable value: openssl rand -base64 32.
func NewHMACKeyRing ¶
func NewHMACKeyRing(current []byte, previous []byte) (*HMACKeyRing, error)
NewHMACKeyRing creates an HMACKeyRing. current must be at least MinHMACKeyBytes (32 bytes). previous may be nil for single-secret mode; if set, it must also meet the minimum length.
func (*HMACKeyRing) Current ¶
func (r *HMACKeyRing) Current() []byte
Current returns a copy of the active signing secret. The returned slice is a fresh allocation; callers cannot mutate the ring's internal state.
func (*HMACKeyRing) Secrets ¶
func (r *HMACKeyRing) Secrets() [][]byte
Secrets returns a copy of all secrets in try-order: current first, then previous (if set). The returned slice is a fresh allocation; callers cannot mutate the ring's internal state.
type InMemoryNonceOption ¶
type InMemoryNonceOption func(*InMemoryNonceStore)
InMemoryNonceOption configures an InMemoryNonceStore.
func WithMaxNonceEntries ¶
func WithMaxNonceEntries(n int) InMemoryNonceOption
WithMaxNonceEntries overrides the maximum number of live nonce entries before a forced prune is triggered. The default is defaultMaxNonceEntries.
type InMemoryNonceStore ¶
type InMemoryNonceStore struct {
// contains filtered or unexported fields
}
InMemoryNonceStore is a NonceStore backed by a map with lazy expiry pruning. Suitable for single-instance deployments.
func NewInMemoryNonceStore ¶
func NewInMemoryNonceStore(maxAge time.Duration, clk clock.Clock, opts ...InMemoryNonceOption) (*InMemoryNonceStore, error)
NewInMemoryNonceStore creates an InMemoryNonceStore with the given maxAge and clock. clk must not be nil; use clock.Real() for production and clockmock.New() for tests. maxAge must be at least ServiceTokenNonceTTL; a shorter value reintroduces the replay window the store is designed to close, and is rejected with an error.
func (*InMemoryNonceStore) CheckAndMark ¶
func (s *InMemoryNonceStore) CheckAndMark(_ context.Context, nonce string) error
CheckAndMark checks whether nonce has been seen within its TTL window. If not, it records the nonce and returns nil. If the nonce is still live, ErrNonceReused is returned. If the store is at capacity and no entries can be reclaimed, ErrNonceStoreFull is returned.
func (*InMemoryNonceStore) Kind ¶
func (*InMemoryNonceStore) Kind() NonceStoreKind
Kind reports NonceStoreKindInMemory.
func (*InMemoryNonceStore) MaxAge ¶
func (s *InMemoryNonceStore) MaxAge() time.Duration
MaxAge reports the configured nonce retention window. Exposed for startup validation (assert store outlives ServiceTokenMaxAge) and integration tests.
type IntentTokenVerifier ¶
type IntentTokenVerifier = cell.IntentTokenVerifier
IntentTokenVerifier is a type alias of cell.IntentTokenVerifier so that runtime/auth and kernel/cell share a single canonical interface without conversion at package boundaries. All existing code that references auth.IntentTokenVerifier continues to compile without modification.
This is the only verification interface in GoCell. Every production verification path must declare the expected intent to prevent token-confusion attacks (RFC 8725 §3.11).
type IssueOptions ¶
type IssueOptions struct {
Roles []string
Audience []string
SessionID string
PasswordResetRequired bool
}
IssueOptions carries optional parameters for JWT issuance. Roles, Audience, and SessionID retain their original semantics. PasswordResetRequired is written as the "password_reset_required" claim only when true; when false the claim is omitted to keep the token compact.
This struct replaces the previous 5-parameter Issue signature (backlog T2 trigger: adding PasswordResetRequired would have been the 6th positional argument).
type JWTIssuer ¶
type JWTIssuer struct {
// contains filtered or unexported fields
}
JWTIssuer signs JWT tokens with RS256 using the active key from a SigningKeyProvider. Each issued token carries a kid header derived from the signing key.
func NewJWTIssuer ¶
func NewJWTIssuer(keys SigningKeyProvider, issuer string, ttl time.Duration, clk clock.Clock, opts ...JWTIssuerOption) (*JWTIssuer, error)
NewJWTIssuer creates a JWTIssuer using the active signing key from the provider.
clk is required; pass clock.Real() at the composition root or clockmock.New(...) in tests. Panics on nil or typed-nil clock.
Rejects both plain-nil and typed-nil keys (see NewJWTVerifier).
func (*JWTIssuer) Issue ¶
func (i *JWTIssuer) Issue(intent TokenIntent, subject string, opts IssueOptions) (string, error)
Issue creates a signed JWT token for the given subject and options.
intent declares how the token is meant to be used. GoCell only issues access JWTs. The resulting JWT carries both a JOSE "typ" header (at+jwt) and a "token_use" payload claim so verifiers can reject token-confusion attempts on two independent channels (RFC 9068 §2.1, RFC 8725 §3.11).
The token header includes the kid of the active signing key. When opts.SessionID is non-empty, a "sid" claim binds the token to a specific session for revocation support.
When opts.PasswordResetRequired is true, the claim "password_reset_required" is written into the token payload. When false (the zero value) the claim is omitted entirely for backward compatibility and to minimize token size.
type JWTIssuerOption ¶
type JWTIssuerOption func(*JWTIssuer)
JWTIssuerOption configures a JWTIssuer.
func WithIssuerAudiencesFromSlice ¶
func WithIssuerAudiencesFromSlice(auds []string) JWTIssuerOption
WithIssuerAudiencesFromSlice sets the audience written into tokens when IssueOptions.Audience is nil (fallback / default audience). The slice is copied defensively. Empty or nil slices are silently ignored.
This is the canonical way to configure default audiences when constructing a JWTIssuer from a config.Registry. The config package uses this option internally; production code should prefer config.NewJWTIssuerFromRegistry instead of calling this directly.
type JWTVerifier ¶
type JWTVerifier struct {
// contains filtered or unexported fields
}
JWTVerifier verifies JWT tokens signed with RS256.
ref: go-kratos/kratos middleware/auth/jwt/jwt.go -- JWT middleware pattern Adopted: KeyFunc-based verification, Claims extraction from context. Deviated: RS256 pinned (no configurable signing method), refuses HS256/none. Extended: kid-based key lookup from VerificationKeyStore (RFC 7638 thumbprint).
ref: golang-jwt/jwt v5 parser_option.go -- WithTimeFunc for clock injection. ref: coreos/go-oidc v3 oidc.go IDTokenVerifier -- issuer validation pattern.
func NewJWTVerifier ¶
func NewJWTVerifier(keys VerificationKeyStore, clk clock.Clock, opts ...JWTVerifierOption) (*JWTVerifier, error)
NewJWTVerifier creates a JWTVerifier that validates tokens by looking up the signing key from the VerificationKeyStore using the token's kid header.
clk is required; pass clock.Real() at the composition root or clockmock.New(...) in tests. Panics on nil or typed-nil clock.
Rejects both plain-nil and typed-nil keys (e.g. var ks *KeySet = nil passed through an interface variable): a typed-nil would panic on the first method call downstream, so we fail fast at construction.
func (*JWTVerifier) VerifyIntent ¶
func (v *JWTVerifier) VerifyIntent(ctx context.Context, tokenStr string, expected TokenIntent) (Claims, error)
VerifyIntent validates the token and additionally requires the declared intent (JWT token_use claim + JOSE typ header) to equal expected. Returns ErrAuthInvalidTokenIntent when:
- expected is not a valid TokenIntent
- the token lacks a token_use claim or typ header
- the token's claim/header disagree with each other
- the token's intent does not match expected
This is the primary API for HTTP middleware (expected=access) and the HTTP middleware (expected=access). Refresh endpoints do not verify JWT refresh tokens; they pass opaque wire tokens to refresh.Store.
type JWTVerifierOption ¶
type JWTVerifierOption func(*JWTVerifier)
JWTVerifierOption configures a JWTVerifier.
func WithExpectedAudiences ¶
func WithExpectedAudiences(first string, rest ...string) JWTVerifierOption
WithExpectedAudiences configures VerifyIntent to enforce that the token's aud claim contains at least one of the given audience strings per RFC 8725 §3.3 ("recipients MUST validate the aud claim"). This option is REQUIRED: NewJWTVerifier returns an error if no expected audiences are configured.
The first argument is required (preventing zero-argument calls). Empty strings are silently filtered. Duplicate values across multiple calls are deduplicated.
Verify() is never affected — audience enforcement is intentionally scoped to VerifyIntent only.
ref: RFC 8725 §3.3, RFC 7519 §4.1.3 (aud may be string or array)
func WithExpectedIssuer ¶
func WithExpectedIssuer(iss string) JWTVerifierOption
WithExpectedIssuer configures VerifyIntent to enforce that the token's iss claim exactly matches the given issuer string. The check is applied after audience validation so audience errors remain distinguishable in structured logs. An empty iss argument is silently ignored (no-op), preserving the previous behavior of accepting any issuer.
ref: coreos/go-oidc v3 IDTokenVerifier — issuer validation with equality check ref: golang-jwt/jwt v5 WithIssuer ParserOption — functional option pattern
type KeyProvider ¶
type KeyProvider interface {
// RSAKeySet returns the RSA KeySet for JWT signing and verification.
// Returns an error if no RSA keys are configured.
RSAKeySet() (*KeySet, error)
// HMACKeyRing returns the HMACKeyRing for service token operations.
// Returns an error if no HMAC secrets are configured.
HMACKeyRing() (*HMACKeyRing, error)
}
KeyProvider abstracts the source of cryptographic key material. It is consumed at the composition root (main.go) to build JWTIssuer, JWTVerifier, and service token infrastructure.
Implementations may load from environment variables, files, Vault, or JWKS endpoints. The provider caches loaded keys; consumers call RSAKeySet() or HMACKeyRing() and handle domain-specific errors.
ref: lestrrat-go/jwx — jwk.Set interface for key set abstraction ref: coreos/go-oidc — RemoteKeySet cache-first pattern (future) Adopted: separate RSA / HMAC domains (ISP); cache at construction. Deviated: no remote refresh (FR-017 static config only).
func MustNewTestKeyProvider ¶
func MustNewTestKeyProvider(clk clock.Clock) KeyProvider
MustNewTestKeyProvider creates a KeyProvider with ephemeral RSA and HMAC keys for testing. It panics on error, following the Go test helper convention. clk is required; pass clock.Real() from the composition root or a clockmock for time-controlled tests.
type KeySet ¶
type KeySet struct {
// contains filtered or unexported fields
}
KeySet holds a set of cryptographic keys for JWT operations: one active signing key and zero or more verification-only keys. It provides O(1) key lookup by kid (key identifier). All methods are safe for concurrent use.
ref: dexidp/dex server/rotation.go — 3-state model (Active → Verification-only → Pruned)
ref: 201-wm2-key-rotation — clock injection is needed so rotation tests can advance time without real sleeps; uses kernel/clock.Clock (not func() time.Time) for consistency with the platform clock contract.
func LoadKeySetFromEnv ¶
LoadKeySetFromEnv loads a KeySet from environment variables. It reads the active key pair from GOCELL_JWT_PRIVATE_KEY / GOCELL_JWT_PUBLIC_KEY, and optionally loads a verification-only key from GOCELL_JWT_PREV_PUBLIC_KEY with expiry from GOCELL_JWT_PREV_KEY_EXPIRES (RFC 3339). clk is required; pass clock.Real() at the composition root.
func MustNewTestKeySet ¶
MustNewTestKeySet creates a KeySet from a freshly generated 2048-bit RSA key pair. It panics on error. Intended for test setup and examples only. clk is the clock used for key expiry checks; pass clock.Real() from the composition root or clockmock.New(...) for time-controlled tests.
func NewKeySet ¶
func NewKeySet(priv *rsa.PrivateKey, pub *rsa.PublicKey, clk clock.Clock, opts ...KeySetOption) (*KeySet, error)
NewKeySet creates a KeySet with a single active signing key pair. The kid is derived deterministically from the public key using RFC 7638.
clk is required; pass clock.Real() at the composition root or clockmock.New(...) in tests. Panics on nil or typed-nil clock.
func NewKeySetWithVerificationKeys ¶
func NewKeySetWithVerificationKeys( priv *rsa.PrivateKey, pub *rsa.PublicKey, clk clock.Clock, vkeys []VerificationKey, opts ...KeySetOption, ) (*KeySet, error)
NewKeySetWithVerificationKeys creates a KeySet with an active signing key and one or more verification-only keys. Keys that are already expired at construction time are pruned immediately.
func (*KeySet) PruneExpired ¶
func (ks *KeySet) PruneExpired()
PruneExpired removes verification-only keys whose expiry time has passed from the internal maps. This is an explicit cleanup operation — call it at rotation boundaries or during maintenance, not on the request path. Safe for concurrent use.
func (*KeySet) PublicKeyByKID ¶
PublicKeyByKID looks up a public key by its kid. For verification-only keys, it checks the expiry time and rejects expired keys. This method is a pure-read operation — it does not mutate internal state. Safe for concurrent use.
ref: dexidp/dex, hashicorp/vault, gravitational/teleport — all keep the verification/lookup path read-only; lifecycle mutations happen at the rotation/loading boundary, not on every request.
func (*KeySet) SigningKey ¶
func (ks *KeySet) SigningKey() *rsa.PrivateKey
SigningKey returns the active private key for signing. The returned pointer is shared — the caller must not modify the key or its Precomputed fields. Unlike HMACKeyRing.Current(), RSA private keys are returned by reference because deep-copying is expensive and unnecessary for the signing hot path.
func (*KeySet) SigningKeyID ¶
SigningKeyID returns the kid of the active signing key.
type KeySetOption ¶
type KeySetOption func(*KeySet)
KeySetOption configures KeySet behavior.
func WithKeySetLogger ¶
func WithKeySetLogger(l *slog.Logger) KeySetOption
WithKeySetLogger sets the logger for a KeySet.
type NonceStore ¶
type NonceStore interface {
CheckAndMark(ctx context.Context, nonce string) error
Kind() NonceStoreKind
}
NonceStore tracks nonces for replay prevention. Implementations must be safe for concurrent use. The store must retain nonces for at least ServiceTokenNonceTTL to prevent replay across the token validity window plus the accepted future clock skew; a shorter TTL creates a replay vulnerability.
Kind reports the implementation classification for startup validation. Control-plane guards in adapter mode "real" must reject NonceStoreKindNoop so replay protection is never silently disabled in production.
This interface is identical to cell.NonceStore; runtime/auth types that implement it automatically satisfy the kernel interface.
type NonceStoreKind ¶
type NonceStoreKind = cell.NonceStoreKind
NonceStoreKind classifies a NonceStore implementation for startup validation. It is an alias of cell.NonceStoreKind so that runtime/auth types satisfy the kernel interface directly without conversion.
type NoopNonceStore ¶
type NoopNonceStore struct{}
NoopNonceStore is an explicit null-object implementation of NonceStore that always permits a nonce. It exists so that callers never carry a nil NonceStore through the authenticator pipeline — every code path operates on a non-nil implementation, and dev-mode opt-out is explicit rather than accidental.
cmd/corebundle.SharedDeps.Validate rejects this implementation in adapter mode "real" (see errcode.ErrControlplaneNonceStoreMissing).
func NewNoopNonceStore ¶
func NewNoopNonceStore() NoopNonceStore
NewNoopNonceStore returns the sentinel NoopNonceStore value.
func (NoopNonceStore) CheckAndMark ¶
func (NoopNonceStore) CheckAndMark(context.Context, string) error
CheckAndMark always returns nil — replay detection is disabled.
func (NoopNonceStore) Kind ¶
func (NoopNonceStore) Kind() NonceStoreKind
Kind reports NonceStoreKindNoop.
type Policy ¶
Policy evaluates an HTTP request for authorization. A nil return permits the request; a non-nil errcode error short-circuits with the mapped status. Taking *http.Request (not just context) lets policies read path/query params for self-access checks without handler cooperation.
ref: go-chi/jwtauth Authenticator — middleware wraps handler; error-only decision. Deviated: Policy takes *http.Request instead of context so that SelfOr can read path values directly (r.PathValue) without the handler passing the target ID in explicitly. ref: grpc-ecosystem/go-grpc-middleware WithServerUnaryInterceptor — interceptor pattern where auth is declared at registration time, not inside handler body.
func AnyRole ¶
AnyRole returns a Policy that requires the subject to hold at least one of the given roles. Wraps RequireAnyRole.
Footgun: calling AnyRole() with zero roles produces a Policy that always returns ErrAuthForbidden (no role can match). Pass at least one named role, or use Public:true on the auth.Route if the endpoint should be unauthenticated.
func RequireCallerCell ¶
RequireCallerCell returns a Policy that enforces the request is made by a service principal (PrincipalService) whose CallerCellID is in the allowlist.
Use in auth.Route.Policy for internal endpoints that declare Clients in their ContractSpec; auth.Mount auto-applies this guard when spec.Clients is non-empty.
Errors:
- ErrAuthUnauthorized: no Principal in context
- ErrAuthForbidden: Principal is not PrincipalService, or CallerCellID is empty, or CallerCellID not in allowlist
func SelfOr ¶
SelfOr returns a Policy that permits the request when the subject equals the path parameter value, or when the subject holds one of bypassRoles. pathParam is the name of the path parameter (e.g. "id", "userID") whose value is compared against the authenticated subject via r.PathValue.
If the path value matches the strict canonical UUID form (36-char dashed or 32-char compact, any case), it is normalized to lowercase canonical dashed form before comparison. Brace-wrapped, urn:uuid:, and whitespace-padded shapes are NOT treated as UUIDs here even though google/uuid.Parse accepts them — they fall through to verbatim comparison and almost always fail to match a canonical subject, leaving the bypassRoles check as the only path to authorization. Non-UUID path values (e.g. roleName) are compared verbatim.
If the path parameter is empty (route does not carry it), the check falls back to role-only; prefer AnyRole for those endpoints.
type Principal ¶
type Principal struct {
Kind PrincipalKind
Subject string
Roles []string
AuthMethod string
PasswordResetRequired bool
// CallerCellID is the originating cell id for service principals. It is
// extracted from the 4-part service token (ts:nonce:callerCell:mac) and
// checked against ContractSpec.Clients by RequireCallerCell. Empty for
// user and anonymous principals.
CallerCellID string
// Claims is a read-only snapshot of supplementary JWT claims (e.g. "sid",
// "iss", "token_use"). Callers must treat Claims as a read-only snapshot;
// mutating it has no effect on authentication decisions and may corrupt
// shared state.
Claims map[string]string
// ExpiresAt is the absolute expiration time of the credential that
// produced this Principal. Zero value means "no expiry" (anonymous
// and service principals never expire). For JWT principals this is
// copied from Claims.ExpiresAt by jwtClaimsToPrincipal.
//
// Consumers (e.g. runtime/websocket.Hub) check ExpiresAt against the
// current clock to decide eviction; long-lived WebSocket connections
// are evicted on the next ping tick after token expiry.
ExpiresAt time.Time
}
Principal is the unified authn subject injected into request context after successful authentication. AuthMiddleware and ServiceTokenMiddleware both populate Principal via WithPrincipal; handlers consume it via FromContext. Principal is the authoritative authn context for all business routes (F1/F7).
For service principals (Kind==PrincipalService): identity is expressed via CallerCellID (the originating cell id extracted from the 4-part service token). Subject is always empty; Roles is always nil. Use RequireCallerCell to authorize internal endpoints by caller allowlist.
Immutability contract: After an Authenticator returns a Principal, callers must not modify any field for the lifetime of the request/connection. runtime/websocket.Hub snapshots Subject and ExpiresAt at handshake time and treats the principal as immutable; mutating Subject/Roles/ExpiresAt/Claims after Authenticate has undefined effects on hub subjectIdx and expiry eviction. Slices (Roles) and maps (Claims) are read-only by convention; do not append or mutate. This mirrors the existing Claims map convention.
func MustFromContext ¶
type PrincipalKind ¶
type PrincipalKind int
const ( // PrincipalUnknown is the zero value of PrincipalKind; it indicates an // uninitialised Principal and must never appear in a fully-constructed // Principal returned by an Authenticator. PrincipalUnknown PrincipalKind = iota PrincipalUser // JWT user PrincipalService // service token / mTLS machine PrincipalAnonymous // public endpoint )
func (PrincipalKind) String ¶
func (k PrincipalKind) String() string
type Route ¶
type Route struct {
// Contract is the ContractSpec to bind to this route. REQUIRED. Its
// Method + Path drive both the chi registration pattern and the span
// attributes (gocell.contract.id / kind / transport + http.method /
// route). Contract.Kind MUST be "http".
Contract contractspec.ContractSpec
// Handler is the inner http.Handler. REQUIRED.
Handler http.Handler
// Policy is the per-route authorization policy enforced by
// RequirePolicy. Nil = no guard.
Policy Policy
// Public marks a JWT-exempt route. Public routes MUST carry no Policy
// (server-side authorization has no subject to authorize) and no
// PasswordResetExempt (the gate runs only for authenticated tokens).
Public bool
// PasswordResetExempt allows reset-required tokens through this route.
PasswordResetExempt bool
// BootstrapAuth, when non-nil, marks the route as protected by per-route
// replacement authentication (HTTP Basic Auth using
// GOCELL_BOOTSTRAP_ADMIN_USERNAME/PASSWORD env credentials, supplied by
// the composition root via runtime/auth.NewBootstrapMiddleware). The
// listener-level JWT middleware skips this route (via FinalizeAuth
// matcher); the function passed here authenticates instead. The non-nil
// presence of this field is the single source of truth — there is no
// separate Bootstrap bool flag.
//
// BootstrapAuth is mutually exclusive with Public, PasswordResetExempt,
// and Policy: a route either runs through the listener auth chain (with
// optional Public / PasswordResetExempt overrides + Policy guard) or it
// runs through the bootstrap middleware. Combining the two would mean
// "bypass JWT but also enforce JWT-time policies" — a contradiction that
// validateBypassCompatibility rejects at registration time.
//
// ref: docs/architecture/202605061600-adr-bootstrap-admin-boundary.md §D1
BootstrapAuth func(http.Handler) http.Handler
}
Route binds a handler to a contract. Contract is the single source of truth for HTTP method + fully-qualified path + observability metadata — runtime-side Method/Path fields have been eliminated in round-4 (the pre-round-4 dual-maintenance of Route.Method/Path vs Contract.Method/Path was the drift surface reviewers called out; the collapse pins the two to the same literal).
Mount derives the chi sub-route-relative registration path by stripping the receiving mux's Prefix() (when the mux implements cell.Prefixer; runtime/http/router.Router's nested chiRouterAdapter does). Stdlib *http.ServeMux and test stubs without a prefix use Contract.Path unchanged.
type ServiceTokenOption ¶
type ServiceTokenOption func(*serviceTokenConfig)
ServiceTokenOption configures ServiceTokenMiddleware behavior.
func WithServiceTokenClock ¶
func WithServiceTokenClock(clk clock.Clock) ServiceTokenOption
WithServiceTokenClock overrides the clock for timestamp validation.
func WithServiceTokenLogger ¶
func WithServiceTokenLogger(l *slog.Logger) ServiceTokenOption
WithServiceTokenLogger sets the logger for ServiceTokenMiddleware.
func WithServiceTokenMetrics ¶
func WithServiceTokenMetrics(m *AuthMetrics) ServiceTokenOption
WithServiceTokenMetrics sets the AuthMetrics for ServiceTokenMiddleware.
func WithServiceTokenNonceStore ¶
func WithServiceTokenNonceStore(ns NonceStore) ServiceTokenOption
WithServiceTokenNonceStore configures the replay-defense store. The middleware rejects nonces already consumed within the store's TTL window. Replay protection is mandatory — both ServiceTokenMiddleware and NewServiceTokenAuthenticator reject nil/Noop NonceStore at construction time. Use NewInMemoryNonceStore(ServiceTokenNonceTTL) for dev/test wiring.
Passing nil is a no-op: cfg.nonceStore stays nil and construction will fail.
type SigningKeyProvider ¶
type SigningKeyProvider interface {
// SigningKey returns the active RSA private key for signing tokens.
SigningKey() *rsa.PrivateKey
// SigningKeyID returns the kid (key identifier) of the active signing key.
SigningKeyID() string
}
SigningKeyProvider supplies the active signing key for JWT issuance. Implementations must be safe for concurrent use.
*KeySet satisfies this interface.
type StaticKeyProvider ¶
type StaticKeyProvider struct {
// contains filtered or unexported fields
}
StaticKeyProvider holds pre-constructed key material. Useful for tests and composition roots that load keys before building the provider.
func NewStaticKeyProvider ¶
func NewStaticKeyProvider(ks *KeySet, ring *HMACKeyRing) *StaticKeyProvider
NewStaticKeyProvider creates a KeyProvider from pre-existing key material. Either parameter may be nil; the corresponding getter returns an error.
func (*StaticKeyProvider) HMACKeyRing ¶
func (p *StaticKeyProvider) HMACKeyRing() (*HMACKeyRing, error)
HMACKeyRing returns the pre-loaded HMACKeyRing or an error if nil.
func (*StaticKeyProvider) RSAKeySet ¶
func (p *StaticKeyProvider) RSAKeySet() (*KeySet, error)
RSAKeySet returns the pre-loaded KeySet or an error if nil.
type TokenIntent ¶
type TokenIntent = cell.TokenIntent
TokenIntent is a type alias of cell.TokenIntent so that runtime/auth and kernel/cell share a single canonical type without conversion at package boundaries. All existing code that references auth.TokenIntent continues to compile without modification.
ref: RFC 9068 §2.1 (typ: at+jwt), RFC 8725 §3.11 (token confusion defense) ref: AWS Cognito token_use claim ("access"/"id"), Keycloak TokenUtil.java
type UnionAuthenticator ¶
type UnionAuthenticator struct {
// contains filtered or unexported fields
}
UnionAuthenticator tries each child in order and returns the first successful result. An error from any child short-circuits the chain (FailOnError semantics).
func NewUnionAuthenticator ¶
func NewUnionAuthenticator(children ...Authenticator) *UnionAuthenticator
NewUnionAuthenticator returns a UnionAuthenticator that delegates to children in the order provided.
func (*UnionAuthenticator) Authenticate ¶
Authenticate iterates over child authenticators and returns the first result that indicates a valid credential. If a child returns an error the chain stops immediately and the error is propagated (credential present but invalid).
type VerificationKey ¶
VerificationKey is a previously active signing key that has been demoted. It retains the public key for token validation during the grace period.
type VerificationKeyStore ¶
type VerificationKeyStore interface {
// PublicKeyByKID returns the public key matching the given kid.
// Returns an error for unknown or expired kids.
PublicKeyByKID(kid string) (*rsa.PublicKey, error)
}
VerificationKeyStore looks up public keys for JWT verification by kid. Implementations must be safe for concurrent use.
*KeySet satisfies this interface.
Source Files
¶
Directories
¶
| Path | Synopsis |
|---|---|
|
Package authtest provides test-only auth Policy helpers for runtime middleware behavior tests.
|
Package authtest provides test-only auth Policy helpers for runtime middleware behavior tests. |
|
Package config is the production wiring entrypoint for runtime/auth components.
|
Package config is the production wiring entrypoint for runtime/auth components. |
|
This file intentionally declares only one sentinel (ErrRejected).
|
This file intentionally declares only one sentinel (ErrRejected). |
|
memstore
Package memstore provides an in-memory implementation of refresh.Store.
|
Package memstore provides an in-memory implementation of refresh.Store. |
|
storetest
Package storetest provides a reusable contract test suite for refresh.Store implementations.
|
Package storetest provides a reusable contract test suite for refresh.Store implementations. |
|
Package session declares the typed-Go-heavy Protocol primitive that bundles session-related protocol decisions for accesscore (and any future cell that owns server-side session state).
|
Package session declares the typed-Go-heavy Protocol primitive that bundles session-related protocol decisions for accesscore (and any future cell that owns server-side session state). |
|
storetest
Package storetest provides a reusable Protocol-driven contract test suite for session.Store implementations.
|
Package storetest provides a reusable Protocol-driven contract test suite for session.Store implementations. |