v2.2.0 — production-grade observability and audit closure
The "production-grade observability and audit closure" release. Three RFC-level features (CIMD, DPoP, observability adapters), four security closures, four perf items, +175 percentage points of direct handler coverage on two highest-blast-radius packages, and an architectural cleanup that brings theauth.go from 1,171 LOC under the 500 LOC ceiling.
Public API stays byte-stable with v2.1.0. Every addition is additive; downstream code compiles unchanged.
Highlights
Spec features
- CIMD (#42) — OAuth Client ID Metadata Documents per MCP spec 2025-11-25. Clients identify by HTTPS URL; metadata cached with TTL;
DenyAlltrust policy default (fail-closed). - DPoP (#43) — RFC 9449 sender-constrained access tokens. Stolen tokens cannot be replayed without the holder's private key. Wire via
Config.AuthorizationServer.DPoPandmcpresource.WithDPoPVerification(...).mcpresourcegains zero new transitive deps. - Observability adapters (#44) — pluggable
Tracer+Metricsinterfaces with 10 spans + 10 metrics live. Real-dep example bridges to OTel and Prometheus live inexamples/observability-{otel,prom}/with their own go.mod; root andmcpresourcego.mod gain zero new deps.
Security
- Lows L1-L3 + L5 (#45) — magic-link rate limit, PKCE constant-time compare, RevokeToken walks refresh family,
Config.RequireStateknob. - Mediums M3-M5 (#48) —
SecureCookiedeprecation warning ahead of v3.0 default flip, JWKS rotation transactional with partial unique index,mcpresource.Validator.Diagnostics()for misconfiguration warnings. - N1 regression guard —
TestSuspendAgentBustsClientAuthCacheconfirms revoked agents cannot re-authenticate via cached Argon2 entries within the 5-minute TTL.
Performance (#46)
- SCIM auth uses a single storage lookup.
keyedLimiterusessync.RWMutexplus atomiclastUsed(5-10x read-heavy throughput improvement under contention).- 5-second chain-walk cache eliminates 3+ storage calls per 3-deep chain.
- Audit redactor uses precomputed lowercase key set with
strings.EqualFold.
Tests
internal/saml/handlers: 0% to 91.4% statement coverage (#47).internal/webauthn/handlers: 0% to 84.5% (#47).internal/organizations/handlers: 0% to 94.0% (#49).internal/admin/handlers: 0% to 81.1% (#49).
Architecture (#50)
theauth.go: 1,171 LOC to 383 LOC.- Wiring extracted to
wiring.go, storage interface tostorage.go, config sub-structs toconfig.go. - OAuth provider state machine moved to
internal/oauth/service.go. internal/account/handlers/andinternal/admin/handlers/parent packages collapsed.
Fixes
InsertOAuthClientcoerces nil text[] slices to empty arrays (#40), fixingSQLSTATE 23502on fresh schemas.postgres.Migrate(ctx, pool)runtime migration helper (#32) so downstream consumers can delete their forked migration code.
See CHANGELOG.md for the full entry.