Hacker News new | ask | show | jobs
by kylebyproxy 1357 days ago
I feel like this point gets overblown. Subscribing to an "invalidations" message queue seems pretty straightforward. What am I missing?
4 comments

Yeah that’s what we ended up doing but do hate that everyone who encounters this problem has to cook up their own solution for a scenario that’s very common.

It’s not terribly difficult to do because most JWTs should only last 10 to 15 minutes. We just broadcast the “jti” of the blocked JWT along with its expiration. The recipients just hold it in memory for that duration. That’s a relatively compact message so it’s quite simple to hold for 10 to 15 minutes.

One of you mentioned that it fails open. True but it’s mitigated by the fact that it’s only alive for 10 to 15 minutes (which you can still do a lot of damage with depending on scenario) and we repeat the message over a few times. In our case, it’s just being extra cautious since we’ve never seen our message bus fail. And if it does, functionality is impaired anyways for many of our services so it also inconveniences the attacker too. Maybe your scenario is different but in our case, it’s more than good enough. The attacker has to line up multiple holes to even get to this point. Or put differently, if an attacker is going to succeed in penetrating, it won’t be because we failed to receive a log out message from our message bus after someone has already logged out but before the JWT expired.

Can you explain what this looks like? So each application that authenticates its requests using the jwt subscribes to a message queue and replicates the data in some database local to it? Then it checks that database each time a new request comes in?
Pretty much, with the type of local db/cache changing based on the amount of concurrent invalidation you expect.

The theory is that your list of prematurely invalidated tokens (expired by user before the token's own expiry date) is much, much lower than your active tokens, so you only have to check a requests JWT against this tiny subset, rather than every active session to confirm it doesn't exist.

It has its own unique failure modes where an invalidation doesn't make it to all listeners, so either you can expend effort to make it more robust (and it's again, less data to sync than the sum of all active sessions) or just live with some parts of the system allowing the token for a few minutes..I

Typically, unless you are at a very large scale, or dealing with offline clients, I'd stick to traditional cookies + session.

That makes sense. Thank you.

Honest question - What would you do to justify doing this over using cookie based sessions? Like, are there back of the napkin calculations you could do to find when one approach becomes comparable in performance to another? That's one thing in system design that I struggle with.

Depends on how many invalidations you see. Some systems can get away with a message queue with an in memory copy of whatever's still in TTL with a bloom filter in front to make for a really cheap check. If you have more invalidations than a simple library implementation can handle, you do a similar thing with a DB like Redis sitting off to the side.
An invalidations queue will fail open if the queue does not deliver. If you check a session token against a database and the database is down, you fail closed.

There are systems at scales where you have to take that hit, but the overwhelming majority of people don't run them.

How many do you retain?
Not that many since they only last 10 to 15 minutes so you can just set the TTL to that. Also, you only need to retain the “jti” of the token and not the entire token. So you can hold quite a lot of these jti of tokens even in memory which is what we do. And then after 15 minutes they are ejected from memory.