Documentation
¶
Overview ¶
Package authn is the listener-independent MCP token-validation core for both capabilities. It validates JWTs against caller-supplied trusted keys and opaque tokens against caller-supplied introspection results, enforces issuer / audience (canonical Culvert resource) / scope / time / tenant / capability rules, and composes token validation, sender-constraint verification and principal resolution into an immutable identity context.
It performs NO network I/O: no JWKS fetch, no HTTP introspection, no TLS handshake. Trusted keys, introspection results and observed TLS-binding material arrive as explicit inputs. It makes no allow/deny policy decision (PR-6) and never brokers a credential (PR-4) — after validation the consumer receives only the immutable identity.ResolvedContext, never a raw token.
Index ¶
- func Authenticate(req AuthRequest, cfg CapabilityAuthConfig, deps Deps, now time.Time) (*identity.ResolvedContext, error)
- func AuthenticateVerified(v *VerifiedCredential, req AuthRequest, cfg CapabilityAuthConfig, deps Deps, ...) (*identity.ResolvedContext, error)
- type AuthRequest
- type CapabilityAuthConfig
- func (c CapabilityAuthConfig) CanonicalResource() string
- func (c CapabilityAuthConfig) Capability() protocol.Capability
- func (c CapabilityAuthConfig) Limits() limits.AuthLimits
- func (c CapabilityAuthConfig) MinAssurance() identity.AssuranceLevel
- func (c CapabilityAuthConfig) SenderProfile() senderconstraint.Profile
- type CapabilityConfigInput
- type Claims
- func ValidateIntrospection(res IntrospectionResult, cfg CapabilityAuthConfig, now time.Time) (*Claims, error)
- func ValidateJWT(token string, cfg CapabilityAuthConfig, keys KeyResolver, now time.Time) (*Claims, error)
- func ValidateOpaque(token string, cfg CapabilityAuthConfig, in Introspector, now time.Time) (*Claims, error)
- type ConfigSet
- type Credential
- type Deps
- type IntrospectionResult
- type Introspector
- type KeyResolver
- type Location
- type RequestBinding
- type StaticKeyResolver
- type TokenType
- type VerifiedCredential
Constants ¶
This section is empty.
Variables ¶
This section is empty.
Functions ¶
func Authenticate ¶
func Authenticate(req AuthRequest, cfg CapabilityAuthConfig, deps Deps, now time.Time) (*identity.ResolvedContext, error)
Authenticate is the top-level composition: it rejects a forbidden credential location, validates the token (JWT or opaque), cross-checks the token ids against the caller's typed principals, verifies the required sender constraint, and resolves the immutable identity context. It returns the context or a typed rejection, and never returns the raw token.
func AuthenticateVerified ¶ added in v1.0.216
func AuthenticateVerified(v *VerifiedCredential, req AuthRequest, cfg CapabilityAuthConfig, deps Deps, now time.Time) (*identity.ResolvedContext, error)
AuthenticateVerified completes authentication for an ALREADY-VALIDATED credential: it cross-checks the caller's asserted principals against the verified token, verifies the required sender constraint, clamps assurance to what was verified, and resolves the immutable identity.
It is the second half of Authenticate, which is now DEFINED as ValidateCredential + AuthenticateVerified — so there is exactly one code path and the split cannot drift from the combined API.
Four guards make the split safe. Each fails CLOSED:
- a nil VerifiedCredential is refused (no "unverified means fine" branch);
- the credential presented in req must be byte-identical to the one that was verified — otherwise a caller could validate token A and authenticate token B (the TOCTOU / swap);
- the config identity must match the one the credential was verified against — otherwise a token verified for Gateway could be presented under the Management config, defeating capability isolation;
- the time-based claims are RE-CHECKED against the caller's `now`, so a credential verified earlier cannot be redeemed after it expires. This is free (no cryptography) and removes the staleness class entirely.
Types ¶
type AuthRequest ¶
type AuthRequest struct {
Credential Credential
Subject identity.Subject
Agent *identity.Agent
Client identity.Client
Tenant identity.Tenant
Server *registry.ServerID
Tool *identity.ToolRef
Resource *identity.ResourceRef
Binding RequestBinding
}
AuthRequest is the full input to Authenticate. The caller supplies the typed principals (which a token cannot express — e.g. Human vs Workload and their type-specific fields); Authenticate cross-checks their stable ids against the cryptographically-validated token before resolving the identity.
type CapabilityAuthConfig ¶
type CapabilityAuthConfig struct {
// contains filtered or unexported fields
}
CapabilityAuthConfig is the immutable, validated auth configuration for ONE capability. Management and Gateway carry INDEPENDENT configs; a credential, scope or resource for one must never validate for the other. All fields are caller-supplied trusted configuration.
func NewCapabilityConfig ¶
func NewCapabilityConfig(in CapabilityConfigInput) (CapabilityAuthConfig, error)
NewCapabilityConfig validates the input into an immutable CapabilityAuthConfig. It rejects a missing canonical resource, an empty issuer/scope set, a blanket scope, a wildcard scope not in the explicit wildcard allowlist, and a fail-open (zero) sender profile.
func (CapabilityAuthConfig) CanonicalResource ¶
func (c CapabilityAuthConfig) CanonicalResource() string
CanonicalResource returns the exact expected canonical Culvert resource.
func (CapabilityAuthConfig) Capability ¶
func (c CapabilityAuthConfig) Capability() protocol.Capability
Capability returns the surface this config governs.
func (CapabilityAuthConfig) Limits ¶
func (c CapabilityAuthConfig) Limits() limits.AuthLimits
Limits returns the auth bounds.
func (CapabilityAuthConfig) MinAssurance ¶ added in v1.0.216
func (c CapabilityAuthConfig) MinAssurance() identity.AssuranceLevel
MinAssurance returns the capability's minimum-assurance floor. It is exposed so a composition root that knows its own SUBJECT MODEL can prove the (profile, floor) pair is satisfiable before it binds a listener — authn itself cannot, because an attested workload reaches High under any profile (see effectiveAssurance).
func (CapabilityAuthConfig) SenderProfile ¶
func (c CapabilityAuthConfig) SenderProfile() senderconstraint.Profile
SenderProfile returns the required sender-constraint profile.
type CapabilityConfigInput ¶
type CapabilityConfigInput struct {
Capability protocol.Capability
TrustedIssuers []string
AcceptedClientIDs []string
CanonicalResource string
RequiredScopes []string
AllowedScopes []string // optional additional scopes that may appear
WildcardScopes []string // the only wildcard scopes explicitly permitted
SenderProfile senderconstraint.Profile
MinAssurance identity.AssuranceLevel
Limits limits.AuthLimits
}
CapabilityConfigInput is the mutable input to NewCapabilityConfig.
type Claims ¶
type Claims struct {
Issuer string
Audiences []string
Subject string
ClientID string
Scopes []string
Tenant string
Expiry int64
HasExpiry bool
NotBefore int64
HasNbf bool
IssuedAt int64
HasIat bool
AuthTime int64
HasAuthTime bool
CnfJKT string // cnf.jkt (DPoP)
CnfX5T string // cnf["x5t#S256"] (mTLS)
HasCnf bool
}
Claims is the normalized, typed view of a token's claim set (JWT payload or opaque introspection metadata). It is extracted via ONE strict decode path (canonical.Decode → *Node), so duplicate keys, invalid UTF-8 and escaped unpaired surrogates are rejected before any claim is read. It never carries the raw token.
func ValidateIntrospection ¶
func ValidateIntrospection(res IntrospectionResult, cfg CapabilityAuthConfig, now time.Time) (*Claims, error)
ValidateIntrospection validates an already-obtained introspection result.
func ValidateJWT ¶
func ValidateJWT(token string, cfg CapabilityAuthConfig, keys KeyResolver, now time.Time) (*Claims, error)
ValidateJWT verifies a compact JWT against the capability config and the caller-supplied trusted keys, and returns its normalized Claims. It validates the compact shape, the signing-algorithm allowlist (rejecting none/HMAC/unknown and algorithm confusion), the signature, and then every claim (issuer/audience/time/TTL/scope/tenant/subject/client). It performs no network I/O. The raw token is never returned or retained.
func ValidateOpaque ¶
func ValidateOpaque(token string, cfg CapabilityAuthConfig, in Introspector, now time.Time) (*Claims, error)
ValidateOpaque introspects an opaque token (via the caller's adapter) and validates the returned metadata against the capability config. An inactive or malformed result is rejected.
type ConfigSet ¶
type ConfigSet struct {
// contains filtered or unexported fields
}
ConfigSet holds the two INDEPENDENT capability configs and proves, at construction, that Management and Gateway do not overlap on any identity-bearing value (issuer∩clientID∩resource∩scope). An overlap that violates the accepted separation contract fails construction.
func NewConfigSet ¶
func NewConfigSet(mgmt, gateway CapabilityAuthConfig) (*ConfigSet, error)
NewConfigSet validates the two configs and their non-overlap.
func (*ConfigSet) For ¶
func (s *ConfigSet) For(c protocol.Capability) CapabilityAuthConfig
For returns the config for a capability.
type Credential ¶
Credential is the presented token with its stated provenance. The raw Token is used only during validation and is never copied into the resolved context.
type Deps ¶
type Deps struct {
Keys KeyResolver
Introspector Introspector
Registry *registry.Registry
Catalog *catalog.Catalog
Replay *senderconstraint.ReplayCache
}
Deps carries the caller-supplied validation dependencies (no network I/O).
type IntrospectionResult ¶
type IntrospectionResult struct {
Active bool
Issuer string
Audiences []string
Subject string
ClientID string
Scope string // space-delimited scopes (RFC 7662 `scope`)
Tenant string
Expiry int64
HasExpiry bool
NotBefore int64
HasNbf bool
IssuedAt int64
HasIat bool
CnfJKT string
CnfX5T string
}
IntrospectionResult is normalized RFC 7662-style opaque-token metadata supplied by the caller. PR-3 validates this metadata; it never performs HTTP introspection. The caller's Introspector returns already-obtained results.
type Introspector ¶
type Introspector interface {
Introspect(token string) (IntrospectionResult, error)
}
Introspector returns normalized metadata for an opaque token. It performs NO network I/O — the caller-supplied implementation returns metadata it has already obtained. Remote introspection, caching and revocation distribution are out of scope for PR-3.
type KeyResolver ¶
type KeyResolver interface {
// ResolveKey returns the public key for (issuer, kid, alg), or an error if no
// trusted key matches. It must never fetch over the network.
ResolveKey(issuer, kid, alg string) (crypto.PublicKey, error)
}
KeyResolver resolves a JWS verification key for a token. It performs NO network I/O (no JWKS fetch): implementations serve caller-supplied trusted keys only.
type Location ¶
type Location uint8
Location is where the credential was presented. A bearer token in the query string is a forbidden location, rejected before any normal validation.
const ( // LocationUnknown — unspecified (rejected). LocationUnknown Location = iota // LocationAuthorizationHeader — the only supported bearer location. LocationAuthorizationHeader // LocationQueryString — forbidden; rejected as credential_in_query. LocationQueryString // LocationOther — any other explicitly unsupported location. LocationOther )
type RequestBinding ¶
type RequestBinding struct {
HTTPMethod string
HTTPURI string
Nonce string
DPoPProof string
ObservedCertThumbprint string
}
RequestBinding is the caller-supplied request metadata for sender-constraint verification (no network I/O). DPoPProof/HTTPMethod/HTTPURI/Nonce drive DPoP; ObservedCertThumbprint drives mTLS.
type StaticKeyResolver ¶
type StaticKeyResolver struct {
// contains filtered or unexported fields
}
StaticKeyResolver is a fixed, in-memory trusted-key set keyed by (issuer, kid). It is the caller-supplied trust anchor for JWT validation — the only source of verification keys in this listener-independent PR.
func NewStaticKeyResolver ¶
func NewStaticKeyResolver() *StaticKeyResolver
NewStaticKeyResolver returns an empty resolver.
func (*StaticKeyResolver) Add ¶
func (r *StaticKeyResolver) Add(issuer, kid string, key crypto.PublicKey)
Add registers a trusted public key for (issuer, kid).
func (*StaticKeyResolver) ResolveKey ¶
func (r *StaticKeyResolver) ResolveKey(issuer, kid, _ string) (crypto.PublicKey, error)
ResolveKey returns the trusted key for (issuer, kid), or a signature-invalid error when none is registered (an unknown key id cannot verify a signature).
type VerifiedCredential ¶ added in v1.0.216
type VerifiedCredential struct {
// contains filtered or unexported fields
}
VerifiedCredential is unforgeable proof that ONE credential was cryptographically validated against ONE CapabilityAuthConfig.
It exists to remove a genuine 2x amplification of the most expensive attacker-reachable operation (OVN-06). The observe runtime validated every request's token TWICE: once to derive the asserted principals, and again inside Authenticate, which re-validates. Measured on the live pipeline that was 2 full ECDSA P-256 verifications per request — ~96 µs of a ~206 µs authenticated request (47%), plus 8.7 KB and 206 allocations, for work whose result was already known.
Every field is unexported and the ONLY constructor is ValidateCredential, so an ordinary caller cannot fabricate one or mutate one after the fact. It carries no capability of its own: presenting it to AuthenticateVerified re-runs every non-cryptographic check (cross-check, sender constraint, assurance clamp, identity resolution) exactly as Authenticate does.
func ValidateCredential ¶ added in v1.0.216
func ValidateCredential(cred Credential, cfg CapabilityAuthConfig, deps Deps, now time.Time) (*VerifiedCredential, error)
ValidateCredential performs the ONE cryptographic validation of a credential against cfg: the forbidden-location check, then the JWT signature or opaque introspection path, then the capability policy (issuer / audience / scopes / lifetime). It makes NO identity or authorization decision.
func (*VerifiedCredential) ClientID ¶ added in v1.0.216
func (v *VerifiedCredential) ClientID() string
ClientID returns the validated `client_id` (or `azp`).
func (*VerifiedCredential) Issuer ¶ added in v1.0.216
func (v *VerifiedCredential) Issuer() string
Issuer returns the validated `iss`.
func (*VerifiedCredential) Subject ¶ added in v1.0.216
func (v *VerifiedCredential) Subject() string
Subject returns the validated `sub`.
func (*VerifiedCredential) Tenant ¶ added in v1.0.216
func (v *VerifiedCredential) Tenant() string
Tenant returns the validated tenant claim.