← Recipes

阅读中文版本

Validate OIDC Tokens with JWKS

Fetch the issuer JWKS, verify the signature, check iss, aud, exp and nbf, cache keys by kid, and refresh the key set on rotation.

~45 minauthenticationoidcsecurity

Task

Verify an OpenID Connect ID token correctly instead of trusting decoded claims.

When to use this

A service decodes the JWT payload without verifying the signature, so any attacker can forge a token with an admin claim.

Prerequisites

  • A JWT library
  • The issuer discovery document or JWKS URL

Steps

1. Fetch signing keys from the issuer JWKS and cache them by kid.

Expected output: Signature verification uses the right public key.

2. Verify the signature and reject any token that fails.

Expected output: Forged tokens are rejected.

3. Validate iss, aud, exp, and nbf against the expected values.

Expected output: Tokens for other audiences or expired tokens are rejected.

4. Refresh the JWKS when a kid is unknown, and rate-limit refreshes.

curl -s https://issuer.example.com/.well-known/jwks.json | jq '.keys[].kid'

Expected output: Key rotation is handled without a fetch per request.

Troubleshooting

Problem

Unknown kid causes fetch storms

Solution

Cache the JWKS with a TTL and a single-flight refresh so one rotation does not trigger many fetches.

Problem

Alg none or HS256 confusion

Solution

Pin allowed algorithms to the asymmetric ones you expect; never accept alg none.

Limitations

  • JWKS validation proves the token is authentic; it does not by itself authorize the caller, which still needs scope checks.
  • Clock skew between issuer and verifier can cause valid tokens to fail unless small leeway is allowed.

Context

Verify an OpenID Connect ID token correctly instead of trusting decoded claims. A service decodes the JWT payload without verifying the signature, so any attacker can forge a token with an admin claim.

Steps

  1. Fetch signing keys from the issuer JWKS and cache them by kid. Expected: Signature verification uses the right public key.
  2. Verify the signature and reject any token that fails. Expected: Forged tokens are rejected.
  3. Validate iss, aud, exp, and nbf against the expected values. Expected: Tokens for other audiences or expired tokens are rejected.
  4. Refresh the JWKS when a kid is unknown, and rate-limit refreshes. Expected: Key rotation is handled without a fetch per request.
const { payload } = await jwtVerify(token, getKey, {
  issuer: 'https://issuer.example.com',
  audience: 'my-api',
  algorithms: ['RS256'],
});

Verify

  • Signature verification uses the right public key.
  • Forged tokens are rejected.
  • Tokens for other audiences or expired tokens are rejected.
  • Key rotation is handled without a fetch per request.

Limits

  • JWKS validation proves the token is authentic; it does not by itself authorize the caller, which still needs scope checks.
  • Clock skew between issuer and verifier can cause valid tokens to fail unless small leeway is allowed.