2.4.0-rc.23
Pre-release
Pre-release
Release candidate for 2.4.0.
Since 2.4.0-rc.22
Security
- A logged-out subject can no longer seed new delegations (#776) — token exchange verified a
subject_token's signature, issuer and type and nothing else. The resulting delegated token is bound to a third-partyresourceand validated by that server, which has no view of this session store, so after a logout the user's agent kept minting fresh, externally valid credentials until the subject token expired. Exchange now checks the subject session's liveness when the token carries asid. Service-account subjects stay exempt — aclient_credentialstoken has no browser session. Same opaqueinvalid_grantas the invalid-subject path, so the endpoint is not a sign-in oracle. Ships with audit + metrics on all fourteen previously-silent exchange refusal paths (token.exchange_failed,authorizer_auth_events_total{event=token_exchange},token_exchange_rejected), plus meters for the agent scope ceiling and delegated-token validation refusals. - Revocation is now visible at
/oauth/introspectand/oauth/revoke(#774) — the session entry is this codebase's only revocation record, and neither token endpoint consulted it. Introspection answeredactive:truefor tokensValidateAccessTokenalready rejected, disclosingsub,scopeandaudwith it (RFC 7662 §2.2); any resource server trusting the endpoint accepted a logged-out token for the rest of its TTL./oauth/revokeadvertisedtoken_type_hint=access_tokenbut only ever looked up refresh tokens, returning the mandated 200 having revoked nothing — the hint now orders the lookup rather than restricting it (RFC 7009 §2.1).id_tokenis carved out by type; it is never registered in the store. - Two reachable IANA special-purpose ranges added to the SSRF block list (#781) —
198.18.0.0/15and192.88.99.0/24. The former is the one with real exposure: not being globally routable is exactly why organisations use it internally, so an SSRF there reaches a live internal service through an address that reads as public to every "is this private?" check, including this one. Found while testing the JWKS-fetch path: a container on a198.18.0.0/24docker network was fetched successfully through the unmodified guard. - Spec-defined outbound fetches refuse redirects (#775) — backchannel logout and client-metadata retrieval must reach the resource AT the URL named. Webhook delivery and the admin test-endpoint probe deliberately still follow redirects; those URLs are the operator's own. Also fixes
cacheTTLparsing, wherefmt.Sscanfacceptedmax-age=600junkas 600 (the clamp made every outcome safe, so no live defect).
Added
/mcpaccepts RFC 8693 delegated tokens (#773) — an agent could not ask Authorizer about its own delegated authority:check_permissionswas unreachable with the very token that proves the delegation, so agent delegation over MCP stayed a stdio-only story. The fallback is gated on theactclaim and goes through a separate entry point, so audience matching stays exact in both directions: a token bound to the bare server URL is still refused at/mcp, and an MCP-bound one is still refused everywhere else.
Fixed
- Workload identity: issuer enums are allow-listed, SPIFFE labelled preview (#777).
- Correct false comments, remove dead code, wire missing telemetry (#779).
Tests
- Kubernetes workload identity pinned against a real cluster (#778) and e2e coverage of workload identity against a real JWKS fetch (#782).
Upgrading from 2.3.x
--url is required — the server refuses to start without it. It is not --allowed-origins: --url is this server's own address, --allowed-origins is the apps it may redirect to. See Trusted base URL.
--encryption-key is required for RS*/ES* deployments.
Full detail in CHANGELOG.md.