Intermediate20 min

JWT / JWS / JWE

The self-contained token format: signed claims anyone with the public key can verify.

By the end of this lesson, read a JWT, explain how its signature is verified, and avoid the classic JWT security mistakes.

How deep?
How the pieces actually move.

A JWT (JSON Web Token) is three base64url parts joined by dots: header.payload.signature. The header names the algorithm, the payload is a set of claims (JSON facts about the subject), and the signature lets a verifier confirm the token was issued by who it says and not modified.

Signed tokens are JWS. Encrypted tokens are JWE. Most bearer tokens you see are JWS — signed, not encrypted, so treat the payload as readable by anyone.

First, the whole system

First, see the whole system. Then we’ll open it up.

Issue, carry, verify

Issue, carry, verify

  1. The issuer signs a JWT with its private key.
  2. The client sends it to the API as a bearer token.
  3. The API fetches the issuer's public keys from the JWKS endpoint and verifies the signature and claims.
flowchart LR
  idp[Issuer / IdP] -->|signs with private key| jwt[JWT]
  jwt -->|Authorization: Bearer| api[API]
  api -->|fetch public keys| jwks[JWKS endpoint]
  api -->|verify signature + claims| ok[Trust subject]
Standard claims
iss (issuer), sub (subject), aud (audience — who the token is for), exp (expiry), iat (issued-at), nbf (not-before). Verifying aud and exp is not optional.
Abstraction leak

This is where the abstraction starts leaking.

The payload is signed, not secret. Anyone can decode a JWS and read every claim. Never put secrets in a JWT. And never trust a claim you didn't verify the signature for — a decoded-but-unverified token is attacker-controlled JSON.

Now break it

An API decodes the JWT to read sub but skips signature verification 'to save time'. Now craft a token with sub: admin. It works. Signature verification is the entire point.

alg=none / algorithm confusion

Trigger
A verifier accepts the token's declared algorithm, including none, or confuses RS256 (asymmetric) with HS256 (symmetric) using the public key as an HMAC secret.
Symptom
Attackers forge valid-looking tokens without the private key.
Blast radius
Complete authentication bypass — any identity, any claims.
Mitigation
Pin the expected algorithm; never accept none; separate verification keys by algorithm.

Deep dive: Token Replay

Missing audience / expiry checks

Trigger
A token minted for service A is replayed to service B, or an expired token is honored.
Symptom
Cross-service token reuse or use of stale tokens.
Blast radius
Access to services the token was never intended for.
Mitigation
Always validate aud, exp, nbf, and iss; keep clock skew tight.

Deep dive: Clock Skew

Latency

JWTs remove a per-request session lookup (great for latency and horizontal scale) but push key management (JWKS caching, rotation, kid handling) and the revocation problem onto you.

Can you store a user's credit card in a JWT if the token is signed?

No. Signing prevents tampering, not reading. A JWS payload is readable by anyone holding the token.

A JWT proves claims about a subject. But who issued it, and how did the client get one without handing its password to every app?

Next: OAuth 2.x →