Skip to content

2.4.0-rc.19

Pre-release
Pre-release

Choose a tag to compare

@lakhansamani lakhansamani released this 13 Aug 11:36
· 25 commits to main since this release
e035554

Security and feature release on top of 2.4.0-rc.18.

Security fixes

Social-login state injection (#760). The state carried four values through the provider round trip joined by ___, and the first of them was supplied by the caller. A caller whose state contained the delimiter shifted every later field left, so state=A___https://evil.example___admin___openid made the callback read its redirect URI from the caller's segment and its roles from the next one. The callback only checks roles against --protected-roles, so any non-protected role was accepted.

The benign face of the same bug was far more common: a caller's state is typically base64url, whose alphabet includes _, so a token that merely ended in one produced ____, split a character early, and failed with an invalid redirect URI on roughly 1 in 64 social logins, for every provider.

The fix is not a better delimiter. Nothing the caller controls travels to the provider any more — the state is an opaque random handle and the four values live server-side. There is no format left to collide with. The redirect URI, roles and scope also no longer pass through a third party at all.

TOTP recovery codes were not single-use under concurrency (#761). Redemption read the recovery-code blob, decided in Go, and wrote it back unconditionally. Concurrent redemptions of the same code all observed it unconsumed and all succeeded — measured at 3 of 8 racing attempts accepted. One leaked recovery code was therefore worth an unlimited number of MFA-bypassing logins. Racing redemptions of different codes also silently lost one another's writes.

Redemption now goes through an atomic compare-and-swap implemented natively per backend (SQL WHERE + affected rows, MongoDB filtered UpdateOne, Cassandra LWT, DynamoDB ConditionExpression, ArangoDB AQL, Couchbase document CAS). Exhausting the retries is reported as a fault, never as an invalid code — a database problem must not spend a user's recovery credential.

Consumed recovery codes could be resurrected (#762). Follow-up to the above. TOTP validation read the authenticator row, spent time on it, then wrote it back whole — carrying the recovery-code blob as it was at read time and restoring a code a concurrent redemption had just consumed. The two writers to an authenticator row now touch disjoint columns and commute.

Features

MCP over HTTP as an OAuth 2.1 resource server (#757). Opt-in via --mcp-enabled (requires --url). Serves RFC 9728 protected resource metadata, validates access tokens with audience and subject-liveness checks, and exposes an authenticated FGA decision over the MCP surface.

Client ID Metadata Documents, consent screen, and opt-in Dynamic Client Registration (#758). CIMD (draft-ietf-oauth-client-id-metadata-document-00) lets a client identify itself by a URL to its own metadata; RFC 7591 DCR is available opt-in for clients that need to self-register. Both are public-client only and PKCE is required (RFC 9700 §2.1.1). Adds a server-rendered consent screen styled to match the hosted login UI, including the operator's logo and organization name.

Upgrade notes

No breaking changes in this release. If you are upgrading from rc.15 or earlier, read the rc.16 notes first — that release carried the full 22-finding pre-release security audit.

Note that rc.15 was tagged but its build was cancelled — no rc.15 image was ever published.

Matching SDK releases

SDK Version
authorizer-js 4.0.0-rc.0 (npm rc tag)
authorizer-react 2.2.0-rc.7 (npm rc tag)
authorizer-go v2.2.0-rc.5
authorizer-py 0.3.0rc4
authorizer-proto-go v0.2.0-rc.1
authorizer-proto 0.2.0rc1