Ask HN: What's the current sentiment on JWT for stateless auth tokens?
HN threads from 2016-2018 complain that JWT gives us too much rope with which to hang ourselves, and some advocate for alternatives like PASETO instead. Today it's not clear however that any alternatives like PASETO have crossed the threshold of adoption to warrant preference over JWT when choosing a stateless auth token solution.
What solution would you choose today for stateless auth tokens?
239 comments
[ 3.7 ms ] story [ 252 ms ] thread> What solution would you choose today for stateless auth tokens?
Issue token (=random 256 bit string), verify against cached database. If you believe this to be a significant performance issue, you are very likely wrong.
Everyone thinks they're building the next Facebook, but most of us aren't gonna stress out even a single Redis instance.
In my implementations of such schemes, I will typically feed the CSPRNG output with a time-based salt into SHA256 or SHA512 in order to generate the final token, which is simply stored on both ends (user session db row on server & cookie on client). At this point, the server can simply 1:1 the supplied client token with the row in the database to determine if the session is valid and which claims to return (or which validated final server response to return as the case may be).
Advantages with using a token made out of pure entropy soup are that it's consistent throughout the entire user's session, and confers absolutely zero information regarding the nature of that session. Disadvantages are honestly unclear. I feel like JWT and other stateless schemes started as a bandaid to re-couple the authentication domains of disparate business services, but by way of the various clients.
One place I still might use JWT is in service-to-service auth.
But more importantly, why do you need stateless auth tokens?
So you aren't hitting some validation server on every HTTP request to validate a token. Said validation server adds a very nasty point of failure & potential for page latency.
Going mostly stateless via JWT lets you balance the desire for fast token revocation times and load on your auth server. Even if your revocation time is a minute, meaning your clients need to refresh their token once every sixty seconds, that can still substantially lighten the load on your auth server. High security stuff can always validate the token on every request but stuff that doesn't matter as much can have a much more policy.
This is not a real problem for the 99.9% case and you can add another nine if you aren't defaulting to "extreme conservative"on your risk analysis. A session token lookup in a Redis or a Postgres is completely trivial and cheap.
("But my auth service is over a network link," cries the architecture astronaut, who now has more problems and fewer solutions. Build simpler things, you almost certainly don't need more complex and you can build better solutions than JWT when you need them.)
Which is to say until you need a working authentication system.
JWTs solve a nonexistent problem in the 99.9% case but are so very, very bloggable that they've brainwormed the population but good.
JWT doesn’t mean you give up anything....
Looking up an ID in a database is much simpler that using JWTs (which involve crypto) and handling revocation.
Libraries might hide a lot of the complexity of JWTs behind abstractions, but that doesn't mean it doesn't exist.
If you are gonna play the “look under the layers of abstraction to count true complexity” game.... your assertion falls apart in a hurry. Calling a database or redis every http request is a hell of a lot more complex in terms of call stack, configuration, network dependencies and library support than JWT.
But it is a stupid way to frame your argument to begin with. Both ways abstract all their doings behind a library. Both probably have almost the same general configuration foot print. And JWT has the benefit that you can still treat it like a session token and validate on every call for parts of your site that need it. Most sites do not need to instantly revoke a token everywhere!
The number of detractors around here who don’t understand that last point is pretty sad...
For the record, I'm not a JWT "detractor" (if you look elsewhere in this thread you'll find a comment where I used it to good effect).
Sure.
The vanishing minority of projects ever get to that point.
Didn't we internalize YAGNI literally a decade ago?
Know your facts. And YAGNI and all that...
I get that it's hard with your bloggable thing, but it's better for the people on the other end.
There are at least a few cases where a central auth service makes sense. Gov't here uses it for example, so that the services from all the various departments and whatnot don't have to roll their own auth.
I ask for an access token from auth server, I pass access token to API server, API server optionally checks with auth server if token is ok, or if token is self-contained, verifies signature, checks expiration time etc.
Obviously self-contained tokens cannot be revoked, they can only expire.
For this service, JWT is used when requesting an access token, and as an access token for self-contained tokens.
The crypto used for JWT not some brand new space age thing. Public key cryptography has existed for decades now.
JWT is a very elegant solution to the problem space once you understand what it does. I get the feeling the actual architecture astronauts are the folks around here dismissing it.
That might not matter to you because you might not work on things that matter and that's fine. If what you work on doesn't matter, do whatever you want, but if it does, don't use JWT.
Some use cases:
1) Sign In with Google gives your browser a time-limited JWT that it then provides to the application you're trying to sign in to. The application's server checks the validity of the token (without having to contact Google, except for daily key refreshes) and can upgrade that to a session for your browser using its normal session-management machinery. The idea is to transfer a piece of information between two systems (Google and the server app) through an untrusted system (your browser). For that, the JWT works well enough.
2) I wrote a proxy to convert one's external SSO session cookie to a JWT internally. That ways apps can check the user without having to make any requests to a user service, and can propagate that safely to other backends. It was very time-limited (min(RPC deadline, global deadline) + a few seconds of slop in both directions) and never exposed outside of an internal network.
Some anti-patterns:
You don't want to store sessions in a database, so you store the state in a JWT that the user stores in their browser cache. This has the problem that you can't revoke the token when the user is fired or gets hacked, which is a big problem. (You can of course add a database that only stores revocations... but you now have that check inside your request path, so you should have just done it the other way around and saved the user from having to send you a giant cookie on every request.)
I think, on some level, you can make a revocation database scale better than a validity database. You store the revocation in some durable datastore. Then you have some service in front of this that calculates the entire set of revoked tokens, and pushes that to each auth gateway. Then the auth gateway can check if a token is revoked without putting any load on a database. It has to do this for every request, but you can scale the auth gateway horizontally rather easily.
(When an auth gateway starts, it gets a copy of the full revocation table again.)
On some level, you do need the ability to push revocations to your auth server, because when you hit the kill switch on a session, you really want it to close all their open TCP connections (HTTP/2, Websockets, SSH, etc.)
Having said that, starting simple is the best approach. I used MySQL to store sessions at my last job. Every request went from Envoy -> ext_authz -> authentication/authorization service -> MySQL and this whole flow added only 1-3ms of latency per request. Someday you will hit scaling limits of using MySQL for this, but it's a long way in the future. Do the simple and reliable thing first, then make it complicated later.
But I do use them in one place with a revocation scheme similar to this. On init, instances build an in-memory bitwise blacklist that token IDs are checked against.
This arrangement probably already implies this, but just in case: this is at a very small scale where the complexity of pushing revocations isn't justifiable, power granted to the token-holder is limited and easy to clean up if misused, and revocations are so rare that restarting instances to update the blacklists is tolerable).
Is that a big problem in practice? I always wonder about the tangible benefits of this approach.
I've done something similar, to "terminate" Windows authentication at an API gateway, so downstream services can run on any platform, and don't have to deal with the vagaries of Windows auth.
JWTs worked very well for this use case (obviously any other kind of token would have worked too, such as PASETO).
http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...
The issue is I just logged in 90 seconds ago, typed up a few paragraphs, and when I go to submit I have to do it again, because someone in another company using the tool got phished and needs a new password.
It's frustrating enough when Gmail makes me put my password in after 30 days and I just clicked into an email I wanted to read. Having it happen all day long would make me want to kill someone.
Additionally JWEs don't support forward-secrecy meaning that I can MITM and collect all JWEs passing through, find the key in your compromised backend and decrypt ALL these collected tokens.
No need to do this with JWTs since they're unencrypted by default.
"Your self driving car has been compromised, but don't worry sir the attackers will relinquish control at some point in the next 30 seconds"
In my view JWT are fine so long as you don't really care about whatever is they are being used for.
That seems like the charitable interpretation. Note that the author’s point has not been that JWT is useless, but rather that trying to use stateless JWT for sessions is a bad idea.
If the point of using for JWT was to avoid all server side state in authn/authz flow — which is the case for many, many developers, and that is the intended audience of the author’s post — then one of two things: you either contradict yourself and implement server side revocation for refresh tokens, or you let the lifetime of your sessions be the same as the refresh token — and in the case of refresh tokens with no expiry, the user (or anyone with their refresh token) can stay logged in forever.
To me, JWT works great for things like physical people getting access to a physically isolated building, like a military research site that's disconnected from the internet. Some secure other place holds the private key and signs the JWT token then you can wave in anywhere you need to go until the validity period is up. Then you have to go back to the secure place to get a new key, verifying the same way you did the first time and the secure place can broadcast its public key to the sites you want access to. It's essentially a great mechanism where you don't have constant communication links.
But I just don't get it for servers. Also, everyone considering using a JWT token should know about the "algorithm none" setting and ensure that their stack isn't vulnerable to it.
You only have to involve state in 1 request every 10-15 minutes upon refresh. All other requests can be done without having to check for revocation. This is a pretty nice real-world tradeoff.
Nothing about adopting JWT says you can't still use a full auth check for state-changing endpoints, and use a stateless session to shave some evaluation time off of frequent read-only requests for data that isn't terribly sensitive.
You probably shouldn't do it lightly or prematurely since it makes it harder to reason about authentication, but I think it's worth considering whenever you're optimizing.
Hint: you don’t — but I figure taking the Socratic approach may be more convincing.
If you need to do revocation you need to call an external server.
If you need to call an external server why do JWT?
A common validity period is around 1 hour, which is good enough that most users won’t need to refresh very many times per day.
Also: you don’t necessarily need to track tokens that aren’t refreshable. A typical architecture might have a timestamp stored per user that specifies the earliest token creation date that’s valid for refresh. That way you can invalidate all tokens without needing to ever store them; something you might need to do for password reset, for example.
Never worked with JWTs but still, can't believe I didn't think of this. It seems like a fairly elegant way to revoke things where your only stored state is a timestamp.
There's a race condition there, though. If you have a revocation event and set the timestamp, it's possible that an in-progress refresh might have a timestamp just slightly after your stored timestamp. Then you have a rogue JWT that can continue to be refreshed.
Clocks are sometimes not in sync with millisecond precision. Databases don't replicate instantly. Distributed systems just don't work that way.
I just read about it on Auth0 [0] and facepalmed. Then I went to node-jsonwebtoken to ensure they added an ‘algorithm’ argument to a ‘verify’ call and you guess what [1].
They added ‘algorithms’, but into ‘options’, so that naive user like me could just skip it and stay open to attacks. That’s auth0’s own module, btw.
Thanks for pointing that out!
[0] https://auth0.com/blog/amp/critical-vulnerabilities-in-json-...
[1] https://www.npmjs.com/package/jsonwebtoken#jwtverifytoken-se...
unfortunately, that's not good enough when dealing with stringent security requirements like PCI where revoking a token, generally speaking, means the token is no longer valid starting the moment it was revoked.
Store CreationDate in the refresh token. Store RefreshTokenInvalidBefore in the user record. When refresh is attempted, if Token.CreationDate <= User.RefreshTokenInvalidBefore, reject the refresh. The only drawback here is that it would sign the user out on all devices. However, assuming this is because of a password or token compromise, that's likely the least of their worries.
And since the IDs in the revoked-tokens table only matter for the duration of token validity, you can prune the table pretty often, meaning necessary space needed in the DB is relatively small.
It's certainly more complicated than just dealing with stateful session tokens, so you might decide that the trade off isn't worth it, though.
What single point of failure is inherent with plain server side sessions and a session cookie?
One point to keep an eye on is, if you are using (e.g.) AWS API Gateway with a custom authorizer, it caches the authentication response for a certain time.
This is not a problem of jwt, but makes the 'logout everywhere' and every black and whitelist need some more time.
Tag them with sec-uid, sec-uid-dev-id, sec-devclass, sec-we-dont-like-user etc to group them together.
Want to kill sessions of just user uid? Hit a purge endpoint that purges all objects with sec-uid. Want to kill a session for a specific user for a specific device? Hit the purge endpoint that purges all objects with a tag sec-uid-sec-devid. Want to logout all users on an iPhone because "oops, security bug in a version we rolled out"? Hit an endpoint that purges all the docs tagged with sec-devclass of an iphone.
You don't even need to remember tokens you issued.
You don't need to know a token of jakelazaroff. You don't need to know its state. You don't even need to know if it exists.
You know that a token associated with jakelazaroff iphone if it exists will have a tag jakelazaroff-iphone because every token associated with <user> iphone will always have a tag <user>-iphone since you always tag a token of any <user> on an iphone with a tag <username>-iphone when you issue the token.
Imagine you’re writing your API on AWS Lambda, so memory and the file system are not shared between requests. A client presents your API with a token. Using no external resources (no database hits, no network requests, etc) how do you determine the veracity of that token?
Edit: I think I understand what you’re saying. Using a CDN that will let you “tag” resources and conditionally provide access based on whether HTTP headers match those tags, you issue tokens to clients and add or remove tags to give and revoke access. The CDN determines whether a given tag can access the resource, and your application doesn’t worry about authentication.
This is still stateful. The tags are the state!
I certainly wouldn't call that "stateless", but it does fulfill the main reason why you might want to be stateless, that you don't have to maintain any state yourself.
The tokens are the resource.
Your auth is what tags the tokens. Your auth is the only entity that is able to compute the tags that would match the criteria of the tokens to invalidate them.
1. It's small (id per revoked, nonexpired JWT)
2. It isn't sensitive, nobody learns anything by seeing the list of revoked JWT ids.
3. It's monotonic. You just maintain a set that you add to. You can clean up expired tokens if you want but that's not necessary for correctness.
These three items make bugs less likely and make it easier to manage consistency when you can't afford to use transactions everywhere.
In this case obviously you have a persistence layer for the keys and can't claim to be absolutely stateless.
https://tools.ietf.org/html/rfc7515#section-4.1.4
- https://paseto.io
- http://macaroons.io
I use simple http only cookies with the following setup:
This is valid for a year when you can decide if you want to renew it. Everything else stays on our end and we controll sessions, permissions, etc. Javascript (in theory) cannot access these cookies.Macaroons are a really lovely, tight, purpose-built design that happens to capture a lot of things you want out of an API token, including some things that JWTs don't express naturally despite their kitchen-sink design.
JWT is more popular because there are libraries for it in every language, and people don't think of tokens as a cryptographic design (or nobody would be using JWT!), they think of them as a library ecosystem. JWT is definitely the stronger library ecosystem!
This is also why I probably wouldn't ever bother recommending PASETO. If you're sophisticated enough to evaluate token formats based on their intrinsic design, then you should implement Macaroons if possible (it's almost always possible). If you're not, then you're going to use JWT.
My understanding is that the receiving app has to contact each third party to retrieve the “discharge macaroon”. What stops someone from adding a bunch of dummy caveats to cause *N requests on the server?
> nothing stops discharge macaroons from containing embedded first- or third-party caveats for the verifier to consider during verification.
So the user requests discharge tokens on his own, but the discharge tokens have to be verified by _my_ server and might contains nested third-party caveats which I’ll have to verify.
Either way, in each deployment a server supports a set of caveats that makes sense and rejects others.
Or that's the intention of the original idea (I'm an author of the paper). I can't speak for macaroonjs specifically.
Basically the developer is responsible for generating & distributing auth tokens for each device that they want to use our service.
This service is designed to fit into games and AR apps, so we didn't want to force our own oauth flow on the end user. This way, the developer can generate a token on behalf of the end user, and distribute it how they see fit. (assuming one token per user, but it don;t overly matter, it's still tied to their account.)
The problem with JWTs is that they are discouraged by many cryptographers due to their "cryptographic agility" and provide a mixture of protocols which includes weak ciphers and configurations to shoot the programmer in the foot. Why include insecure ciphers in the first place? The mentioned alternatives stick to a single cipher for its intended purpose.
JWTs are inefficient for performance and are bloated in their data structure and have a performance hit when you parse the token to extract its properties compared with the same operation in other alternatives (simple session cookie require zero parsing and thus is faster).
And using them for sessions poses it own pitfalls [2] which you are better off with the alternatives or plain old session cookies.
They say that JWTs are "good when used right" but with those above footguns, that's like saying C++ is safe when used right, rather than having safe defaults.
[0] https://paseto.io/
[1] https://branca.io/
[2] http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...
[1] https://ai.google/research/pubs/pub41892
It also references a new one I haven’t seen before, https://vanadium.github.io/concepts/security.html
While a session ID doesn't require parsing, it is useless on it's own; a fairer comparison would be: time to validate and parse/deserialize a JWT vs time to retrieve session data from the backing store (e.g. database, redis).
I certainly like the lack of footguns in PASETO, but I'm curious about how much real-world adoption it (or indeed other JWT alternatives) really have?
The biggest drawback is the inability to revoke tokens without giving up statelessness. Keeping a KV blacklist isn't the end of the world, especially if expiration times for tokens are short. But at that point, the cost/benefit vs cookies+sessions gets blurry.
Some general JWT tips:
* Use a sufficiently long secret key if you're using HMAC based signing (https://auth0.com/blog/brute-forcing-hs256-is-possible-the-i...).
* Use bearer tokens to avoid CSRF attack surfaces.
* Avoid long (or non-existant) expiration times.
IMHO, the problems with JWT are overblown. Yes, people have made huge mistakes implementing JWTs, but the fault lies with the implementation not the standard.
Stateless authentication tokens are always going to problems with revocation. There is always going to be a limit on how much you can cram into them. These problems are not unique to JWTs.
JWT has the advantage of being a standard with good support across languages. Other schemes might be slightly better at handling larger amounts of session data. Take your pick.
In more words, if you need to carry application state and you haven't outgrown the simple database cluster, then just use the tried and true formula of carrying state via SQL database. Pretty much every engineer these days understands how the architecture works, it's fast and reliable due to the sheer maturity of the components involved, and you don't add cryptography and maliciously crafted input attack surfaces to your auth system (see the recent talk at BlackHat 2019 about Outlook accepting unverified auth tokens). In this case using a predictable and mature approach is a good thing.
On the other hand, JWT's work well for (1) federated authentication, though revocation is still hard, and (2) carrying authorization claims. The reason is that these aren't state in the sense of application state. They're attestations of an existing fact and you're using the signature mechanism to let one service trust another without having to talk directly to each other or to protect any shared secrets. You need to send signed payloads containing user ID's and lists of claims anyway, and JWT's meet those requirements pretty well.
My understanding was that they're supposed to only last on the order of 5-10 minutes, with a fresh one created for the next set of requests instead of reusing one JWT for days. Is revocation still an issue with the expiry so short?
however, once you really care about the security perimeter, i think the requirement/wish "stateless" must be dropped.
This is why people suggest "short tokens" as a work-around, accepting a 5min window of unrevokable tokens as trade-off.
Or, one has to justify that a blacklist implementation is significantly cheaper than a whitelist one. But is it, really?
On the other hand, you generally expect that you rarely ever need to actually revoke tokens, so you can naturally expect that a blacklist will change infrequently and will remain tiny compared to the whitelist.
As the size of the service grows, most people converge to a fast train/slow train design: the whitelist is big and is accessed infrequently, and the blacklist is tiny (goes obsolete within the TTL of the whitelist query) and can be pushed out quickly.
If you're one of the tech giants, it's not slightly cheaper, it's a lot cheaper.
In my experience, usage of the black list occurred with two use cases:
1) Invalidating all of a user's tokens 2) Alteration of claims that a user's JWT should have
As far as your applications go, you still have the benefit of not having to bother with checking the state (this is already done by the gateway).
One of the biggest challenges with using JWTs is really all these gotchas that you have to think about. This isn't dissimilar to the infamous `alg:none` fiasco. In this case, it's not really about protecting against an attack, but forcing the developer on the other end to think about it. Their quick initial code that doesn't bother setting exp or has it hard-coded to 1000 years in the future will never work, so it had less chance of being left in to cause a security issue later.
In general, I tend to look at token trust as a decaying function of time, irrespective of signatures.
Wasn't the question about using them for auth, not application state?
Whether a user is auth'd or not can indeed be a form of application state
In general authentication/authorization isn’t just a binary in or out. For example, you might have to reauthenticate to access password or 2FA settings in your account, so now you have multiple authenticated states. So I made my comment from the assumption that you probably need to decorate the user’s authentication “state” with extra information for your service. In those cases, I was suggesting to only carry that info in the token if you need to cross service boundaries with it, such as if you have multiple front end micro services operating on independent data stores.
2018: https://news.ycombinator.com/item?id=18353874
https://news.ycombinator.com/item?id=16517412
2017: https://news.ycombinator.com/item?id=14290114
https://news.ycombinator.com/item?id=13865459
2016: https://news.ycombinator.com/item?id=11929267
https://news.ycombinator.com/item?id=11895440
2015: https://news.ycombinator.com/item?id=9616425
2014: https://news.ycombinator.com/item?id=8283006
>JWTs are useful, but there are a few things that are not immediately obvious. >1) They are signed not encrypted.
This is not true. JWT's can be encrypted, signed, or both (nested). One might first look to see if this misconception is the fault of a poorly written spec, but it's literally in the second sentence of the abstract and plain as day in the introduction. People are simply not reading it.
https://tools.ietf.org/html/rfc7519
It drives me mad when people refer to JWT's as basically JWS's, ignoring the rest of the JWT standard and JOSE. The fact that these misconceptions continue to be up voted shows the broad misunderstanding of the standard.
EDIT: Case in point, all the disagreements and conflicting best practices throughout the threads on this topic.
Perhaps, but in practice there generally seems to be a correlation between complexity of a specification and holes in the specification (e.g., html, pdf, etc.).
That said, the class of attacks that relied on the attacker modifying the header of the JWT could have been totally avoided by including the header values in the signature calculation.
I have mostly stuck to plain old server side sessions that are easy to revoke. If anyone is using Go, I highly recommend this library :
https://github.com/alexedwards/scs
But you have implicitly made a tradeoff. Your front-end infrastructure now has a very strong dependency on your auth server. Any minor hiccup in the auth system will blow up your downstream clients. Worse, in large sites you will be sending a ton of traffic to your auth server.
You've also introduced a source of latency to your page-load times as each request now needs to call the auth server to validate the token. Whatever the response time of the auth system now gets added to the front-end response time.
Going with tokens that can be validated on the front-end system can dramatically lighten load on your auth system and reduce your front-end response time.
The only major trade-off is you can't kick somebody out instantly. The bad-actor can do whatever until the token expires--a knob you control. And even then, for shit that matters, you can always have the front-end server validate the auth token on each request anyway.
I recently got an Apple Watch and having to just tap on my wrist to authenticate is amazing.
Would love to just be able to give a website one way to contact me and have it ping my device to authenticate with faceid, touchid, Windows hello, or an smart watch.
Basically, a magic link on steroids.
I kept the token expiry to an hour or two depending upon the usecase.
Added logic to client to refresh the token right before expiry of existing token. Otherwise, on token expiry, server returns 401 and client can attempt to refresh token or send user to login page.
If there is a need of instant revocation, then I can maintain a list of revoked tokens for the duration of token life (1-2 hours) in cache layer. After 1-2 hours token will be expired anyway.
You could build a session cache, either in memory or out of process, but it would still be more work than the token approach
JWT has its own slew of problems, most of them are temporal (eg invalidation of all sessions), usability (eg short expiry), or additional security vectors (many poor JWT implementations, accidentally authenticating invalid tokens w/o signing, careless storage of readable values)
Since you need to keep a list of invalid tokens, it's not easier to just use standard token. If what's you like in JWT is the ability to store a payload that your backend gets back, you can keep that in your store alongside the token and read it at the same time you check the token is valid.
IMO, JWT only makes sense when an other entity is doing the authentication of your clients.
Sounds like a great way to DOS a server by running out its disk or memory space. (unless there's some sort of rate limiting in place)
But why add all this complexity? Just have a whitelist, and you avoid these problems.
I think we are making the same argument, JWTs are supposed to be stateless but can't as you need to be able to logout.
> And keeping a blacklist opens up the doors to [...] running out your storage [...] because the blacklist will just keep growing.
> But why add all this complexity? Just have a whitelist, and you avoid these problems.
How does a whitelist helps you there?
If you are using JWT to maintain client state, then you are doing it wrong.
Sure, Branca may compare to just a JWT, but it doesn't compare to a JWS let alone JOSE.
If all you need is stateless auth tokens, use Branca or a good JWT library.
IMHO, the problem is the libraries, not the JOSE(JWS/JWE/JWK/JWT/JWA) standard. IMHO, after ~4 years there's still no good Javascript or Go library for JOSE, but potentially a few good JWT focused libraries.
https://blog.zamicol.com/2018/03/its-jose-not-jwt-pedantic-c...
Otherwise, blacklisting tokens as a way of revocation process seems just fine to me. Set reasonable expiration to the key, allow users to refresh keys, then when you need to revoke a key, add it to blacklist together with the expiration timestamp and expire it out of the blacklist at the same time the key itself would expire. You can do all of this by magic by using Redis for your blacklist storage and setting expiration on your blacklisted keys. Yes, you would need to check your blacklist on every request, but a) blacklist is going to be smaller than a list of every issued token (like you would with normal sessions) b) you don't even have to store the whole token, just calculate some hash and store that, now you only need to read a few bytes on every request instead of reading the whole session.
The author does make a some good points though.
Disclaimer: I work at Cloudflare
[0] - https://developers.cloudflare.com/workers/about/limits/
(Disclosure: I'm the lead engineer for Workers.)
If a single component of your application maintains a little state, say the API Gateway, and all the downstream services simply get a clean JWT they can trust with all the right claims in it?
I'm thinking this is still nicer than having every downstream service also having to check auth claims against some datastore.
Its fine to be wrong its also hilarious