JWKS cache and rotation failures
The messy edge cases behind JWKS caching, signing key rotation, stale kid values, and outage resistant verifier design.
JWKS looks simple from a diagram: issuer publishes keys, services cache them, tokens verify. The failures happen in the timing. A key rotates while a cache is stale. A new kid appears before every region has the JWKS update. A verifier refreshes on every miss and accidentally gives attackers a tiny denial of service lever. None of these problems require a broken signature algorithm. They come from distributed systems behaving like distributed systems.
The two bad cache policies
The first bad policy is caching forever. It makes normal rotation scary, so teams avoid rotation until an incident forces it. The second bad policy is fetching JWKS on every request or every unknown kid. That keeps keys fresh but turns the issuer into part of the hot path. An attacker can send thousands of tokens with random kid values and cause a storm of outbound fetches unless the verifier rate limits misses.
A better design keeps a normal TTL, serves from cache during issuer blips, and refreshes in the background when a new kid appears. Unknown kid should trigger at most one refresh per issuer in a short window. Other requests should fail closed using the current cache, not queue behind a remote fetch.
Rotation order matters
Publish the new public key before signing tokens with the new private key. Keep the old public key until the longest token lifetime plus clock skew has elapsed. If access tokens live fifteen minutes and clients can be five minutes off, removing the old key after ten minutes guarantees false failures. The timeline must include CDN propagation, regional config rollout, and any mobile clients that hold tokens while offline.
Signals worth tracking
Track verification failures by issuer, kid, algorithm, and reason. A spike in unknown kid after deploy is different from a spike in bad signature. Log the cache age and refresh result. During an incident, those two fields tell you whether you have a compromised key, a bad rollout, or a broken network path to the JWKS endpoint.
Testing the failure modes
A pentest should include stale JWKS behavior, not only forged tokens. Can the app still verify old tokens during rotation? Does it accept a token signed by a removed key? What happens when the JWKS endpoint returns 500 or a slow response? Defenders should rehearse rotation with a staging issuer and forced cache states. If the first real rotation happens under pressure, the runbook is already late.