-------------------------------------------------------------------------- resource server profiles: stub,roles -------------------------------------------------------------------------- Scenario: a signing key is compromised. The issuer publishes a replacement and removes the compromised key from the JWK Set immediately. Tokens it signed are already out there with an hour left to run. 1. A token signed with stub-key-1, one hour to live: GET /api/me -> 200 jwks fetches: 1 2. Issuer rotates to stub-key-2 and retires stub-key-1. { "message": "ok", "activeKid": "stub-key-2", "publishedKids": [ "stub-key-2" ], "jwksFetches": 1 } Anyone fetching /jwks.json from this moment sees only stub-key-2. 3. From here the ONLY traffic is the leaked token. Nothing carries an unknown kid, so nothing forces a refresh. Whether the token keeps working is decided purely by whether the cached JWK Set expires. elapsed leaked jwksFetches t+0s 200 1 t+30s 200 1 t+60s 200 1 t+90s 200 1 t+120s 200 1 t+150s 200 1 t+180s 200 1 t+210s 200 1 t+240s 200 1 t+270s 200 1 t+300s 401 3 t+330s 401 4 t+360s 401 5 t+390s 401 6 t+420s 401 7 t+450s 401 8 A row that flips to 401 is the cache expiring and the retired key going away. A column of 200s is a resource server that has not noticed, and will not, until something happens to bring it a token it cannot verify.