4 ms·
> What a flawed argument, Touché! > session revocation, even in an async stateless jwt context > blacklisting But isn't blacklisting stateful in its nature
by cygned 9y ago
> What a flawed argument,
Touché!
> session revocation, even in an async stateless jwt context
> blacklisting
But isn't blacklisting stateful in its nature and thus achieving the opposit of what JWTs are for? Am I missing something obvious?
- kodablah 9y agoJWT's are just special signed formatted strings with a couple of dots in the middle. I only use them for API tokens, and I don't use them statelessly, I just use them so the client knows the format and can check expiration inside it. Sure many use them to pass around signed state, but that's a choice. They're just a container for a stateful session ID for me.
- dwaite 9y agoThere's always state. Stateless is a misnomer, in much the way that serverless is a misnomer. It typically indicates the state information is passed inlined with the request. When you inline the state in a cookie for "stateless" operation, you are essentially operating on a (hopefully cryptographically secured) cache. Revocation is thus a cache invalidation, and requires its own state. When revoking from a database or memory, you can simply delete or mark the record. When revoking cookies, you are going to build that state mechanism as a blacklist. This is why OAuth lets you have both access and refresh tokens. Your access tokens can have inlined state with a short duration, such that an API can service a request without having to be bottlenecked in a database or API call against an authoritative source. However, when that access token expires (say, after 10 minutes), the API will stop accepting it. That is when you have to use the refresh token, which goes back against the authoritative source. You lose the immediacy of a blacklist, but you don't have to have that state distributed across your infrastructure. You instead wind up pushing the effort to keep up-to-date tokens onto the OAuth clients.
- davnicwil 9y agoI think you have this right. This was also my experience with JWTs - go far enough down the revocation rabbit hole and it seems you just end up with a stateful solution again, but just with a more complex and expensive token verification mechanism (compared to just equality checking the token value). At that point, it really seems pointless.
- theprotocol 9y ago>you just end up with a stateful solution again It checks the "stateful" box in a nominal way, but it does not have the drawback of stateful session cookies that "stateless" defines itself in comparison to: in the backend, the session is still not in-memory or in-db on a single machine. So you don't really go back to "stateful" except nominally; a very large part of the scaling benefit remains.
- davnicwil 9y agoLet me be more concrete, maybe it will illustrate the point I'm trying to make better and/or highlight some aspects I'm wrong about. I'm aware of broadly two schemes for revocation support with JWT: 1) Immediate revocation - keep a blacklist in state on a single machine. On verification of the JWT, check the blacklist and only succeed if it is not present. 2) Eventual revocation - keep a renewal token in state on a single machine, and give your JWTs an expiry time T. On verification of the JWT, if verification fails due to expiry then try to renew the JWT using the renewal token, and if successful then succeed the verification and also return the new JWT to the client. To revoke the JWT, just revoke the stateful renewal token. The JWTs can therefore be revoked eventually within a max time of T. -- So, the above Vs the classical stateful session token (from here SST) approach: Both 1) and 2) contain the same 'single stateful machine' limitation as SST, yet are more complex to understand, reason about and maintain. In 1) you get no additional benefits over SST, and you have completely nullified the statelessness and therefore scalability of JWT. You also get the additional downside of a more computationally expensive token verification than just a simple equality check. Conclusion - it seems irrational to use 1) over SST in all cases. -- In 2) you get precisely one potential benefit for scalability - that for a time (max T) you get a stateless 'cache' for the verification of the JWT, which will horizontally scale to infinity. This MAY give you some practical benefit at massive scale, but there are two significant downsides: A) You have a max lag T on revocation of the JWT, which is a security hole if the JWT is stolen. So you'd like to minimise T - but not so much that you lose the benefit of the 'cache' - otherwise the whole thing is pointless. You have to literally trade off security for scalability! Where do you draw that line? B) The whole system is very much more complex to operate, debug, and reason about, than SST. You have verification logic that is split across 1 central stateful renewal token server & n JWT verification servers. Conclusion - the scalability gain of 2) comes only as a trade off against security. Even assuming you are able to accept this trade off at all it is really unclear to what extent you should do so, and indeed if the whole complexity of the thing is even worth it after all that. -- Perhaps there is another way to do revocation of JWTs that I've missed that is clearly superior to SST, maintaining stateless verification without any clear security trade offs. If so please educate me - I would love to discover it! Otherwise, I concede that scheme 2) might be a better option than SST in some very rare and special cases, but in general if revocation is needed, then JWT does not make sense in place of SST.
- gizzlon 9y ago> "But isn't blacklisting stateful in its nature and thus achieving the opposit of what JWTs are for?" JWTs are really just the format, and you can store them however you want to. What you want is to avoid having to query a centralized database or datastore for every request. (1) It's hard, but doable, to design a blacklisting scheme that does not depend on a centralized db (2). This way you can have your cake and eat it too. You avoid the "ask the database for every request" scenario. People will counter with "but you query the database for every request anyway" to which I answer: "No, I do not" :) 1: Well, that depends on your app and usecase, of course 2: Basically you push the blacklist, which is short, out to everything that needs it. It gets complicated =/