JWT tokens
JWT is a signed (not encrypted) token: header.payload.signature, base64url-encoded, used for stateless authentication.
A JWT is three base64url-encoded strings joined by dots. header.payload.signature. The header says what algorithm signed it, the payload contains claims about the user, the signature proves the payload was not tampered with.
eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJleHAiOjE3MDB9.abc123
Decode the first two parts with any base64 decoder and you can read them. JWTs are signed, not encrypted (unless you use JWE, which almost nobody does). The signature only stops tampering.
The signature uses one of two key models.
- HS256/HS384/HS512: HMAC with a shared secret. Same secret signs and verifies. Use when one service issues and verifies, like a session token.
- RS256/ES256/EdDSA: asymmetric signature. Private key signs, public key verifies. Use when one issuer (auth server) signs and many resource servers verify, like OAuth.
Standard claims you should know.
sub: subject, usually the user ID.exp: expiration timestamp. Always set it.iat: issued-at timestamp.iss: issuer.aud: audience (which service this token is for).jti: unique token ID, for revocation lists.
The use case is stateless auth: server issues a JWT on login, client sends it in Authorization: Bearer <jwt> on every request, server verifies the signature without hitting a database. Scales horizontally because there is no session state.
Three rules.
- Always set
exp. Short lifetimes (15 minutes for access tokens). Use refresh tokens for longer sessions. - Always validate the algorithm. If you expect RS256, reject HS256 tokens. Library bugs let attackers downgrade.
- Never put passwords, API keys, or PII in the payload. It is readable. Put user IDs and role claims only.
You cannot revoke a JWT once issued (without a blocklist or short expiration). That is the tradeoff for statelessness.
Learn more
- Docs
- Article
- Docs