DEV Community

Autional
Autional

Posted on

How JWT Signing Keys Are Issued and Rotated (and Why 'alg' Deserves Distrust)

Someone forwards a coupon that looks like it came from your store — same name, same styling. It’s fake. A JWT has the exact same problem: what makes it real is not what it says, but who could produce its signature.

A JWT is three parts

header.payload.signature

The signature is computed over header + payload. Change a single byte and it no longer matches — that’s the whole point.

Keys are a pair

  • Private key — used to sign. There is exactly one, and it never leaves the issuer.
  • Public key — used to verify. It’s published, so anyone can check a signature.

So: only the issuer can produce a signature, but everyone can verify one. The public key can verify — it cannot forge.

Publishing keys: JWKS + kid

Verifiers fetch the public keys from a JWKS endpoint (/.well-known/jwks.json). Each key carries a kid (key id), and each issued token declares which kid signed it. That’s how a verifier picks the right public key.

Rotation: overlap or you break everything

When you rotate keys, publish the new public key first and keep the old one alongside until every token signed by the old key has expired. Pull the old key too early and every live token becomes invalid at once.

Verify with a fixed algorithm — never trust the token

Only accept signatures from an explicit algorithm allowlist (e.g. RS256/ES256). Never trust the token’s own alg header — this is the classic alg=none and algorithm-confusion footgun. RFC 8725 (JWT Best Current Practices) is explicit about it.

A real case: Storm-0558 (2023)

In 2023, a signing key that should have been scoped to one system was obtained and used to forge tokens, letting attackers read email across 20+ organizations. A single leaked signing key is a skeleton key for everything that trusts it.

Engineering practice

  • Generate keys as a pair (RSA-2048 / EC). The private key never leaves the service.
  • Publish JWKS with a kid on each key; include the kid in every token.
  • Rotate with overlap: new + old public keys published together until old tokens expire.
  • Fix the algorithm whitelist; reject none; don’t let the token tell you how to verify it.

These practices come from building Autional, an open-core identity layer — https://www.autional.com

Reference: RFC 7515 (JWS), RFC 7517 (JWK/JWKS), RFC 7518 (JWA), RFC 7519 (JWT), RFC 8725 (JWT BCP); Storm-0558 (Microsoft disclosure 2023 / CISA CSRB report 2024).

Top comments (0)