If JWT verification starts reporting “key not found” after signing-key rotation, compare the verifier’s cached keys with the keys published by the already trusted issuer. A missing new key, a suppressed refresh, and a wrong issuer require different fixes. Do not disable signature verification to make the error disappear.

There is a second question to check before declaring the rotation complete: will this verifier stop trusting a removed key? Refreshing only when a token names an unfamiliar key can discover additions while missing removals. This guide separates those two paths for developers operating APIs and gateways that validate asymmetrically signed JWTs using a JSON Web Key Set (JWKS).

The diagnostic and timeline below are our synthesis of the linked specifications and documentation. They are not results from a live identity-provider test or a benchmark of JWT libraries.

Is the failure actually a missing key?

Classify the failure before changing cache settings:

Failure stageWhat to inspectWhat a JWKS refresh cannot establish
No eligible verification keyExpected issuer, key ID, allowed algorithm, current key set and cache stateWhether the token is authentic
Key selected, signature rejectedExact verifier configuration, token integrity and compatible key/algorithmThat accepting an invalid signature is safe
Signature accepted, claims rejectedExpected audience, issuer, token kind, expiry and application policyThat the token authorizes this request

A JWT header’s kid is a selection hint, not proof of origin. Use an issuer and discovery configuration your application already trusts. Do not make a token-supplied jku, x5u or arbitrary issuer URL into an unrestricted network destination. Keep application-controlled algorithm rules, cryptographic verification and issuer/audience checks in place. RFC 8725, sections 3.1, 3.3 and 3.8–3.12

For an MCP API, a valid signature also does not make a token intended for another resource acceptable. See the separate MCP token-audience diagnostic. For CI federation, a rejected subject claim is another separate problem covered by GitHub OIDC subject mismatches.

The issuer publishes the new key. Why does one instance still fail?

First confirm the tenant, environment and token type. Then compare a failing process with a working one. Use a small, access-controlled diagnostic record rather than logging the bearer token:

  • Configured issuer and trusted JWKS endpoint identifier.
  • Library version and effective cache/refresh settings.
  • Sanitized, length-bounded key identifier and eligible cached key IDs.
  • Last successful key-set fetch, cache age and refresh result.
  • Whether refresh was skipped, throttled, coalesced, timed out or rejected.
  • Process or region identifier, without copying customer claims or authorization headers.

These fields are a proposed troubleshooting ledger, not a standard telemetry schema. Treat even key IDs and operational topology as potentially sensitive in your environment. Retain only what the investigation needs.

If the issuer’s current published set contains the expected eligible key but a process does not, inspect its refresh path and intermediate caches. Restarting one process may temporarily change the symptom without explaining why another remains stale. If the published set does not contain the key, first investigate issuer/environment mismatch and rollout state. Repeated fetching cannot create a key the trusted endpoint does not publish.

OIDC Core describes publishing keys at jwks_uri and using an unfamiliar kid as a signal to retrieve keys again. It also describes retaining recently decommissioned signing keys for a transition period. That is planned rollover guidance, not a universal duration or an instruction to preserve trust in a compromised key. OIDC Core 1.0, errata set 2, section 10.1.1

Cache lifetime, refresh cooldown and timeout are different controls

A cache lifetime determines how long retrieved state is considered fresh. A refresh cooldown or rate limit bounds repeated retrieval attempts. A timeout bounds how long a retrieval waits. Changing one does not necessarily change the other two.

For example, current jose documentation exposes cacheMaxAge, cooldownDuration and timeoutDuration separately. Its cooldown is described relative to a successful fetch. This is a reason to inspect the options of your installed version, not copy a universal interval from another framework. jose RemoteJWKSetOptions

Unverified key IDs can arrive in arbitrary requests. A “fetch once per request” rule still permits many concurrent outbound fetches. Use the supported library/framework behavior, with bounded refresh work and deliberate concurrency handling; then test legitimate rotation during the limiting window. Auth0 recommends library-managed caching, unfamiliar-key refresh and limiting cache-miss refetches. It also ties cache duration to how quickly revoked keys must stop being accepted. Auth0 JWKS caching guidance

