JWTs explained: what is inside a token and mistakes to avoid

A JSON Web Token (JWT) is a compact string used to carry claims between systems, commonly to represent a signed-in user. It looks like three blocks of random characters separated by dots, but it is more readable than it appears.

The three parts

eyJhbGciOiJIUzI1NiJ9 . eyJzdWIiOiI0MiIsImV4cCI6MTc2MDAwMDAwMH0 . dBjftJeZ4CVP...
      header                          payload                         signature
  1. Header: JSON describing the token, such as {"alg":"HS256","typ":"JWT"}.
  2. Payload: JSON claims, such as {"sub":"42","exp":1760000000}.
  3. Signature: a cryptographic signature over the first two parts.

The header and payload are Base64URL-encoded, not encrypted. Anyone holding the token can read them, which is exactly what a JWT decoder does.

Common claims

ClaimMeaning
issWho issued the token
subWho the token is about (usually a user id)
audWho the token is intended for
expExpiry time, in Unix seconds
nbfNot valid before this time
iatWhen it was issued

Times are Unix timestamps in seconds. Use the timestamp converter when you need to read them by eye.

Decoding is not verifying

This is the most important idea. A decoded token shows what it claims. Only checking the signature with the right key proves the claims were not altered and came from the issuer. Never make an access decision based on a token you have only decoded.

Mistakes to avoid

  • Putting secrets in the payload. It is readable by anyone. Do not include passwords, personal data you would not want exposed, or internal details.
  • Accepting alg: none. Unsigned tokens must be rejected. Servers should allow a fixed list of algorithms rather than trusting the header.
  • Not checking exp, aud and iss. A valid signature on an expired token, or one meant for another service, should still be refused.
  • Algorithm confusion. Treat the algorithm as server configuration, not as something the token gets to choose.
  • Long-lived access tokens. A stolen token works until it expires. Use short lifetimes with refresh tokens.
  • Storing tokens where scripts can read them. localStorage is exposed to any cross-site-scripting bug. Consider HttpOnly cookies for browser apps.
  • Pasting production tokens into random websites. A live token is a credential. Use a decoder that runs locally in your browser, and confirm it with the Network tab test.

Quick debugging checklist

  1. Decode the token and check exp: has it expired?
  2. Check aud and iss: do they match your service’s configuration?
  3. Check the alg in the header matches what your verifier expects.
  4. Compare clocks: small clock skew between servers can make a fresh token look “not yet valid”.

All guides

↑ ↓ navigateEnter openEsc close