Part 2 · 2 chapters · ~12 min

JWTs and Their Pitfalls

The structure of a JWT, the validation checklist, algorithm confusion and alg=none, keys and JWKS with key ids, rotation, short expiry and revocation strategies, token size, and when an opaque token with introspection is better.

5

Structure and validation

code
// validate with a maintained library, never by hand
import { createRemoteJWKSet, jwtVerify } from 'jose';
const JWKS = createRemoteJWKSet(new URL('https://auth.bank.example/.well-known/jwks.json'));
export async function verify(token: string) {
  const { payload } = await jwtVerify(token, JWKS, {
    issuer: 'https://auth.bank.example', audience: 'ledger-api',
    algorithms: ['ES256'], clockTolerance: 30,
  });
  if (!String(payload.scope ?? '').split(' ').includes('transfers:write')) throw new Error('insufficient_scope');
  return payload;
}
A JWT, AND EVERY CHECK ON IT
header.payload.signature, and the validation list
header{"alg":"ES256","kid":"2026-10","typ":"JWT"}payloadiss, sub, aud, exp, iat, nbf, scope, tenantsignatureECDSA over base64url(header).base64url(payload)validationalg allowlist, kid → key, iss, aud, exp, nbf
swipe the figure sideways, or tap expand for full screen
1/4
header
The header names the algorithm and the key id. Never trust alg blindly: the classic attacks are alg=none and switching RS256 to HS256 using the public key as an HMAC secret.
alg and kidallowlist algorithms; never accept none
6

Revocation, size and opaque tokens

needapproach
revoke quicklyaccess tokens of 5-15 minutes; revoke the refresh token; for emergencies a small denylist of token ids (jti) checked until expiry
sensitive operationscheck a session version or "tokens issued after" timestamp per user, bumped on password change or compromise
large permission setsdo not stuff hundreds of permissions into the token; check them at an authorisation service (parts 4-5)
no shared verification desiredopaque tokens with introspection (RFC 7662): the API asks the auth server; easy revocation, a network hop per request (cache briefly)