Keep a failed lookup rejected when no acceptable key is available. Neither a refresh error nor a rate limit justifies bypassing signature or claims validation.

Why does a removed key still work?

Consider this fictional state trace. A and B are labels, not real keys. Assume signatures and claims would otherwise pass; the table isolates key availability only.

EventIssuer’s published keysVerifier’s cached keysWhat the lookup reveals
Initial fetchAAA is known
B is publishedA, BAB is unknown; refresh may discover it
Refresh succeedsA, BA, BBoth are known
A is removed laterBA, BA is still a cache hit
A fresh set is appliedBBA is no longer available from that set

The fourth row is the important counterexample: a known-key hit does not tell you whether the issuer still publishes that key. This is reasoning about the stated fictional cache, not an assertion that every library behaves this way.

Two separate checks: a token naming new key B requires bounded discovery; cached old key A requires freshness and removal handling. Neither key lookup replaces signature and claims validation.

In words: discovery asks “can I find the new key?” Retirement asks “when will I stop using the removed key?” Check both paths, including per-key caches and how a successful fresh response without the old key replaces previously trusted state.

Do not treat a retrieval failure as equivalent to a successful response that no longer contains an eligible key. Also examine stale-on-error behavior. The current node-jwks-rsa examples document an optional cacheMaxAgeFallback and explicitly describe the availability/security trade-off of continuing to use stale keys during endpoint failures. A nominal cache TTL alone is therefore not a complete revocation-delay bound. node-jwks-rsa cache examples

Which provider behavior applies to this verifier?

Do not transfer one provider’s timing or token rules to another:

  • Okta: its custom-authorization-server guidance recommends dynamic JWKS retrieval and following HTTP Cache-Control directives. Its org authorization-server access tokens are different: the page says their public verification keys are not exposed and points to introspection. Check which server and token you use. Okta key rotation
  • Supabase: its signing-key guide describes an edge cache of ten minutes plus potentially another ten minutes in client memory; custom setups can differ. It separately states that its own products do not rely on that cache for revocation. A statement about platform enforcement is not evidence that your custom API has immediately discarded a cached key. Supabase signing keys, public-key discovery and caching

These are documentation observations checked on November 3, 2026, not measured propagation times. OIDC Core is the December 15, 2023 errata-set-2 edition; RFC 8725 is dated February 2020. Provider and main-branch library pages are living references. Confirm the installed release and current provider guidance before operational changes.

What should a safe rotation rehearsal prove?

Use an isolated fixture or staging issuer you control, with synthetic tokens and no production credentials. The following is a proposed acceptance checklist, not an experiment we executed:

  1. A newly published key becomes discoverable, including when refresh limiting is active.
  2. A fresh authoritative set that removes the old key causes the intended loss of trust across all verifier instances and cache layers.
  3. Endpoint timeouts, errors and stale fallback follow an explicit policy with observable limits.
  4. Wrong issuer, audience, algorithm, expired tokens and invalid signatures remain rejected throughout.
  5. Repeated unknown-key inputs do not cause unbounded fetch work; recovery still discovers legitimate keys.

Record expected behavior, actual timing, version and exceptions separately. Choose retirement and outage policies from your application’s risk and provider contract, not from this fictional trace. Planned overlap protects valid sessions during routine rollover; emergency compromise requires its own incident procedure. Do not revoke real keys merely to debug a cache.

Sources & scope

Primary specifications and current provider documentation, with an original fictional cache-state example. No identity provider or JWT library was tested.

  1. OpenID Connect Core 1.0, errata set 2: signing-key rotation
  2. RFC 8725: JWT Best Current Practices
  3. Auth0: JSON Web Key Sets
  4. Okta: Key rotation
  5. Supabase: JWT signing keys
  6. node-jwks-rsa: cache and fallback examples
  7. jose: RemoteJWKSetOptions
Publishing details & corrections

Prepared with AI assistance. Sources, any tests performed, and their limitations are described in the article.

No material corrections recorded for this version.

Send a correction

Back to writingEditorial standards