JWT auth is everywhere and misunderstood everywhere. A JWT isn't magic and isn't encryption — it's a signed, readable token. Understanding that one fact prevents most of the security mistakes people make with it.
What a JWT is
Three base64url parts separated by dots: header.payload.signature. The payload is just JSON — anyone can read it. The signature is what matters: it proves the token was issued by your server and hasn't been tampered with.
A JWT is signed, not encrypted. The payload is readable by anyone who has the token. Never put secrets (passwords, card numbers) in it — only non-sensitive claims like a user id and role.
The flow
- User logs in with credentials → server verifies them.
- Server signs a JWT with its secret and returns it.
- Client sends the token on each request (Authorization: Bearer <token>).
- Server verifies the signature — no DB lookup needed — and trusts the claims.
import jwt from "jsonwebtoken";
// issue
const token = jwt.sign({ sub: user.id, role: user.role }, process.env.JWT_SECRET!, {
expiresIn: "15m",
});
// verify (middleware)
const payload = jwt.verify(token, process.env.JWT_SECRET!);
req.userId = payload.sub;Access vs refresh tokens
Short-lived access tokens (minutes) limit damage if one leaks. A long-lived refresh token (stored securely, server-revocable) mints new access tokens so the user isn't logged out every 15 minutes.
Where to store them — the real debate
- localStorage: convenient but readable by any XSS-injected script. A stolen token is a stolen session.
- httpOnly cookie: not reachable from JavaScript (XSS-safe), but you must defend against CSRF (SameSite + a CSRF token).
- Common pattern: refresh token in an httpOnly cookie, access token in memory only.
Always set a short expiry, verify the signature AND expiry on every request, and have a server-side way to revoke refresh tokens (a denylist or rotating token family). A JWT you can't revoke is a liability.
A JWT is a readable, signed claim about who the user is. Keep access tokens short-lived, store them carefully, never trust the payload without verifying the signature, and keep a revocation path. Get those right and token auth is both simple and secure.