Reading — step 1 of 5
Read
JWKS: JSON Web Key Set
Real-world auth providers (Auth0, Okta, Google, AWS Cognito, Microsoft Entra)
expose /.well-known/jwks.json. The response is a JSON Web Key Set (RFC
7517 Section 5):
{
"keys": [
{"kty":"RSA","kid":"2024-01","alg":"RS256","n":"...","e":"AQAB","use":"sig"},
{"kty":"RSA","kid":"2024-07","alg":"RS256","n":"...","e":"AQAB","use":"sig"}
]
}
Each key has a kid (key ID). When you receive a JWT, you:
- Read
header.kid. - Look it up in your cached JWKS.
- Verify with that key.
Key rotation playbook
| Phase | JWKS contains | Signer uses |
|---|---|---|
| t=0 | old | old |
| t=1 | old + new | old |
| t=2 | old + new | new |
| t=3 (after max-token-age) | new | new |
Never hard-cutover. Old tokens issued under the previous kid must still
verify until they expire.
Cache, but refresh on miss
JWKS responses can be cached for hours (typical: 1h, max-age comes from the
HTTP Cache-Control). But if you receive a JWT with an unknown kid,
refresh immediately — the auth provider rotated and you missed it.
Symmetric vs asymmetric
HS256 secrets MUST stay secret, so HS256 keys are never published in a JWKS. JWKS is for asymmetric public keys (RS256, ES256, EdDSA). We model an HS256 registry here for simplicity, but the lookup protocol is identical.
kid injection: a footgun
A naive verifier does:
kid="../../etc/passwd" reads /etc/passwd. Always treat kid as untrusted
input — restrict to [a-zA-Z0-9_-]+, or use it as a dict key, never a path.
Discussion
Ask a question, share an insight, or help someone who’s stuck.
Sign in to post a comment or reply.
Loading…