JWT Signing-Key Rotation Without Trust Gaps
Design key overlap, bounded refresh, and compromise recovery as separate operating procedures.
Signing-key rotation is often described as publishing a new key and removing the old one. In a distributed system, verifiers cache keys, requests remain in flight, and tokens live longer than the deployment that issued them. Rotation is a coordinated state transition.
I would separate planned rotation from compromise recovery because they optimize for different outcomes. Planned rotation preserves valid sessions. Compromise recovery may need to invalidate them immediately.
Define the acceptance contract first
Each verifier needs a configured issuer, expected audience, allowed algorithms, required claims, and a trusted source of verification keys. Treat kid as a selector within that trusted key set, not permission to fetch an arbitrary URL from a token.
RFC 8725 provides the baseline for algorithm verification and issuer and audience validation. Rotation should preserve those checks; it should not temporarily relax them to make a rollout easier.
For planned rotation, I would use this sequence:
- Publish the new public verification key while the old signing key remains active.
- Confirm verifiers can discover the new key within their bounded refresh interval.
- Begin signing new tokens with the new key.
- Keep the old verification key for the maximum remaining old-token lifetime, including permitted clock skew.
- Remove the old key and verify that old tokens are rejected.
The private old key should stop signing at the planned cutover. Public-key overlap does not require continuing to issue old-key tokens.
Unknown key identifiers are an abuse path
A verifier that refreshes its key set for every unknown kid can be induced to flood the issuer. Bound refresh frequency and concurrent refreshes, cache negative results briefly, and cap the number and size of accepted keys.
A cached known key can continue to validate tokens during a temporary discovery outage according to the defined freshness policy. An unknown key should not trigger acceptance without verification. Explicitly test the case where the new signing key becomes active before one verifier has refreshed.
Compromise changes the objective
If a signing key is stolen, waiting for ordinary token expiration leaves forged tokens usable. Recovery may require pushing a deny rule for the compromised key, forcing key-cache refreshes, disabling affected sessions, or using an additional server-side revocation boundary. The architecture determines how quickly those actions can take effect.
I would measure time from the containment decision to the last successful use of a token signed by the compromised key. “Key removed from discovery” is an intermediate action, not proof of containment.
Identity engineering owns issuance and key custody. Service owners own verifier behavior. Incident leadership owns the decision to trade session continuity for faster containment. Rehearsing that decision with test keys exposes compatibility gaps before a real compromise creates pressure to bypass validation.