Auth Standards Deep Dive · 4 of 6

JWT — Signed Claims, Carry-On Format

A JSON Web Token is a small string carrying signed claims — "this user is sub=42, valid until exp=..., issued by iss=...". They power most modern auth systems. They're also the format with the longest list of "we did it wrong" stories. The format is fine; the way developers validate it often isn't.

JWSClaimsBearerJWKSalg=none
← Back to Security
Quick Facts

What a JWT Is

Basic Concepts

  • Three base64url parts: header.payload.signature. Joined by dots.
  • Header: the algorithm (RS256, HS256, ES256, ...) and key ID (kid).
  • Payload (claims): JSON. Standard claims: iss issuer, sub subject, aud audience, exp expiry, nbf not-before, iat issued-at, jti token ID. Plus your custom claims.
  • Signed, not encrypted. Anyone can read the payload (jwt.io); only the holder of the signing key can produce a valid signature. JWE is the encrypted variant — rarely used.
  • Stateless verification. Given the public key, any service can verify a JWT locally without calling the issuer.
Why Teams Reach for It

Where JWTs Earn Their Keep

  • Stateless services behind a load balancer — no session store needed; every node verifies independently.
  • OIDC ID tokens — the exact use case JWT was designed for.
  • Service-to-service auth — short-lived JWTs signed with a private key; downstream services verify with the public key.
  • Single-page apps talking to APIs — though see the storage caveats below.
  • Signed URLs and tickets — a JWT in a query param to authorize a one-time download or webhook delivery.
The Pitfalls

The Bugs That Recur

alg: none — The Original Disaster

The JWT spec includes "alg": "none" meaning "no signature." Several libraries, in their early days, accepted it — passing an unsigned token through validation as if it were valid. Anyone with a text editor became an admin.

Defense: pin the expected algorithm. Don't accept whatever the token's header claims. Use libraries that require an explicit algorithm whitelist.

Algorithm Confusion (RS256 → HS256)

If the verifier accepts whatever alg the token claims, an attacker can take a token signed with RS256 (asymmetric), change alg to HS256 (symmetric), and sign it using the public key as the HMAC secret. Some libraries did this. The fix is the same: pin the algorithm.

Skipping Claim Validation

Verifying the signature is necessary; it's not sufficient. Always validate:

  • iss matches the expected issuer.
  • aud matches your service.
  • exp in the future, nbf in the past.
  • iat reasonable (not far-future).

A signed token from another service that hasn't checked these is still an attack vector when accepted by yours.

Putting Sensitive Data in Claims

JWT payloads are signed, not encrypted. Anyone who sees the token can decode the claims. PII, internal IDs, sensitive flags shouldn't be there unless you're using JWE (encrypted JWT).

No Revocation Story

A compromised JWT is valid until exp. Without server state you can't revoke it. Three mitigations:

  • Keep access tokens short (minutes), refresh tokens server-side and revocable.
  • Maintain a small, fast denylist of revoked jtis for high-value scenarios (logout, password change).
  • Bind tokens to a key the client holds (DPoP, mTLS) so the token alone isn't enough.
Storing JWTs in localStorage

localStorage is XSS-readable. One DOM XSS leaks every token in every session. Prefer HttpOnly cookies for browser-stored tokens, paired with a backend-for-frontend that proxies API calls. Modern guidance is unambiguous on this.

Trusting the kid Without Constraint

The kid header tells the verifier which key to use. Some libraries used it as a path or DB lookup — and attackers smuggled SQLi or path traversal through it. Treat kid as opaque, look it up against a fixed JWKS, never as a path.

Right-Sizing

When JWT Isn't the Answer

For browser sessions to your own backend, opaque session IDs in HttpOnly cookies are simpler and easier to revoke than JWTs. JWTs shine when verification needs to happen without calling the issuer — across services, at the edge, or in a federated context.

Don't pick JWT because it's "stateless and cool." Pick it when stateless verification is a real requirement; otherwise stick with sessions and save yourself a category of bugs.

Continue

Other Auth Standards