http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...
http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...
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...
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.
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.
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.
In this case obviously you have a persistence layer for the keys and can't claim to be absolutely stateless.
What single point of failure is inherent with plain server side sessions and a session cookie?
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.
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.