The vulnerability is a cross-tenant authentication bypass in Centrifugo's dynamic JWKS feature. The root cause is that the JWKS public key cache and the in-flight request coalescing (singleflight) were keyed solely by the JWT's kid (Key ID) header. The kid is not guaranteed to be globally unique, and different tenants (issuers) could use the same kid.
The analysis of the security patch (commit 15d785015c6f318c1b68ea40b813699c9f8bd2c4) confirms this. The core of the vulnerability lies in internal/jwks/manager.go within the (*Manager).FetchKey function. Before the patch, this function would look up a key in the cache using only the kid. If not found, it would fetch the key, but the singleflight mechanism to prevent concurrent fetches for the same key also used only the kid.
An attacker could exploit this by configuring two tenants with the same kid. By sending a valid token for their own tenant (Tenant A), they would poison the cache. Subsequent requests for the victim tenant (Tenant B) with a token forged by the attacker (signed with Tenant A's key but claiming to be from Tenant B) would be validated against Tenant A's key from the cache, leading to an authentication bypass.
The patch remediates this by creating a composite cache key that includes both the resolved JWKS endpoint URL and the kid. This ensures that keys are namespaced by their trust domain (i.e., the issuer's JWKS endpoint). The functions (*TTLCache).Add and (*TTLCache).Get were modified to accept this new composite key. The primary user-facing functions that trigger this vulnerable logic are (*VerifierJWT).VerifyConnectToken and (*VerifierJWT).VerifySubscribeToken, which are the main entry points for token verification.