Auth · Explainer · Guide

Understanding JWT

JSON Web Tokens look opaque but they are three plain pieces of text with a signature at the end. Here is the full picture — and the gotchas.

Header
{
  "alg": "HS256",
  "typ": "JWT"
}
Payload (claims)
{
  "sub": "1234567890",
  "name": "Alice",
  "iat": 1716239022,
  "exp": 1716242622
}

Anatomy of a JWT

A JWT is a dot-separated string: header.payload.signature. The first two parts are Base64URL-encoded JSON. The third part is a signature computed over the first two using a key only the issuer knows.

Standard Claims

  • iss — issuer
  • sub — subject (user id)
  • aud — audience
  • iat — issued-at (Unix seconds)
  • exp — expiry (Unix seconds)
  • nbf — not-before
  • jti — unique token id (for revocation lists)

Signing: HS256 vs RS256

HS256 uses a shared secret with HMAC-SHA256. Both the issuer and the verifier need the same key. RS256 uses an RSA key pair — the issuer signs with the private key, verifiers only need the public key. For multi-service architectures, RS256 is usually the right choice.

Common Mistakes

Trusting the payload without verifying. Decoding the JSON is not verification — anyone can rewrite the payload. Always run the signature check against the correct key.

Accepting alg: none. Older libraries let a client switch the algorithm to none, bypassing verification entirely. Pin the expected algorithm server-side.

Putting secrets in the payload. JWTs are signed, not encrypted. Anyone with the token can read every claim. Keep sensitive data out of it.

Try It

Inspect any token with our free JWT Decoder — it formats timestamps and never sends your token anywhere.

Signed, not encrypted

Payload is readable to anyone — don't put secrets in it.

Pin the algorithm

Reject tokens whose alg differs from what you expect.

Short expiry wins

Use short-lived access tokens plus refresh tokens.