authn

package
v1.0.217 Latest Latest
Warning

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

Go to latest
Published: Aug 25, 2026 License: MIT Imports: 18 Imported by: 0

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

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:

  1. a nil VerifiedCredential is refused (no "unverified means fine" branch);
  2. 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);
  3. 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;
  4. 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

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

For returns the config for a capability.

type Credential

type Credential struct {
	Location Location
	Type     TokenType
	Token    string
}

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 TokenType

type TokenType uint8

TokenType selects the validation path.

const (
	// TokenJWT — a signed compact JWT.
	TokenJWT TokenType = iota
	// TokenOpaque — an opaque token validated via introspection metadata.
	TokenOpaque
)

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.

Jump to

Keyboard shortcuts

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