Upd: consider this to be a draft for a blog post. I was explained there actually are some usecases for JWT outside SSO. See also a tl;dr and some clarifications.
Classical authentication flow involves generating a random token on the backend, assigning it with a user account, sending it back as a cookie.
On every request, the server checks if a provided token is valid and who it is assigned to (who exactly sent the request).
They say searching for a token in a database is slow and inefficient, so they invented JSON Web Tokens and even promoted this standard to IETF and made JWT a "best practice", or at least very popular way of doing sessions.
It consists of header, payload and signature.
- Header says what cryptographical algorithm is used for the signature, e. g. HMAC-SHA-256.
- Body/payload contains info about a user (e. g.: id, isAdmin), that in case of a classical auth would be retrieved from a database on the server side.
{base64(header)}.{base64(payload)}
is signed with a secret key, known only to the server, to ensure later that the token is actually generated by this server (otherwise, a hacker could easily change isAdmin to true in his JWT payload) and finally we build a token: b64 of header json, dot, b64 of payload json, dot, b64 of signature.
I already said about JSON-in-base64 in the post above. If you want urlsafe token, then do it urlsafe, without JSON. Why encode your plaintext multiple times.
The payload part can be decoded by anyone, and maybe you don't want an attacker to easily find out that a session belongs to an admin. Storing only ID inside of the body is clearly an overkill, you still need a database to know the user permissions.
The biggest problem with JWTs is revocation.
If a hacker steals our cookie, we can log out or click "terminate session". If it's a classical session cookie.
JWT is not stored on the server at all and the only check is signature verification. We can't somehow mark it revoked — a client already got a valid JWT with "revoked":false
and it continues being valid as it was signed by the server with the same correct secret key.
Let's use JWTs with a short expiration time! Uhmm, no, it does nothing except annoying a user with a login form — an attacker could be quick enough to take a session, and logging out in your browser wouldn't invalidate the JWT.
Then maybe store a list of revoked JWTs anywhere? Yes. It's a common solution.
But remember why this kind of tokens was invented? Searching for a session in a database is a slow-ish operation, we don't want to do it.
So let's use Bloom filter. It's a space-efficient probabilistic data structure allowing to check whether an element is present in a set very quickly. Its performance comes with an accuracy trade-off: it can emit false positives — but not false negatives; so in our case the positive result means "JWT is probably revoked", the negative one is "JWT is definitely valid (if signature is OK)". When there's a probability that JWT is invalid, do a search in a hashmap / linear or binary search in a list / [other algo] in [whatever you use to store the revoked JWTs].
That's the mechanism used also by Mozilla's CRLite — TLS certificate revocation list compiled from all CAs' sources and built into Bloom filter space-efficient structure, regularly updated in browsers, performing the cert check locally (unlike OCSP, requiring an additional request for verification).
But, wait, don't lie to yourself, it's not a database-free token. We still need to store something somewhere, and even made the system extremely complex. Aren't classical auth tokens better?
I also wonder how much JWT's signature verification takes less or more CPU time than a PostgreSQL query to find a token. When awaiting I/O, we can do smth useful in parallel, but when performing cryptographic operations, CPU is busy.
Hey, I have a solution for storing and verifying session tokens. Use a Redis-like database (Valkey, Dragonfly, Redis) for fast lookup, or even something embedded (RocksDB, libmdbx?) to not deal with sockets and make it faster. Tokens are keys, user IDs or objects with user info are values.
#jwt #authentication #revocation #simplicity #performance #infosec