Stop 12 · AuthenticationTopics · 1/10

JWT

Part of the Frontend Developer Roadmap

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.

The jaadu holds up a stamped canteen token while the counter worker checks the stamp with a magnifying glass.

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

Browser
Server
Database

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

ts
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

~20 min

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

  1. 1Trace a full login flow: credentials → server issues JWT → client stores and sends it.
  2. 2Learn where to store the JWT with the best security trade-offs for your app type.
  3. 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.

Download
InsideEdge