Skip to content
Optimal Lab

Security · interactive

JWTs: what's inside, and what makes them safe

A JWT is three base64 strings joined by dots. Anyone can decode it and read the claims, so the only thing that makes it trustworthy is the signature check. Try forging one. (The signature here is a simplified stand-in for HMAC.)

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJ1c2VyXzQyIiwibmFtZSI6IkFkYSIsInJvbGUiOiJhZG1pbiIsImV4cCI6MTgwMDAwMzYwMH0.1hwquwc11x6m1x1wzrdvojejnyc8p2xkzu05da4

{
  "alg": "HS256",
  "typ": "JWT"
}
{
  "sub": "user_42",
  "name": "Ada",
  "role": "admin",
  "exp": 1800003600
}
  • Algorithm✓ HS256, the only one allowed
  • Signature✗ expected chqr7l1xmd…, got 1hwquwc11x…
  • Expiry✓ still valid

Rejected.

Decode, edit the payload, encode again. The old signature is kept.

Token

Server verification

Step 2 of 4

  1. 1

    Decode: anyone can read it

    The header and payload are only base64url, not encrypted. Never put secrets in a JWT.

    role: admin

  2. 2

    The signature covers header + payload

    The server signs the first two parts with its secret. Change one character and the signature no longer matches.

    Editing role without the secret breaks the signature.

  3. 3

    Pin the algorithm on the server

    Some old libraries honoured alg: none and skipped the check. Always pass an explicit allow-list of algorithms to verify().

    Allow-list: only HS256 is accepted.

  4. 4

    Check expiry, audience and issuer

    A valid signature only proves who issued it. Short expiry limits how long a stolen token works, since JWTs can't easily be revoked.

    exp, aud, iss: verify them all.

In production

Tools and technologies

JWTs carry identity between services; the libraries decide how safe they are.

Not tied to one language — use these names when you search docs or design a system.