I'd be surprised if there wasn't a similar strategy available to Biscuits.
A place I worked at recently insisted on JWTs despite being a monolith and calling the database on every API endpoint. I still don't get why.
I do get wanting to keep it simple but with sessions I don't know what is more simple once your project starts to grow. Rolling your own auth can be a minefield especially if you want to span it across more than one service and most of the off the shelf solutions will push you towards OAuth/OIDC any way at which point JWTs are some kind of de-facto standard. The good thing is that once one has gotten accustomed to OAuth verbiage you don't really have to think about it any more.
I resisted microservice mania when it emerged, and now I feel validated that the pendulum has swung back.
At my clients, the typical case is that the "product" consist of an assembly of services where some have been written and some bought so being able to span homogenous access controlls over them is a nice feature of JWTs/OIDC :-)
JWTs are really nice because you can validate things like roles and permissions just by validating the token signature. And there are much better ways to implement revocation lists than "calling the database on every API endpoint" (if that's what you were referring to). Since revocation lists are usually very small (depending on the nature of your app), it's often possible to just replicate them to in-memory data structures on your servers.
Protecting your database from even being queried when the request is unauthorized reduces your attack surface enormously.
I guess DDOS attacks - if we're checking the session ID on every request then that's a potential attack if you just make up uuids and throw them at the server.
But JWT's themselves are an attack vector, surely? If there's any mistake in the encryption (or any vulnerability in the libraries used) then this is a door open very wide indeed.
The beauty of session IDs is the simplicity of it - very few moving parts, so very few opportunities for mischief. JWTs seem to be the opposite: lots of moving parts, lots of opportunities.
Comparing to session IDs, you have no way to know if an unique id is bogus or not. You have to check from a list, be it a cache or a database. This limits the scalability of the solution. I'm not an expert, but AFAIK JWT verifier can be stationed on the edge of the application network, and I have not checked this but I suspect they can even make a hardware solution for those kind of activity. That's definitely a big reduction of attack surface in terms of DDOS.
IMO JWT doesn't have that many moving parts. Encryption parameters are handled by libraries according to tested standards. The only real thing you need to do is to keep your private key safe.
So you have to check a signature, right? Maybe this is what the other commenter meant when mentioned encryption.
Session IDs don't have any of these problems. It does mean you have to do a database lookup on every request, and that can cause problems with scale, but if you have to hit the database anyway for other reasons then you have that problem anyway and have to solve it for those other reasons.
Encrypted session tokens with embedded expiry will serve the needs of 99% of applications/services.
I cited this as one example of that surface area that led to serious vulnerabilities. Most people don’t need multiple ways to encrypt their data, and certainly not a ‘no encryption’ option. Each added option adds more ways to mess things up.
Encrypted session cookies are a proven solution, widely used, and easy to implement yourself using only core libraries (http, crypto, JSON) without introducing security flaws IFF you are an experienced programmer and don’t deviate from the norm.
JWTs are complex. Too complex to implement yourself, so you need to introduce dependencies. Dependencies are usually huge, and each line of code introduces risk (even if that line is for a configuration you don’t use!). Using JWTs at all is far more risky, even if you are an experienced programmer.
If JWTs provides immense benefits over session tokens for your use case, that risk might be worth it. However, for most web apps, session tokens are good enough.
No need to reinvision the rest of the auth flow if you just want to add hashing to reduce DB load.
I've even seen a few cases of Stateful JWTs, where the JWT contained a session ID and everything else was in the DB. Of course, this approach manages to be both an overkill and a security and inferior to the just-use-an-HMAC approach at the same time.
Still, any modern database system could handle these lookups for all but the largest sites, so this smells like premature optimization for the vast majority of projects out there.
I much prefer sticking with something simple and easy to comprehend, with fewer potential footguns, and revisiting the decision when we have enough traffic that it could be a concern. Just putting the tokens in Redis can go pretty damn far.
The reason you avoid the db query isn't about trying to keep the db from being overloaded, it's that you can handle the request far away from the DB without needing to wait for the latency of hitting the DB.
Which is also still a complete premature and unnecessary optimization for most people. And then someone realizes their use case requires revocation so they add a DB check anyways.
I understand that it's not inherently more secure or performant, but it is convenient.
For myself I use short lived JWTs with a refresh token that I can invalidate. Yes, this means if there is a breach you have wait the refresh period but honestly I’ve never seen someone move faster than my refresh time to invalidate a session (on the rare cases I’ve ever seen it done professionally) so I’m not very concerned about the potential hole. It’s just not in my threat model and I have to say I question some people who say it’s in theirs.
I’m not saying there isn’t a use-case for immediate revocation but that a lot of people who talk about it don’t actually need it and/or have no good method for using it. Unless you have automated systems that revoke a token then I fail to see how 5 more minutes after a request worms its way through support is the end of the world. The damage is either already done or 5 more minutes isn’t going to be the end of the world (again, by this point it’s been going on for hours if not days/weeks).
Even if I wanted immediate revocations I’d just use an ID in a JWT and compare against a list since I still find the benefits from having role/tenant in the auth token very valuable. Then all I need is something like memcached or DynamoDB to hold a TTY’d ID for 5-ish minutes. But like I said, my threat model doesn’t require that.
By the nature of the problem you have to store some kind of a list of tokens. Either a black list as with JWTs or a white list as with classic session tokens. There is no way around it. This makes both approaches practically the same.
One can argue that the black list will in general be shorter than the white list, but in a case of a serious attack, would it really be so?
I am afraid that the community will now abandon JWTs and move to biscuits, macaroons, buns and meringues as they did with classic session tokens, throwing away the tools and security practices.
Why? Wouldn't a developer prefer to implement this inside its application logic anyway?
Edit: I think I figured it out myself: you're likely targeting the case where someone with a certain authorization wants to give someone else a weaker form of that authorization (attenuation).
Revocation is essentially the Achilles heel of JWTs - you want to have completely stateless bearer tokens, but if you need to check for revocations, it means you have to do some sort of lookup against a revocation table, and at that point you really lose the stateless nature that you wanted in the first place. There are some tricks to make this more tenable, but it's still a big reason why some folks say "don't use JWTs for end-user auth".
Would greatly appreciate details on how Biscuit implements this with their "revocation IDs".
Edit: Nevermind, I found it: https://www.biscuitsec.org/docs/why-biscuit/ - and it turns out revocation is a "non goal". And then in the linked page about how to implement revocations, they basically do exactly what I do for JWTs: have a revocation list, but then revocations can fall off that list once they're past the expiration date anyway.
So, not to be too harsh, but why would I use this over JWTs? There is no "magic" getting around the "well, they're kinda stateless" problem. To be honest I don't really care about some of the other tangential benefits (after all, you can stick whatever you want on a JWT and interpret it however you like) if it still has by far the same glaring pain point associated with any bearer token system.