JWT Tokens Explained: How Modern Authentication Works
JSON Web Tokens are everywhere — but how do they actually work? A clear explanation of JWT structure, signing, and security best practices.
What is a JWT?
A JSON Web Token (JWT, pronounced "jot") is a compact, URL-safe way to transmit claims between two parties. JWTs are the backbone of modern stateless authentication — instead of storing sessions on the server, the session data travels with the user in a signed token.
Anatomy of a JWT
A JWT has three parts, separated by dots:
xxxxx.yyyyy.zzzzz
│ │ └── Signature
│ └────────── Payload (claims)
└───────────────── HeaderThe Three Components
// 1. Header (base64url encoded JSON)
{
"alg": "HS256", // Algorithm
"typ": "JWT"
}
// 2. Payload (base64url encoded JSON)
{
"sub": "1234567890", // Subject (user ID)
"name": "Sarah Chen",
"role": "admin",
"iat": 1716000000, // Issued at
"exp": 1716086400 // Expiration
}
// 3. Signature
HMACSHA256(
base64UrlEncode(header) + "." + base64UrlEncode(payload),
secret
)How It Works (Auth Flow)
- User logs in with username + password
- Server validates credentials, generates a JWT
- Server returns the JWT to the client
- Client stores the JWT (localStorage or httpOnly cookie)
- Client sends JWT in
Authorization: Bearer <token>header - Server verifies signature — if valid, request proceeds
Common Algorithms
- HS256 — HMAC with SHA-256 (symmetric, shared secret)
- RS256 — RSA with SHA-256 (asymmetric, public/private key)
- ES256 — ECDSA with SHA-256 (modern, efficient)
Security Best Practices
- Always validate signatures — never trust a token without verification
- Set short expiration times — 15–60 minutes for access tokens
- Use refresh tokens — rotate access tokens without re-login
- Store secrets securely — environment variables, not in code
- Prefer httpOnly cookies — not localStorage (XSS vulnerable)
- Include
jti— unique token ID for revocation
Never Do These
- Don't put sensitive data in the payload (it's just base64-encoded, not encrypted)
- Don't use
nonealgorithm (JWT algorithm confusion attacks) - Don't skip signature verification
- Don't store JWTs in localStorage if you can avoid it
Validate JWTs in Code
// Node.js with jsonwebtoken
const jwt = require('jsonwebtoken');
const decoded = jwt.verify(token, process.env.JWT_SECRET);
console.log(decoded.userId, decoded.role);JWT vs Session Cookies
| Aspect | JWT | Session Cookies ||--------|-----|----------------|| Storage | Client-side (localStorage/cookies) | Server-side || Scalability | Stateless, scales easily | Requires shared session store || Revocation | Complex (requires blocklist) | Easy (delete session) || Size | Larger (token in every request) | Small (session ID only) || Security | Vulnerable to XSS | httpOnly cookies protect against XSS |Access Tokens vs Refresh Tokens
The best practice is to use two tokens:
// Access token: short-lived (15-60 min), contains claims
// Refresh token: long-lived (days/weeks), used to get new access tokens
// Example flow:
1. Login → returns access_token (15min) + refresh_token (7days)
2. API calls use access_token
3. When access_token expires → use refresh_token to get new pair
4. Refresh token can be invalidated (logout, security breach)Common JWT Libraries
- Node.js: jsonwebtoken, jose (universal)
- Python: PyJWT, python-jose
- Java: jjwt, java-jwt
- Go: golang-jwt/jwt
- Rust: jsonwebtoken
Security Checklist
- Always use HTTPS — Tokens are credentials, protect them in transit
- Validate algorithm — Prevent algorithm confusion attacks
- Check expiration — Always verify
expclaim - Verify issuer — Check
issmatches your expected value - Verify audience — Check
audis for your API - Use secure storage — httpOnly cookies over localStorage
- Implement refresh token rotation — Prevents token reuse attacks
Troubleshooting Common Issues
"Token expired" error
Check the server clock is synchronized. JWT validation compares against server time.
"Invalid signature"
Verify the secret/key matches exactly what was used to sign the token.
"Token not before" error
The nbf (not before) claim is set in the future. Wait or adjust server time.
Algorithm confusion attack
Always specify and validate the expected algorithm. Don't let clients choose the algorithm.
Frequently Asked Questions
JWT vs session cookies — which should I use?
JWT is best for stateless APIs, microservices, and SPAs that need to call multiple services without a shared session store. Session cookies are simpler for traditional server-rendered apps, allow immediate server-side revocation, and have built-in CSRF protection. For most web apps, httpOnly session cookies are safer; reach for JWT when you genuinely need statelessness.
Where should I store JWTs on the client?
Best: httpOnly + Secure + SameSite=Strict cookie — immune to XSS theft but needs CSRF protection. Acceptable: in-memory (lost on refresh, no XSS risk but bad UX). Avoid: localStorage — any XSS vulnerability lets attackers steal the token and all your user's data. The httpOnly cookie approach is the industry standard for sensitive apps.
How do I revoke a JWT before it expires?
JWTs can't be truly revoked without checking the server (defeating statelessness). Three workarounds: (1) Short expiration (15 min) + refresh tokens you can revoke, (2) Maintain a server-side 'revoked jti' list checked on every request, (3) Use a token versioning system where the user's current version is stored server-side. For most apps, short expiration + refresh tokens is the right answer.
