JWT Explained
A practical guide to JSON Web Tokens for authentication and authorization
What is a JWT?
A JWT (JSON Web Token) is a compact, URL-safe token format for securely transmitting claims between two parties. JWTs are the most popular method for API authentication — when you log into a web application, you likely receive a JWT that proves your identity on subsequent requests.
JWTs are defined in RFC 7519. They're self-contained: the token itself carries the user's identity, roles, and permissions. The server doesn't need to look up a session in a database — it just verifies the token's signature.
JWT Structure
A JWT consists of three parts separated by dots:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
1. Header (Base64url)
{ "alg": "HS256", "typ": "JWT" }Specifies the signing algorithm (HS256, RS256, ES256) and token type.
2. Payload (Base64url)
{
"sub": "1234567890",
"name": "John Doe",
"role": "admin",
"iat": 1516239022,
"exp": 1516242622
}Contains claims: user data, permissions, expiration. NOT encrypted — anyone can decode it.
3. Signature
HMACSHA256(base64url(header) + "." + base64url(payload), secret)
Cryptographic proof that the token hasn't been tampered with. Only the server with the secret can create valid signatures.
Standard Claims
| Claim | Name | Description |
|---|---|---|
| iss | Issuer | Who issued the token (e.g., auth.example.com) |
| sub | Subject | Who the token is about (usually user ID) |
| aud | Audience | Who the token is intended for (e.g., api.example.com) |
| exp | Expiration | When the token expires (Unix timestamp) |
| iat | Issued At | When the token was created |
| nbf | Not Before | Token is not valid before this time |
| jti | JWT ID | Unique identifier for the token |
How JWT Authentication Works
- User sends username + password to
POST /login - Server verifies credentials, creates a JWT with user claims, signs it with a secret key
- Server returns the JWT to the client
- Client stores the JWT (usually in memory or httpOnly cookie)
- Client sends JWT in
Authorization: Bearer <token>header on every request - Server verifies the signature and reads claims from the payload — no database lookup needed
Signing Algorithms
- HS256 (HMAC-SHA256) — Symmetric. Same secret key signs and verifies. Simple, fast. Good for single-server setups.
- RS256 (RSA-SHA256) — Asymmetric. Private key signs, public key verifies. Best for microservices where multiple services verify tokens.
- ES256 (ECDSA-SHA256) — Asymmetric. Smaller keys and signatures than RSA. Recommended for new projects.
Security Best Practices
JWTs are NOT encrypted. The payload is only Base64url-encoded — anyone can read it. Never put passwords, credit card numbers, or sensitive PII in the payload.
- Always validate the signature — never trust an unverified JWT.
- Check expiration (exp) — reject expired tokens. Use short lifetimes (15 min for access tokens).
- Validate issuer (iss) and audience (aud) — prevent token confusion attacks.
- Never accept "alg": "none" — this disables signature verification entirely.
- Store in httpOnly cookies — not localStorage, which is vulnerable to XSS.
- Use refresh tokens — short-lived access tokens (15 min) + long-lived refresh tokens (7 days).
- Use strong secrets — HS256 keys should be at least 256 bits of random data.
JWT vs Session Cookies
| Aspect | JWT | Session Cookie |
|---|---|---|
| State | Stateless (self-contained) | Stateful (server-side session store) |
| Scalability | Excellent (no shared state) | Requires sticky sessions or shared store |
| Revocation | Hard (need blocklist) | Easy (delete session) |
| Size | Large (payload in every request) | Small (just session ID) |
| Cross-domain | Easy (any header) | Hard (CORS, SameSite) |
| Best for | APIs, microservices, SPAs | Traditional web apps |