JWT, Explained

JSON Web Tokens are the de-facto authentication primitive of the modern web. The format is straightforward; the security pitfalls are not. This guide covers the structure, the claims that matter, and the four mistakes nearly every team makes at least once.

The structure

A JWT is a string with three dot-separated parts: header.payload.signature. Each part is base64url-encoded. The header and payload are JSON objects; the signature is a binary hash bound to the header+payload contents and a signing key.

Example (truncated, line-broken for readability):

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4iLCJpYXQiOjE1MTYyMzkwMjJ9
.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c

Decode the header, you get something like {"alg":"HS256","typ":"JWT"}. Decode the payload, you get the claims. The signature isn't human-readable.

The claims that matter

The payload is just JSON, so it can hold anything. But there are seven registered claims (RFC 7519) that every JWT library understands and most identity providers populate:

For application data, use unregistered claims (just JSON keys). Common conventions: email, name, roles. Don't put secrets in the payload — the payload is base64-encoded but not encrypted. Anyone with the token can read it.

Algorithms — and the algorithm trap

JWT supports many algorithms. The common ones:

The four pitfalls

1. Accepting alg: none

Some old JWT libraries accept tokens with {"alg":"none"} and no signature, treating them as valid. An attacker who controls the token can drop the signature and change the alg, and the server says "ok." Cure: explicitly whitelist allowed algorithms in your verification call. Never trust the alg field alone.

2. Algorithm confusion (RS256 → HS256)

If your service uses RS256 and exposes the public key (which is normal), an attacker can craft a token with {"alg":"HS256"} and sign it using the public key as if it were an HMAC secret. A naive verifier that uses the alg field to pick the verification function will use the public key as an HMAC secret — and accept it. Cure: lock the algorithm at verification time, don't read it from the token.

3. Not validating aud

If service A and service B trust the same identity provider, a token issued for A may be replayed against B if B doesn't validate that aud == B. The fix is one line of code, but it's frequently skipped because "we control both sides." Skipping it is how many SaaS products get cross-tenant token replay vulnerabilities.

4. Long-lived tokens with no revocation

JWTs are stateless by design — the server doesn't track them. That means if a token leaks, the only way to "revoke" it is to wait for exp. If your tokens last 30 days, leaked tokens last 30 days. Cure: keep access tokens short-lived (5-15 min) and use a revocable refresh-token flow for the longer session.

JustKit's role

The JustKit JWT decoder shows you the header and payload in plain text. It does not verify the signature — that requires the secret/key, which we don't have and shouldn't ask for. For verification, use your library's debug tooling (jsonwebtoken in Node, PyJWT in Python, etc.). Use the JustKit tool when you need to read what's in a token quickly without a CLI handy.