An Interactive Explainer
Tokens, tamper-proofing, and why your server trusts a string of random-looking characters.
Act I
HTTP is amnesiac. Every single request arrives at the server completely fresh — no context, no history, no idea who sent it. This is by design, and it's what makes the web scale so well. But it creates an obvious problem: how does a server know you're you after you've logged in?
Your browser sends a username and password once. The server checks them and says "yep, that's Alex, user #42." And then… the connection closes. The next request arrives and the server has already forgotten. It has no idea who's knocking.
So we need some way to carry identity from request to request. The oldest solution is sessions: after login, the server creates a record in its database ("session abc123 = user 42"), puts that ID in a cookie, and checks it on every request. It works! But it requires the server to maintain state — that database of active sessions. On big systems with millions of users, that becomes its own headache.
What if the server didn't need to remember anything at all?
Act II
Here's the simplest idea: just have the client tell the server who they are on every request. No session database needed — the identity travels with the message.
Try it below. You've just logged in as a regular user. Edit the request fields and see what the server believes…
Right. If the server just trusts whatever the client says, anyone can claim to be anyone. Set userId to 1 and suddenly you're the admin. The server has no way of knowing if the data was tampered with.
This is where cryptography earns its keep.
Act III
Here's the key insight — and it's a beautiful one. Instead of the server remembering what it issued, it can verify any message it receives by running a mathematical function on it.
The function is called HMAC — Hash-based Message Authentication Code. Here's how it works: you feed it a message and a secret key, and it produces a fixed-length fingerprint. Change even one character of the message and you get a completely different fingerprint. Without the key, you can't produce a valid fingerprint at all.
The server holds the secret key. Clients never see it. Let's watch this in action:
Now here's how a server uses this: when it issues a token, it signs the data with its secret key and appends the signature. When the token comes back in a future request, the server re-signs the data it received and compares. If the signatures match — it's the same data, untouched. If they don't — something was changed.
Act IV
A JSON Web Token is just this idea in a standardised format. It's a string with three parts, separated by dots:
HEADER . PAYLOAD . SIGNATURE
Each part is just Base64url encoded — a way of representing bytes as plain text characters that are safe to put in headers and URLs. It's not encryption (we'll come back to that). Click each part below to see what's inside:
The header tells the server which algorithm to use for verification. The payload holds your actual data — user ID, role, expiry time, whatever the application needs. The signature is HMAC applied to header + "." + payload using the server's secret key.
Notice the payload has an exp field — an expiry timestamp. JWTs are self-contained, so they carry their own expiry. The server doesn't need to look anything up; it just checks if the current time is before exp.
Act V
This is the main event. When a JWT arrives at the server, here's the exact sequence:
Split on the dots. Take the first two parts (header + payload). Re-run HMAC with the secret key. Compare the result to the signature in the third part. If they match — the data is genuine. If they don't — someone tampered with it, and the request gets rejected.
Try breaking the token below:
This is why JWTs are stateless. The server doesn't store anything. It doesn't look up sessions. It just does math — a hash function that takes about a microsecond — and either accepts or rejects the request. A server handling millions of requests can validate tokens without touching a database at all.
Act VI
Here's something that trips people up. Base64 encoding looks like gibberish, but it's not secret at all — it's just a way of encoding bytes into safe text characters. Anyone who gets their hands on a JWT can decode the payload and read everything inside.
Try decoding a payload yourself:
So: anyone who intercepts a JWT can read the payload in plain text. Never put sensitive data — passwords, credit card numbers, personal details — inside a JWT payload. The signature proves the data wasn't tampered with; it doesn't hide the data.
If you need to hide the payload, you'd use JWE (JSON Web Encryption), which actually encrypts the contents. Most applications don't need this — they use JWTs over HTTPS, which encrypts the whole connection, so the token is private in transit. The payload being readable at rest is usually fine.
Putting It Together
Here's the complete JWT flow, end to end:
And that's how your Nintendo Switch (or any modern API) can trust an incoming request without querying a session database. The math is the memory.
One small caveat we glossed over: unlike sessions, JWTs can't be easily invalidated before they expire. If a user's token is stolen, or they change their password, the old tokens are still technically valid until they expire. Most production systems handle this with short expiry times (15 minutes) and a separate revocation mechanism — but that's a story for another day.