A JWT Is a Format, Not a Login System – Simply Explained
The other day the same old fight rolled across the internet again: should you use JWTs or not? One camp swears by them, the other warns against them. Both sides argue with fervor, and in the end everyone goes home unchanged.
That was my cue to take a calm look at it and understand what a JWT (spelled out, a JSON Web Token) actually is.
Spoiler: in the end everyone was arguing about two different things and calling both by the same name.
What the thing even is
Picture two ways a website can recognize you after you’ve logged in.
The first way is like a coat-check token. You’re handed a random number. The number on its own means nothing. Only the cloakroom, the server, knows whose it is. You hand in the token, the server looks it up in its list, and knows: ah, that’s you.
The second way is a JWT, and it works like an ID card you carry yourself. The facts are written right inside it: who you are, what you’re allowed to do. And the whole thing is sealed, so nothing can be faked.
Now comes the crucial point almost everyone misses. This card is sealed, but not secret. Anyone who gets their hands on it can read it. Nobody can change it without breaking the seal, but anyone can read it.
Signed but not secret: that’s the trick and, at the same time, the danger.
A JWT, by the way, looks like a long string of gibberish with two dots in it. Pretty it is not. But the contents are openly readable to anyone holding it.
Where the self-carried card is exactly right
The card plays to its strength precisely when the one who issues it is different from the one who checks it, and the two share no common list.
The classic example: “log in with Google.” Google issues you the card. A completely unrelated website checks it. That website can look at the seal and see at once: genuine. It never has to phone Google to do so.
The sealed card saves everyone a phone call here, and that’s exactly what it was built for.
The same goes when, behind the scenes, lots of little programs talk to each other and have to prove who they are. Each one can check the seal itself, nobody has to consult a central list. With huge volumes of requests, that’s the difference between smooth and jammed.
Where it gets dangerous
And now the spot the fight is really about. Some people use this self-carried card as the everyday login for a perfectly ordinary website, and keep it somewhere any script on the page can grab.
That brings two very concrete problems.
First: you can’t take the card back. Once issued, it’s valid until it expires on its own. You log out? The card still works. You change your password because someone broke in? The stolen card still works anyway.
With the coat-check token, the server just crosses out the line in its list, and the token is dead. With the card, you can’t.
Second: if the card is lying around in the open, a malicious script on the page can steal it. And with the stolen card, the thief is, for all purposes, you.
The usual workaround is a blocklist, checked on every visit. Sounds reasonable. But that means you’re asking a central list again, and throwing away the very advantage you took the card for in the first place.
At that point you might as well stick with the coat check.
What to use instead
For an ordinary website login, the boring old coat-check token is simply better. The server keeps the list, the browser guards the token in a protected pocket, and logging out means crossing out one line.
The best part: every common building-block system ships this ready-made. You don’t have to build your own, and with security in particular, building your own is the last thing you want.
The self-carried card stays the right tool for programs talking to each other. For your login in the browser, it isn’t.
(There’s also a newer card format called PASETO that designs some of the typical traps away from the start. Nicely thought out, but for most people the familiar way, done cleanly, is plenty.)
The actual fallacy
And here the whole fight dissolves. People compare “coat-check token versus ID card”, that is, cookie versus JWT. But that’s a comparison of apples and oranges.
One is where you keep something. The other is what you keep. You can even put the card into the coat-check pocket, the two go together just fine.
The only question that really matters is a completely different one: does the secret live with the server, or is it inside the card itself?
That’s worth talking about. Token versus card is not.