Replace the single pinned id_token_signed_response_alg with an allow-list
of asymmetric algorithms (RS*, PS*, ES*, EdDSA). Production Keycloak signs
with ES256 and the test realm with EdDSA; with the old EdDSA default every
login against production failed with "Invalid token provided" unless the
app overrode the item. The new default works for both with no config.
Upstream can only pin one algorithm (withExpectedAlg(?string)) and
IdTokenVerifier is final, so AlgAllowListIdTokenVerifier wraps it: it reads
alg from the token header, rejects anything not on the allow-list, then
pins that alg with withExpectedAlg() so upstream still registers its
AlgorithmChecker. AsymmetricIdTokenVerifierBuilder is used for login,
refresh and verifyLogoutToken(); id_token_signed_response_alg is no longer
put in the client metadata.
"none" and HS* can never be allowed - the schema and the builder
constructor both reject them. Choosing among asymmetric algorithms is
safe: a signature still needs the provider's private key, and
jwt-framework checks the key type and the JWK alg against the algorithm.
At build() the list is intersected with the provider's discovery
id_token_signing_alg_values_supported. Discovery can only narrow it; an
empty intersection fails with an explicit error instead of a generic
"Invalid token provided".
Config: new optional idTokenSigningAlgs list. idTokenSignedResponseAlg
(released in v2.0.2) keeps working as a deprecated single-alg alias and
cannot be combined with the new item.
Verified with locally generated keys: RS256, PS256, ES256 and EdDSA tokens
are accepted, HS256 signed with the client secret and alg "none" are
rejected, a tampered payload and an RS256->PS256 header swap fail on the
signature. Against the live sitmp test realm the discovery intersection
resolves to all asymmetric algs and an HS256 token is rejected. A full
browser login against Keycloak has not been run.
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>