I was on a call with an engineer who was migrating their auth system to JWTsJSON Web Tokena compact, self-contained token that carries identity between systems. They were sharing their screen, an auth flow diagram behind them, boxes and arrows all pointing in the right direction. “It’s just simpler,” they told me. “No session store, no database lookups, everything lives in the token.” I asked what happens when they need to revoke one before it expires. They paused. “I’ll figure that out later.”
Something about that pause stuck with me.
You log in. The server hands you a token. From that point forward, you carry it with you everywhere you go. Every APIApplication Programming Interfacethe way two software systems talk to each other call, every page load, every action. That token is your proof that you’re who you say you are.
If that sounds like the session cookie from the last post, you’re not wrong. But there’s a key difference.
Think of a session cookie as a permission slip where the school keeps the original on file. You carry a copy with just a number on it. Every time you show it at a door, someone calls the front office, reads the number, and confirms there’s a matching original in the filing cabinet. The proof lives in that cabinet.
A JWTJSON Web Tokena compact, self-contained token that carries identity between systems works differently. It’s a permission slip where you carry the only copy. Signed by the principal. It says who you are, what you’re allowed to do, and when it expires. No filing cabinet. No front office to call. The person at the door reads the slip, checks the signature, and lets you through.
That’s what made the engineer on my call so excited. And honestly, it is elegant.
No more lines at the front office
Someone has to run that front office. Every request means another call back to check the filing cabinet. If you’ve got multiple buildings, each one needs its own cabinet, and your slip only works at the building that issued it.
The self-signed permission slip doesn’t have that problem. Any door, any building, any service can read it and know what it says. No filing cabinet. No bottleneck. No worrying about which building you’re connected to. For systems built from many small, independent services, this feels like freedom. You remove an entire class of headaches in one move.
That engineer saw all of this. What they didn’t see was what they’d given up to get it.
You can’t take it back
Here’s the thing about a permission slip with no filing cabinet. Once you hand it out, it’s valid until it expires. There’s no pulling the original and shredding it. With a session cookie, you open the cabinet, pull the original, and it’s done. The copy the user carries is worthless the moment the original is gone.
With a JWTJSON Web Tokena compact, self-contained token that carries identity between systems, there is no original. The slip is the only copy, and it’s in someone else’s hands. If that someone turns out to be an attacker, you can’t reach into their pocket and take it back. The expiration timestamp says it’s valid, and the server trusts the timestamp.
So you’d need to start keeping a list of revoked slips. Check that list every time someone shows up at a door. And now you’re making a trip to the filing cabinet on every request. Which is the exact thing you were trying to avoid.
You’ve rebuilt the front office. You’ve just put it behind a different door.
That engineer said they’d figure out revocation later. This is what later looks like.
The short fuse
The standard advice is to make your permission slips expire quickly. Five minutes. Fifteen minutes. Pair them with a longer-lived credential stored more carefully, called a refresh token, that can request a new slip when the old one dies.
This works, mostly. But now you have two credentials to manage instead of one. And the refresh token becomes the real target. Steal it and you can generate new permission slips all day long. So the refresh token needs its own storage, its own rotation strategy (swapping for a new one each time it’s used so a stolen one is already dead), and its own revocation mechanism.
You’re back at the filing cabinet. You’ve just relabeled the drawer.
Written in plain ink
New developers often assume that because a JWTJSON Web Tokena compact, self-contained token that carries identity between systems looks like a random blob of characters, it must be encrypted. It’s not. The payload is Base64 encoded, a way of converting data into text characters so it can travel easily. It’s not encryption. It doesn’t hide anything. Anyone with the token can decode it in seconds and read everything inside.
Think about what that means for the permission slip. Everything on it is written in plain ink. Your name, your role, what you’re allowed to do, when it expires. Anyone who picks it up can read the whole thing.
Teams routinely put user emails, roles, internal IDs, and sometimes even more sensitive data into the payload. That information travels with every single request, in a format anyone can read, often in a URL or a header that gets logged somewhere.
The signature at the bottom of the permission slip proves the data hasn’t been tampered with. It doesn’t hide what’s written above it. Those are two very different things, and most teams I’ve talked to don’t realize they’re different until it’s too late.
What happens when someone forges the signature
The signature is the whole reason anyone trusts the permission slip. So what happens when someone figures out how to forge it?
Every JWTJSON Web Tokena compact, self-contained token that carries identity between systems includes a header that tells the server which signing method to use. The permission slip itself gets to say how it should be checked. That design choice alone should make you uneasy.
If an attacker changes that header to say “none,” meaning no signature required, some libraries will accept the slip without checking the signature at all. The permission slip says no verification is needed, and the system shrugs and lets them through.
There’s a subtler version too. There are two common ways to sign a permission slip. One uses a private key that only the issuer has. The other uses a shared secret that both sides know. The attack works by switching the method and signing the slip using the server’s public key, which is, by definition, available to everyone. The attacker forges the signature using information that’s freely available. And if the library isn’t careful, it accepts the forged slip as valid.
These aren’t theoretical. They’ve been found in production systems, in major libraries, in real applications handling real user data. And the barrier to pulling them off has dropped. What once required deep knowledge of the JWTJSON Web Tokena compact, self-contained token that carries identity between systems specification now requires a well-worded prompt to an LLMLarge Language Modelan AI system trained to generate and understand text, like the models behind chatbots and coding assistants and a few minutes of iteration. The era of “nobody will figure this out” ended when AI made offensive security knowledge accessible to anyone with a keyboard.
That engineer’s auth flow diagram had all the arrows pointing in the right direction. None of them pointed at this.
The tradeoff nobody reads
JWTsJSON Web Tokena compact, self-contained token that carries identity between systems solve a real problem. But most teams adopt the solution without understanding what they just traded away.
If you’re building a single application with a database that’s already handling every request, you probably don’t need JWTsJSON Web Tokena compact, self-contained token that carries identity between systems. A server-side session will be simpler, easier to revoke, and harder to mess up. The filing cabinet is already there. Use it.
If you’re building a distributed system where multiple services need to verify identity without a central filing cabinet, JWTsJSON Web Tokena compact, self-contained token that carries identity between systems make sense. But you need to go in with your eyes open. Short lifetimes. Careful refresh token handling. Pinning the algorithm so your server only accepts the signing method you chose. Having a plan for when a slip needs to die before its time is up. And being able to see how slips are actually being used across your system, not just at the moment you hand them out.
Most teams I’ve talked to can tell you they use JWTsJSON Web Tokena compact, self-contained token that carries identity between systems. Very few can tell you what’s actually inside them, how long they live, or what happens in the fifteen minutes between a breach and an expiration. That engineer said they’d figure out revocation later. I keep wondering if fifteen minutes is what later looks like.
Previously on Off White Paper: The Flimsy Wristband explored why the thing keeping you logged in matters more than your password. This post picks up where that one left off.