What is a JWT? (the wristband your app checks at the door)
In one sentence:A JWT is a signed token that proves who a user is, so your server can trust it on every request without looking anything up in a database.
What it actually is
A JWT (JSON Web Token, usually pronounced “jot”) is a signed string your server hands a user after they log in, so it can trust who that user is on every request after that, without checking a database each time. It’s really three chunks of text glued together with dots: header.payload.signature. The header names the signing algorithm, the payload holds claims like the user’s ID and an expiry time, and the signature is what makes the whole thing trustworthy: proof it was issued by your server and hasn’t been edited since.
Think of it like a wristband at a club. Getting it stamped at the door (logging in) takes a minute and checks your ID properly. After that, every bouncer inside just glances at the wristband instead of walking you back to the front desk. The wristband itself isn’t secret, anyone can look at it, but only the door staff know how to spot a fake one. That’s the signature’s job, not to hide what’s on the wristband, just to prove nobody forged it.
That last part trips people up: a JWT is encoded, not encrypted. The payload is only base64url text, readable by anyone who has the token, not scrambled or locked. Paste one into jwt.io and you’ll see your own user ID and expiry sitting there in plain JSON.
Why your AI just did this
When your agent builds login with Supabase Auth, NextAuth, Auth0, or a hand-rolled setup, it needs a way to keep proving who’s asking without re-checking a password on every click. A JWT solves that: the server signs one at login with a secret only it holds, the client stores it and sends it back with each request (usually in an Authorization header or a cookie), and the server verifies the signature instead of hitting the database. Supabase leans on this directly: Supabase Auth issues the JWT, and Row Level Security policies in Postgres read the user’s ID straight out of it via auth.uid(), no separate session lookup involved.
When you’ll run into it
- An error like
jwt expired,invalid signature, orJsonWebTokenError: jwt malformedfrom your backend or a library likejsonwebtoken - A
401 Unauthorizedon a request that worked five minutes ago, usually the token’sexpclaim finally ran out - Your agent asking whether to store the token in a cookie or in
localStorage, a real security tradeoff, not a coin flip - Debugging a login bug by pasting your own JWT into jwt.io to read the claims inside it
- Your agent mentioning a “refresh token” alongside the JWT: the longer-lived credential that quietly issues you a fresh one
What to check
- The secret used to sign JWTs lives server-side only (an environment variable), never shipped to the browser
- The token sits in an httpOnly cookie rather than
localStoragewhere possible;localStorageis readable by any script that runs on your page, including an injected one - Nothing sensitive, a password, a raw secret, a full credit card number, ever goes into the payload; treat it as public once issued
- Your server actually verifies the signature on protected routes, rather than just checking that some token was present
- The
expclaim is set to something reasonable (minutes to hours, not months), with a refresh token handling the “stay logged in” part
Say it like a dev
Instead of: “make it so I never get logged out” Say: “use a short-lived JWT with a refresh token, not one that never expires”
Instead of: “the JWT is encrypted, so it’s safe to put anything in it” Say: “the payload is only base64-encoded, not encrypted; keep secrets out of it”
Instead of: “check if this token looks real” Say: “verify the JWT’s signature server-side, don’t just trust that a token was sent”
People actually ask
“What is a JWT and what does it actually do?”
It's a JSON Web Token: a signed piece of text your server hands a user after login, proving their identity on every request after that. The server checks the signature instead of looking the user up in a database each time, which is what makes it fast, and it's designed to expire on its own after a set time.
“Is a JWT encrypted? Can someone read what's inside one?”
No, by default it's not encrypted, only encoded and signed. Anyone who has the token, including the user holding it, can decode the middle section and read the claims inside (paste one into jwt.io and watch it happen). The signature stops someone from forging or editing it, it does nothing to hide the contents, so nothing sensitive belongs in there.
“Why does my JWT keep expiring and logging me out?”
Every JWT carries an expiry (the `exp` claim) baked in at the moment it's issued, on purpose, so a stolen token goes stale quickly. Most apps solve the annoyance with a second, longer-lived refresh token that quietly gets you a new JWT behind the scenes, so if you're getting logged out with no refresh step in sight, that's usually the missing piece.
Related terms
Checked against
Glim can explain your sessions, live.
The app watches your coding agent and narrates it in plain words. Waitlist is open.