In short
After login, the server must recognise you on every request. A JWT is a small signed note that says who you are, and almost every auth system uses one.
In an auth-system context: using signed JSON Web Tokens to represent a logged-in session, verified by the server on each protected request.

Think of it like this
A stamped canteen token
At the college canteen you pay once and get a token with the cashier's stamp. The counter boy doesn't call the cashier again, he just checks the stamp. Anyone can read what's written on the token, but nobody can fake the stamp.
Watch it happen
A JWT on its rounds
POST /login
1/7You send your email and password once, over HTTPS.
The big ideas
What you need to know
Three parts, two dots
A JWT looks like xxxxx.yyyyy.zzzzz. That's the header, the payload (your data) and the signature.
Readable, not secret
The payload is only base64url encoded, not encrypted. Paste any JWT into jwt.io and you can read it. Never put passwords in it.
The signature stops tampering
The server signs the token with a secret key. Change even one letter of the payload and the signature check fails.
Keep them short-lived
A JWT stays valid until it expires (the exp claim). Short expiry, like 15 minutes, limits damage if one leaks.
See it in code
Walk through the code
1// Server only. Uses the "jose" package.2import { SignJWT, jwtVerify } from "jose";3 4const secret = new TextEncoder().encode(process.env.JWT_SECRET);5 6export async function createToken(userId: string) {7 return new SignJWT({ sub: userId })8 .setProtectedHeader({ alg: "HS256" })9 .setExpirationTime("15m")10 .sign(secret);11}12 13export async function readToken(token: string) {14 const { payload } = await jwtVerify(token, secret); // throws if fake or expired15 return payload.sub;16}1/3The secret is read from a server-only env variable and turned into bytes for signing.
The secret lives only on the server in an environment variable. jwtVerify checks both the signature and the expiry, so you never trust a token without calling it.
Save yourself the pain
Common mistakes
Oops
Storing the JWT in localStorage because it's easy.
Do this instead
Prefer an HttpOnly, Secure cookie. Any injected script can read localStorage, but it can't read an HttpOnly cookie.
Oops
Decoding the token on the client and trusting the role inside it.
Do this instead
Decoding is fine for showing a name. Real decisions happen on the server after jwtVerify.
Oops
Putting the signing secret in a NEXT_PUBLIC_ variable.
Do this instead
Anything prefixed NEXT_PUBLIC_ ships to the browser. Keep the secret in a plain server-only env variable.
Your mission
Dissect a real token
Quick check
Your friend says JWTs are safe for storing a user's phone number and password because they're signed. What's wrong?
Go deeper
- 1Trace a full login flow: credentials → server issues JWT → client stores and sends it.
- 2Learn where to store the JWT with the best security trade-offs for your app type.
- 3Understand token expiry and what your app should do when a token expires mid-session.
More topics at this stop
Stuck on this topic? Ask an Insider
Get 1:1 guidance from people who've walked this exact path — on the InsideEdge app.