Documentation
¶
Index ¶
- Constants
- Variables
- func Allow(ctx context.Context, checker PermissionChecker, cl Claims, perm authkit.Perm, ...) (bool, error)
- func NewSSRFGuardedClient() *http.Client
- func Optional(v *Verifier) func(http.Handler) http.Handler
- func RequirePermission(checker PermissionChecker, perm authkit.Perm, ...) func(http.Handler) http.Handler
- func Required(v *Verifier) func(http.Handler) http.Handler
- func RequiredLive(v *Verifier) (func(http.Handler) http.Handler, error)
- func Sensitive(options ...SensitiveOptions) func(http.Handler) http.Handler
- func SensitiveClaims(cl Claims, options ...SensitiveOptions) bool
- func SetClaims(ctx context.Context, cl Claims) context.Context
- func WithPermissionScope(ctx context.Context, scope PermissionScope) context.Context
- type Claims
- func (c Claims) Attribute(key string) (json.RawMessage, bool)
- func (c Claims) AttributeIsReference(key string) bool
- func (c Claims) AttributeReference(key string) (ref string, ok bool)
- func (c Claims) AuthenticatedWithin(maxAge time.Duration) bool
- func (c Claims) BoundToPermissionGroup() bool
- func (c Claims) Delegated() (DelegatedPrincipal, bool)
- func (c Claims) DelegatedAccess() (DelegatedPrincipal, bool)
- func (c Claims) DocumentReference(documentType string) (documents.Reference, bool)
- func (c Claims) HasAMR(method string) bool
- func (c Claims) HasEntitlement(ent string) bool
- func (c Claims) HasPermission(perm authkit.Perm) bool
- func (c Claims) HasRole(role string) bool
- func (c Claims) IsDelegatedAccessToken() bool
- func (c Claims) IsUser() bool
- func (c Claims) PermissionGroupAllows(scope PermissionScope) bool
- func (c Claims) Principal() authkit.Principal
- func (c Claims) PrincipalKind() authkit.PrincipalKind
- type DelegatedPrincipal
- type Enricher
- type FederationStats
- type IssuerKey
- type IssuerOptions
- type LivenessSource
- type PermissionChecker
- type PermissionScope
- type PermissionValidator
- type RemoteApplicationSource
- type SensitiveOptions
- type ServiceJWTVerifyOption
- type Verifier
- func (v *Verifier) AddIssuer(issuerID string, audiences []string, opts IssuerOptions) error
- func (v *Verifier) AddMFAEnrollmentExemptRoutes(paths []string) *Verifier
- func (v *Verifier) AllowLive(ctx context.Context, checker PermissionChecker, cl Claims, perm authkit.Perm, ...) (bool, error)
- func (v *Verifier) FederationStats() FederationStats
- func (v *Verifier) HTTPClient() *http.Client
- func (v *Verifier) HasLiveness() bool
- func (v *Verifier) IsLive(ctx context.Context, cl Claims) (bool, authkit.UserLiveness, error)
- func (v *Verifier) LoadRemoteApplications(ctx context.Context, src RemoteApplicationSource, audiences []string) error
- func (v *Verifier) RemoveIssuer(issuerID string)
- func (v *Verifier) SetMFAEnrollmentExemptPaths(paths []string) *Verifier
- func (v *Verifier) SetRemoteApplicationSource(src RemoteApplicationSource)
- func (v *Verifier) ValidateDocumentIssuer(ctx context.Context, issuer string) error
- func (v *Verifier) Verify(ctx context.Context, tokenStr string) (Claims, error)
- func (v *Verifier) VerifyClaims(ctx context.Context, tokenStr string) (jwt.MapClaims, error)
- func (v *Verifier) VerifyDelegatedAccess(ctx context.Context, tokenStr string) (Claims, DelegatedPrincipal, error)
- func (v *Verifier) VerifyDelegatedAccessRequest(r *http.Request) (Claims, DelegatedPrincipal, error)
- func (v *Verifier) VerifyDocument(ctx context.Context, document documents.SignedDocument, ...) (documents.Envelope, error)
- func (v *Verifier) VerifyRequest(r *http.Request) (Claims, error)
- func (v *Verifier) VerifyRequestLive(r *http.Request) (Claims, error)
- func (v *Verifier) VerifyServiceJWT(ctx context.Context, tokenStr string, opts ...ServiceJWTVerifyOption) (authkit.ServiceJWTClaims, error)
- func (v *Verifier) WithLiveness(src LivenessSource) *Verifier
- func (v *Verifier) WithService(svc Enricher) *Verifier
- type VerifierOption
- func WithAPIKeyPrefix(prefix string) VerifierOption
- func WithAlgorithms(algs ...string) VerifierOption
- func WithHTTPClient(c *http.Client) VerifierOption
- func WithPermissions(fn PermissionValidator) VerifierOption
- func WithRemoteApplicationAudiences(audiences ...string) VerifierOption
- func WithRequireMFAEnrollment(require bool) VerifierOption
- func WithSSRFGuard() VerifierOption
- func WithSkew(d time.Duration) VerifierOption
Constants ¶
const ( AccessTokenType = jwtkit.AccessTokenType DelegatedAccessTokenType = jwtkit.DelegatedAccessTokenType RemoteApplicationAccessTokenType = jwtkit.RemoteApplicationAccessTokenType )
Token-type tags used by the verification layer. Sourced from jwtkit so they stay in lockstep with the signer; authhttp exposes the same values via its own delegation.go constants.
const APIKeyPrincipalType = "api-key"
APIKeyPrincipalType is the TokenType value carried by an opaque API key: a machine credential, not a user.
const DefaultOutboundTimeout = netguard.DefaultTimeout
DefaultOutboundTimeout bounds the verify layer's outbound HTTP calls (JWKS fetches).
const DefaultSensitiveMaxAge = 15 * time.Minute
const MaxDelegatedRoles = 64
MaxDelegatedRoles bounds how many role UUIDs we lift from attributes.roles on a delegated token, so a hostile issuer can't inflate a principal unboundedly.
const RemoteApplicationTokenType = "remote_application"
RemoteApplicationTokenType is the TokenType value carried by a remote application access token: a remote_application acting AS ITSELF. Like an API-key principal it carries Permissions (its STORED authority) but no UserID; the live-user enrichment/ban gate is skipped (there is no user).
Variables ¶
var ( // ErrSenderProofRequired rejects a certificate-bound token presented // without its certificate: no TLS peer, a different leaf, or a token-only // verification detached from its request. ErrSenderProofRequired = authkit.E(authkit.CodeSenderProofRequired) // ErrInvalidConfirmation rejects a `cnf` claim that is not exactly // {"x5t#S256": <unpadded base64url sha256>}. ErrInvalidConfirmation = authkit.E(authkit.CodeInvalidConfirmation) // ErrConfirmationWrongTokenType rejects `cnf` on any token type AuthKit does // not bind — accepting an unenforced binding would be a silent downgrade. ErrConfirmationWrongTokenType = authkit.E(authkit.CodeConfirmationWrongTokenType) )
RFC 8705 certificate-bound delegated tokens (ak#277). A `cnf.x5t#S256` claim is honoured only against the leaf certificate Go's TLS stack authenticated on THIS request; no header or context value can stand in.
var ErrLivenessUnconfigured = errors.New("verify: liveness gate used without a LivenessSource (call Verifier.WithLiveness)")
ErrLivenessUnconfigured is returned by VerifyRequestLive when no LivenessSource is wired. It is NOT a wire error: a missing source is a host wiring mistake, not a bad credential, and conflating the two would let a deployment that cannot check liveness look like one where every user is banned. RequiredLive refuses at construction so this can only be reached by an out-of-band caller.
Functions ¶
func Allow ¶ added in v0.71.0
func Allow(ctx context.Context, checker PermissionChecker, cl Claims, perm authkit.Perm, scope PermissionScope) (bool, error)
Allow checks machine permission ceilings against the exact UUID and authority issuer. Unbound delegated permissions retain their explicit issuer-trust contract. Human permissions always come from live assignments on GroupID. A missing or mismatched machine binding never falls back to human authority.
func NewSSRFGuardedClient ¶
NewSSRFGuardedClient returns a timeout-bounded *http.Client whose dialer resolves the target itself and refuses any private/reserved address, so a crafted jwks_uri (including DNS rebinding) can never reach internal services. WithSSRFGuard installs it on a Verifier.
func Optional ¶
Optional validates when Authorization is present; otherwise passes through. Gin hosts: use the gin-native authkitgin.Optional (adapters/gin) instead of hand-wrapping this.
func RequirePermission ¶ added in v0.65.0
func RequirePermission(checker PermissionChecker, perm authkit.Perm, resolve func(*http.Request) PermissionScope) func(http.Handler) http.Handler
RequirePermission authorizes the resolved group once and places that exact scope in the request context for the downstream handler. Missing resolution or any permission-check error denies. Unbound delegated authority is scope-free.
func Required ¶
Required validates the Bearer token (JWT), enforces iss/aud/exp, and stores claims in request context. Gin hosts: use the gin-native authkitgin.Required (adapters/gin) instead of hand-wrapping this.
func RequiredLive ¶ added in v0.92.0
RequiredLive is Required with the per-request account-liveness gate: a banned or deleted user is rejected on their NEXT request instead of at token expiry, and the downstream handler reads fresh identity claims.
It returns ErrLivenessUnconfigured when no LivenessSource is wired. A gate that cannot perform its check is a boot-time configuration error, refused before it reaches the route table rather than degraded to a weaker gate that looks like the stronger one.
func Sensitive ¶ added in v0.54.0
func Sensitive(options ...SensitiveOptions) func(http.Handler) http.Handler
func SensitiveClaims ¶ added in v0.54.0
func SensitiveClaims(cl Claims, options ...SensitiveOptions) bool
func WithPermissionScope ¶ added in v0.98.0
func WithPermissionScope(ctx context.Context, scope PermissionScope) context.Context
WithPermissionScope carries an already authorized scope into a trusted host adapter's handler. Call only after Allow/AllowLive succeeds; this does not authorize anything itself.
Types ¶
type Claims ¶
type Claims struct {
UserID string
Email string
EmailVerified bool
Username string
DiscordUsername string
SessionID string
// DeviceKeyID is the AuthKit-issued machine credential that minted this
// access token. It is present only on device-key tokens.
DeviceKeyID string
Roles []string
Entitlements []string
AMR []string
ACR string
AuthTime time.Time
TwoFAEnrollment bool
// MFAEnrolled reports whether the user has a usable second factor enrolled
// (claim `mfa_enrolled`, stamped at issue from MFAStatus.Satisfied). The
// Sensitive() gate uses it to require 2FA from users who have it, while never
// blocking users who don't.
MFAEnrolled bool
Issuer string
UserTier string
JTI string
// A delegated access token carries the external delegated subject in
// DelegatedSubject (claim `delegated_sub`). It never carries `sub` (UserID
// stays empty), so the local-user gate does not apply.
DelegatedSubject string
// Attributes is the `attributes` claim of a delegated access token: the
// canonical app-specific ESCAPE HATCH (#75). It is an object of issuer-
// asserted, NAMESPACED, OPAQUE key/values that AuthKit transports but NEVER
// interprets —
// the semantics belong to the consuming app. Each value is in one of two
// modes (see Attribute / AttributeIsReference):
// INLINE — the value carries the full definition, e.g.
// {"tier":{"endpoints":[...],"caps":[...]}}.
// REFERENCE — the value is a short JSON string key, e.g. {"tier":"tier-1"},
// resolved against a definition the remote_application
// registered ahead of time (resolve via the attribute-def
// registry, or opt-in verify-time hydration).
// Reserved well-known keys: `tier` (opaque entitlement-tier string, surfaced
// as UserTier) and `roles` (uuid array, surfaced as DelegatedRoles).
// `documents` is forbidden here because it is a top-level signed claim.
// Everything else is free-form per consuming app. Values are kept as raw
// JSON so the receiver decodes each into its own typed schema; nil when the
// claim is absent.
Attributes map[string]json.RawMessage
// DelegatedRoles are the delegated subject's role UUIDs carried by a
// delegated access token under `attributes.roles` (a JSON array of UUID strings). They are
// extracted and validated at verify (malformed entries dropped, count
// capped) and surfaced on DelegatedPrincipal.Roles. Downstream services use
// them as e.g. budget-scope keys; authkit treats them as opaque strings.
// Nil when absent. Distinct from the native-user Roles claim, which a
// delegated token never carries.
DelegatedRoles []string
// Documents is the validated top-level `documents` claim on a delegated
// token: versioned document type -> canonical content digest. Payload schema
// and authorization remain application-owned.
Documents map[string]string
// ConfirmationCertificateSHA256 is the RFC 8705 `cnf.x5t#S256` binding of a
// delegated token, already matched against the TLS peer leaf. Nil for an
// unbound token.
ConfirmationCertificateSHA256 *[32]byte
// TokenTyp is the JOSE `typ` header value. "access+jwt" identifies an
// AuthKit user access token; "delegated-access+jwt" identifies a delegated
// access token; "remote-application-access+jwt" identifies a remote
// application access token.
TokenTyp string
// TokenType marks the credential class. Empty for ordinary user JWTs;
// "api-key" for an API-key principal. An API-key principal carries
// Permissions but no UserID, so the live-user ban/enrichment gate is skipped
// (there is no user to look up).
TokenType string
// Permissions are the app-defined permission strings an API-key principal
// carries directly — the PBAC grant. Empty for user principals. authkit
// treats permission strings as opaque.
Permissions []string
// RemoteApplicationID / RemoteApplicationSlug identify the remote_application
// authenticated by a remote application access token. Populated ONLY for
// RemoteApplicationTokenType claims, resolved server-side from the validated
// `iss` (never from a self-asserted token claim). The principal's Permissions
// carry its STORED, assigned authority.
RemoteApplicationID string
RemoteApplicationSlug string
// RemoteApplicationDomain / Tier / TrustRoot are the application's stored
// identity facts (#296), resolved server-side like ID: hosts authorize on
// an unclaimable identity (id, proven domain, root-registered issuer),
// never on the slug.
RemoteApplicationDomain string
RemoteApplicationTier string
RemoteApplicationTrustRoot string
// Machine authority is resolved live from the receiving AuthKit deployment.
// Names are presentation only; UUID and authority issuer fence ownership.
PermissionGroupID string
PermissionGroupAuthorityIssuer string
PermissionGroupPersona string
PermissionGroupInstance string
}
Claims is a typed view of authenticated user information attached by middleware.
func (Claims) Attribute ¶
func (c Claims) Attribute(key string) (json.RawMessage, bool)
Attribute returns the raw JSON value of a single delegated-access-token attribute and whether it was present. The value is opaque (#75): the caller decides whether it is an INLINE definition (a JSON object/array) or a REFERENCE (a JSON string key) — see AttributeIsReference / AttributeReference.
func (Claims) AttributeIsReference ¶
AttributeIsReference reports whether attribute `key` is a REFERENCE (JSON string) rather than an INLINE definition. Convenience over AttributeReference.
func (Claims) AttributeReference ¶
AttributeReference reports whether attribute `key` is in REFERENCE mode (a JSON string the consumer resolves against the remote_application's registered definition) and returns the reference key. ok is false for INLINE values (objects/arrays/other) or an absent key. This is the ref-vs-inline detector the consumer uses before resolving against the attribute-def registry.
func (Claims) AuthenticatedWithin ¶ added in v0.52.0
func (Claims) BoundToPermissionGroup ¶ added in v0.83.0
BoundToPermissionGroup reports whether these claims carry an owning permission-group binding (#248) — true for machine principals (API keys, remote-application access tokens) whose authority was resolved server-side from a specific group instance; false for user and delegated tokens.
func (Claims) Delegated ¶
func (c Claims) Delegated() (DelegatedPrincipal, bool)
Delegated returns the typed DelegatedPrincipal when the claims are delegated.
func (Claims) DelegatedAccess ¶
func (c Claims) DelegatedAccess() (DelegatedPrincipal, bool)
DelegatedAccess is the canonical accessor for a delegated access token's principal. It returns the typed DelegatedPrincipal and true only when the claims are a delegated access token (see IsDelegatedAccessToken).
func (Claims) DocumentReference ¶ added in v0.86.0
DocumentReference returns one validated typed document reference carried by these claims. It does not fetch or interpret the referenced payload.
func (Claims) HasEntitlement ¶
func (Claims) HasPermission ¶
HasPermission reports whether the claims carry a permission token covering the requested concrete permission.
func (Claims) IsDelegatedAccessToken ¶
IsDelegatedAccessToken reports whether these claims represent a delegated access token. The canonical signal is the `typ=delegated-access+jwt` JOSE header plus a delegated subject and no local user subject.
func (Claims) IsUser ¶ added in v0.72.0
IsUser reports whether these claims represent a native human user.
func (Claims) PermissionGroupAllows ¶ added in v0.83.0
func (c Claims) PermissionGroupAllows(scope PermissionScope) bool
PermissionGroupAllows compares immutable ownership. Missing binding fields on a machine principal deny; an old spelling cannot transfer authority.
func (Claims) Principal ¶ added in v0.72.0
Principal returns the small generic-auth shape for host adapters.
func (Claims) PrincipalKind ¶ added in v0.72.0
func (c Claims) PrincipalKind() authkit.PrincipalKind
PrincipalKind reports the broad credential class represented by these claims.
type DelegatedPrincipal ¶
type DelegatedPrincipal struct {
// Issuer is the validated token issuer the receiving service trusts.
Issuer string
DelegatedSubject string
// Permissions are the resource-defined permission strings the receiving
// service authorizes against its own catalog. This is the authority source.
Permissions []string
// Attributes is the issuer-asserted escape-hatch bag (#75): namespaced,
// OPAQUE, consumer-interpreted key/values, each INLINE or REFERENCE (see
// Claims.Attributes / Claims.AttributeReference). Reserved keys: `tier`
// (-> UserTier) and `roles` (-> Roles); `documents` is forbidden because it
// is a top-level signed claim. Raw JSON values.
Attributes map[string]json.RawMessage
// Documents are exact typed signed-document references carried by the token.
Documents map[string]string
// ConfirmationCertificateSHA256 is the verified certificate binding; nil
// when the token is an unbound bearer.
ConfirmationCertificateSHA256 *[32]byte
// JTI is the token identifier (`jti` claim), when present.
JTI string
// UserTier is the resolved tier, sourced from `attributes.tier`.
UserTier string
// Roles are the actor's role UUID strings, sourced from `attributes.roles`
// (each validated as a well-formed UUID at verify; malformed entries are
// dropped, count is capped). Kept as strings so consumers parse to uuid
// without forcing a uuid dependency on the principal. Nil when absent.
Roles []string
}
DelegatedPrincipal is the identity carried by a delegated access token: an external actor (DelegatedSubject) whose authority is bounded by the VALIDATED Issuer plus Permissions. The subject does NOT exist as a local user in the validating service — authorization is by issuer trust plus Permissions, not local-user lookup.
func (DelegatedPrincipal) DocumentReference ¶ added in v0.86.0
func (p DelegatedPrincipal) DocumentReference(documentType string) (documents.Reference, bool)
type Enricher ¶
type Enricher interface {
ResolveAPIKeyDetailed(ctx context.Context, keyID, secret string) (authkit.ResolvedAPIKey, error)
GetRemoteApplication(ctx context.Context, issuer string) (*authkit.RemoteApplication, error)
ListEnabledRemoteApplications(ctx context.Context) ([]authkit.RemoteApplication, error)
ResolveRemoteApplicationAuthority(ctx context.Context, appID string) (authkit.RemoteApplicationAuthority, error)
}
Enricher is the optional, DB-backed hook surface the Verifier and middleware use for best-effort enrichment (roles/email/provider username), the live-user ban/deleted gate, opaque API-key resolution, and remote_application + attribute lookups. *authkit.Service satisfies it. The Verifier holds this as an INTERFACE (not *authkit.Service) so the verification layer carries no hard dependency on core's storage stack — a verify-only consumer can leave it nil or supply a lightweight implementation (#110).
type FederationStats ¶ added in v0.98.0
type FederationStats struct {
Snapshot int // enabled issuers in the last snapshot
SnapshotAt time.Time // when it was taken (zero: never)
Negative int // snapshot members whose registration recently failed
InFlight int // issuers currently being registered
}
FederationStats is a point-in-time view of the verifier's remote-application lazy-load state, for diagnostics and tests. Every count is bounded by the number of enabled remote applications, never by request traffic.
type IssuerOptions ¶
type IssuerOptions struct {
// JWKSURI is the URL to fetch JWKS from. If set, keys are fetched
// automatically and refreshed when they expire or an unknown kid appears.
JWKSURI string
// Keys are pre-provided public keys as PEM. The caller is responsible for
// refreshing by calling AddIssuer again with updated keys.
Keys []IssuerKey
// RawKeys are pre-provided public keys (e.g., from a co-located authkit.Service).
RawKeys map[string]crypto.PublicKey
// CacheTTL controls how long fetched JWKS keys are considered fresh.
// Default: 10 minutes.
CacheTTL time.Duration
// MaxStale controls how long stale keys may be used as fallback after
// a failed JWKS refresh. Default: 1 hour.
MaxStale time.Duration
// RemoteApplicationSlug is the receiver-internal remote-application slug
// registered for this issuer. Tokens do not self-assert this value; it comes
// only from the trusted issuer registry.
RemoteApplicationSlug string
// IsLocal marks this issuer as the host application's own (first-party) token
// signer, as opposed to a remote_application/federated issuer. It guards the
// signing-key registry against a non-local registration overwriting the local
// issuer entry (AK-AUTH-01); it does not change how claims are parsed.
IsLocal bool
}
IssuerOptions configures how keys are obtained for an issuer. Provide one of JWKSURI, Keys, or RawKeys.
type LivenessSource ¶ added in v0.92.0
type LivenessSource interface {
UserLivenessByIDs(ctx context.Context, ids []string) (map[string]authkit.UserLiveness, error)
}
LivenessSource resolves account liveness — and the identity fields that are fresh as of that same lookup — for verified user principals (#267). authkit.Client satisfies it, embedded or remote, so wiring is `v.WithLiveness(client)`; verify declares the port rather than importing the engine, exactly as it does for PermissionChecker.
type PermissionChecker ¶ added in v0.65.0
type PermissionChecker interface {
CanOnGroup(ctx context.Context, subject authkit.Subject, groupID string, perm authkit.Perm) (bool, error)
}
PermissionChecker checks live authority on an already resolved immutable group. Hosts resolve a name once at their request boundary and reuse its GroupID.
type PermissionScope ¶ added in v0.65.0
type PermissionScope struct {
GroupID string
AuthorityIssuer string
Persona authkit.Persona
Instance string
}
PermissionScope is a trusted request resolution. GroupID and AuthorityIssuer identify ownership; Persona and Instance describe its canonical public name.
func PermissionScopeFromContext ¶ added in v0.98.0
func PermissionScopeFromContext(ctx context.Context) (PermissionScope, bool)
PermissionScopeFromContext returns the exact group authorized by middleware, so a domain handler does not resolve the mutable path a second time.
type PermissionValidator ¶
PermissionValidator validates a delegated access token's `permissions` against the receiving service's own permissions. Return an error to reject the token. Called only for delegated access tokens.
type RemoteApplicationSource ¶
type RemoteApplicationSource interface {
ListEnabledRemoteApplications(ctx context.Context) ([]authkit.RemoteApplication, error)
// GetRemoteApplication fetches a SINGLE remote_application by its issuer,
// used after signature verification to resolve a service principal
// (remoteApplication). The lazy-load-on-miss path never calls it: it answers
// from the ListEnabledRemoteApplications snapshot (ak#297). *authkit.Service already
// implements this.
GetRemoteApplication(ctx context.Context, issuer string) (*authkit.RemoteApplication, error)
}
RemoteApplicationSource is the minimal store contract the Verifier needs to load remote_application principals (#74). *authkit.Service satisfies it. An embedding app may supply its own implementation in tests.
type SensitiveOptions ¶ added in v0.54.0
type ServiceJWTVerifyOption ¶
type ServiceJWTVerifyOption func(*serviceJWTVerifyConfig)
ServiceJWTVerifyOption configures VerifyServiceJWT.
func WithServiceJWTMaxLifetime ¶
func WithServiceJWTMaxLifetime(d time.Duration) ServiceJWTVerifyOption
WithServiceJWTMaxLifetime caps accepted service-JWT lifetime. Empty defaults to AuthKit's 15-minute service-JWT lifetime.
type Verifier ¶
type Verifier struct {
// contains filtered or unexported fields
}
Verifier validates JWTs from one or more issuers.
For verify-only mode, create with NewVerifier and add issuers via AddIssuer. For issuing mode, authhttp.Service creates a Verifier internally.
func NewVerifier ¶
func NewVerifier(opts ...VerifierOption) *Verifier
NewVerifier creates a new Verifier. Add trusted issuers via AddIssuer.
func (*Verifier) AddIssuer ¶
func (v *Verifier) AddIssuer(issuerID string, audiences []string, opts IssuerOptions) error
AddIssuer registers (or updates) a trusted issuer. This is the single method for adding any issuer — whether at startup or at runtime, whether keys come from a JWKS URL or are pre-provided.
func (*Verifier) AddMFAEnrollmentExemptRoutes ¶ added in v0.98.0
AddMFAEnrollmentExemptRoutes registers ANCHORED exempt paths (mount prefix + route path), matched exactly. authhttp.MountHandler calls it with the prefix it mounted under; once any anchored route is registered the suffix match of SetMFAEnrollmentExemptPaths is no longer consulted, so a host route that merely ends in "/user/2fa" cannot be reached with an enrollment-only token (ak#324). The suffix form remains for verify-only consumers that never mount.
func (*Verifier) AllowLive ¶ added in v0.92.0
func (v *Verifier) AllowLive(ctx context.Context, checker PermissionChecker, cl Claims, perm authkit.Perm, scope PermissionScope) (bool, error)
AllowLive is Allow with the account-liveness precondition: "this account is live AND holds perm", in one call.
It exists because both consumer hosts had independently written that conjunction by hand, each bolting a liveness lookup in front of verify.Allow — two gates a caller could get out of order, or forget one half of. A banned user who still holds a permission assignment must be denied, and that ordering is now the library's to guarantee, not the host's to remember.
Fail-closed throughout: a liveness error, a dead account, or a Can error all deny (the error is returned; callers must deny on a non-nil error).
func (*Verifier) FederationStats ¶ added in v0.98.0
func (v *Verifier) FederationStats() FederationStats
func (*Verifier) HTTPClient ¶
HTTPClient returns the outbound HTTP client the Verifier uses for JWKS fetches (the WithHTTPClient override, or the default timeout-bounded client).
func (*Verifier) HasLiveness ¶ added in v0.92.0
HasLiveness reports whether a LivenessSource is wired. Hosts that mount a liveness-gated route set conditionally can assert this at boot instead of discovering the gap on the first request.
func (*Verifier) IsLive ¶ added in v0.92.0
IsLive reports whether cl's principal is a live account, and returns the fresh identity fields alongside the verdict. It is the programmatic predicate behind VerifyRequestLive, for gates that already hold verified Claims and are not driving an HTTP pipeline.
Non-user principals (no UserID) are live by definition here — their liveness lives on their own credential — and come back with a zero UserLiveness. Fail-closed: an error, or an id the directory does not return, is false.
func (*Verifier) LoadRemoteApplications ¶
func (v *Verifier) LoadRemoteApplications(ctx context.Context, src RemoteApplicationSource, audiences []string) error
LoadRemoteApplications loads the ACTIVE remote_applications from authkit's OWN store (the remote_applications table) and registers each as a trusted issuer via AddIssuer with its JWKS URL. The Verifier's in-house JWKS fetch/refresh then handles the keys — there is NO external push or sync of keys.
audiences, when non-empty, is applied to every loaded issuer (typically this resource server's own audience). Call this at startup, and re-call (e.g. on a ticker, or after an inbound registration) to pick up store changes. Pass the embedding app's authkit.Service (or any RemoteApplicationSource); if nil, the Service provided via WithService is used.
func (*Verifier) RemoveIssuer ¶
RemoveIssuer removes a previously added issuer.
func (*Verifier) SetMFAEnrollmentExemptPaths ¶ added in v0.79.0
SetMFAEnrollmentExemptPaths installs the set of route paths that stay reachable to a request blocked by the requireMFAEnrollment gate or carrying a TwoFAEnrollment-only token (#243): the 2FA enroll/challenge/verify surface. AuthKit's server derives this set from its authoritative route registry (authhttp.RouteSpec.MFAEnrollmentExempt) at construction, so a renamed or added enroll route can't silently drift out of the allowlist. A Verifier that never calls this (verify-only, no WithRequireMFAEnrollment) exempts nothing. Paths are suffix-matched against the incoming request path, since AuthKit routes are prefix-neutral (a host may mount them under any prefix).
func (*Verifier) SetRemoteApplicationSource ¶
func (v *Verifier) SetRemoteApplicationSource(src RemoteApplicationSource)
SetRemoteApplicationSource overrides the federation source consulted by the lazy-load-on-miss path (keyForToken). LoadRemoteApplications is the normal way to set it; this is the explicit seam for tests and advanced wiring.
func (*Verifier) ValidateDocumentIssuer ¶ added in v0.86.0
ValidateDocumentIssuer performs the resolver's pre-network trust check. A registered issuer is accepted; a configured remote-application source gets the same bounded lazy-load-on-first-use behavior as token verification.
func (*Verifier) Verify ¶
Verify parses + verifies a token and returns typed Claims. It enforces issuer/audience/expiry with the configured skew, plus authkit's user-token invariant, on top of VerifyClaims. ctx bounds every key lookup the verification needs (JWKS fetch, lazy issuer load, remote-application resolution); a cancelled ctx aborts them. It is detached from any request, so a certificate-bound delegated token (cnf) fails with ErrSenderProofRequired here; verify those through VerifyRequest.
func (*Verifier) VerifyClaims ¶
VerifyClaims parses and cryptographically verifies a token against the registered issuers and returns its RAW validated claims. It performs the generic, token-type-agnostic checks: JWKS key resolution + signature, issuer must be registered, audience match, and exp/nbf/iat with the configured skew. It does NOT apply authkit's user-token semantics (the sub/delegated_sub invariant) or map into the typed Claims struct.
Use it to verify CUSTOM token types (e.g. a host application's capability tokens) that should reuse authkit's single JWKS engine — registry, caching, rotation, lazy-load — while carrying their own claim shape. The caller registers the token's issuer via AddIssuer and parses the returned MapClaims itself. Verify() is built on top of this for authkit's own user tokens.
func (*Verifier) VerifyDelegatedAccess ¶
func (v *Verifier) VerifyDelegatedAccess(ctx context.Context, tokenStr string) (Claims, DelegatedPrincipal, error)
VerifyDelegatedAccess verifies a token, requires it to be a delegated access token, and runs any configured permission/attributes validators. It returns the typed Claims and the DelegatedPrincipal. Use it on resource servers that only accept delegated access tokens and want catalog/policy enforcement.
func (*Verifier) VerifyDelegatedAccessRequest ¶ added in v0.98.0
func (v *Verifier) VerifyDelegatedAccessRequest(r *http.Request) (Claims, DelegatedPrincipal, error)
VerifyDelegatedAccessRequest is VerifyDelegatedAccess bound to the request's bearer token and TLS peer certificate, the only path that accepts a certificate-bound (cnf) delegated token.
func (*Verifier) VerifyDocument ¶ added in v0.86.0
func (v *Verifier) VerifyDocument(ctx context.Context, document documents.SignedDocument, expected documents.VerifyOptions) (documents.Envelope, error)
VerifyDocument verifies exact-byte digest, strict envelope metadata, trusted issuer/key resolution, JOSE profile, and signature. It never decodes the application-owned Envelope.Payload.
func (*Verifier) VerifyRequest ¶ added in v0.65.0
VerifyRequest runs the full Required authentication pipeline — bearer parse, API-key resolution, JWT verify, 2FA gate, and (for delegated principals) the fail-closed issuer gate — and returns the claims WITHOUT writing a response. Embedders that authenticate a request outside the middleware chain call this instead of driving Required against a throwaway ResponseWriter. The native-user path is stateless: it does ZERO DB lookups (#215) — no ban gate, no role/email/provider re-enrichment. Ban/deleted is enforced at token mint (login + refresh); the short access TTL bounds the residual window (#90).
That statelessness is now an explicit OPT-OUT, not the only option (#267): a privileged surface that cannot accept the residual window calls VerifyRequestLive (or mounts RequiredLive) and gets the same pipeline plus a per-request account-liveness gate and fresh identity claims. Choose this one deliberately — for genuinely stateless verifiers, and for read paths where a ≤1-TTL window is acceptable.
func (*Verifier) VerifyRequestLive ¶ added in v0.92.0
VerifyRequestLive is VerifyRequest plus a per-request account-liveness gate: the stateful twin of the deliberately stateless default (#215/#267).
It exists because the stateless path leaves a banned or deleted user holding a syntactically valid token until it expires, and every privileged host surface was hand-rolling the same gate around VerifyRequest to close that window — one of them calling the ADMIN directory per request just to refresh a username and email onto the claims. Both of those are this method's job now.
Behaviour:
- Everything VerifyRequest enforces (bearer parse, API-key resolution, JWT verify, 2FA gates, the delegated issuer gate) runs first, unchanged.
- Only NATIVE USER principals are liveness-checked. An API key resolves its secret live on every request already, and a delegated principal is gated on its remote application being enabled; neither carries a UserID, and inventing a lookup for them would be a second gate, not a stronger one.
- FAIL-CLOSED is the only posture. A lookup error, an id the directory does not return, or a not-Allowed verdict all deny with 401. There is no option to fall back to the stateless answer: a gate that opens when its dependency is down is not a gate.
- The returned Claims carry the FRESH Username, Email and EmailVerified from that same lookup, overwriting whatever the token minted — including overwriting with empty, which is the honest answer for a user who cleared the field. This is what makes a host's per-request AdminGetUser call deletable. Roles and entitlements are deliberately NOT re-enriched here: they already have live reads of their own (RoleSlugsByUsers, Allow, ListEntitlements) and a second copy would be the duplication this issue is removing, not another one of it.
CACHING CONTRACT: none. Exactly one UserLivenessByIDs call per gated request, no memoization, no negative cache. That is not a regression — the hosts this replaces each did one lookup per request — and it is the only version of the contract that can be stated honestly, because any cache reintroduces exactly the stale-authorization window the gate exists to close. A deployment that decides it wants that trade implements LivenessSource itself and owns the staleness window explicitly, rather than inheriting one from a library default.
Compose with permission checks rather than duplicating them: this answers "is this account live", RequirePermission/Allow answer "may it do this".
func (*Verifier) VerifyServiceJWT ¶
func (v *Verifier) VerifyServiceJWT(ctx context.Context, tokenStr string, opts ...ServiceJWTVerifyOption) (authkit.ServiceJWTClaims, error)
VerifyServiceJWT verifies a first-party OIDC service JWT through the verifier's registered issuer/JWKS store and returns the requested permissions. AuthKit does not grant those permissions; the host must intersect them with server-side grants for the issuer/subject/resource.
func (*Verifier) WithLiveness ¶ added in v0.92.0
func (v *Verifier) WithLiveness(src LivenessSource) *Verifier
WithLiveness wires the account-liveness backend used by VerifyRequestLive and the RequiredLive middlewares. Pass the authkit.Client the host already holds.
func (*Verifier) WithService ¶
WithService wires the engine as the API-key/remote-application resolution backend and as the default remote-application source for lazy-load-on-miss (see keyForToken). (#215/#220: the former per-request roles/provider-username enrichment is gone — the request path is stateless.) *embedded.Client's underlying service satisfies Enricher.
type VerifierOption ¶
type VerifierOption func(*Verifier)
VerifierOption configures a Verifier.
func WithAPIKeyPrefix ¶
func WithAPIKeyPrefix(prefix string) VerifierOption
WithAPIKeyPrefix sets the host application's API-key brand prefix used to detect opaque shared-secret API keys in the middleware. Empty -> bare "st_".
func WithAlgorithms ¶
func WithAlgorithms(algs ...string) VerifierOption
WithAlgorithms REPLACES the allowed JWS algorithm set; it does not add to it.
The default is ["RS256", "ES256", "ES384", "ES512", "EdDSA"], not ["RS256"]. The breadth is deliberate: federated and remote-application issuers legitimately sign with EC or Ed25519 keys and must verify out of the box. Narrow it only if you control every issuer this Verifier accepts.
The list is a pure allow-list checked in keyForToken, so "none" and the symmetric HS* algorithms are absent from the default and rejected there. A caller who adds them anyway does not open an algorithm-confusion hole: authkit only ever hands the parser an asymmetric public key, which golang-jwt's HMAC and none signing methods refuse as the wrong key type.
Passing an empty list rejects every token (fail closed).
func WithHTTPClient ¶
func WithHTTPClient(c *http.Client) VerifierOption
WithHTTPClient sets the HTTP client used for JWKS fetching.
func WithPermissions ¶
func WithPermissions(fn PermissionValidator) VerifierOption
WithPermissions installs a validator that VerifyDelegatedAccess runs against the token's `permissions`. Use it to ensure every permission string belongs to this resource server's permissions.
func WithRemoteApplicationAudiences ¶ added in v0.98.0
func WithRemoteApplicationAudiences(audiences ...string) VerifierOption
WithSkew sets the clock skew tolerance for exp/nbf/iat checks. Default: 60s. WithRemoteApplicationAudiences sets the audiences a lazily-loaded remote application issuer is registered with on the keyForToken miss path when the host never calls LoadRemoteApplications (which overrides it). NewServer passes Config.Token.ExpectedAudiences so both load paths enforce the same audience (ak#324).
func WithRequireMFAEnrollment ¶ added in v0.72.0
func WithRequireMFAEnrollment(require bool) VerifierOption
WithRequireMFAEnrollment enables the per-request forced-enrollment gate (#148): when 2FA policy is Required, a native-user request whose token shows the user is not yet enrolled (mfa_enrolled absent) is rejected with 2fa_enrollment_required unless it targets a 2FA enroll/challenge route. This makes Required gate the SESSION — every existing un-enrolled user is challenged on their next request, not just new signups. Set by the AuthKit server from TwoFactor.Mode; verify-only resource servers leave it off.
func WithSSRFGuard ¶
func WithSSRFGuard() VerifierOption
WithSSRFGuard installs NewSSRFGuardedClient as the JWKS client: DNS is resolved first and any private/reserved answer is refused. Use it on Verifiers that fetch JWKS from user-registered (remote_application) issuers.
func WithSkew ¶
func WithSkew(d time.Duration) VerifierOption