Hacker News new | ask | show | jobs
by miguelmota 2315 days ago
Reasons why JWTs are not awesome:

- to revoke a JWT you have to blacklist it in the database so it still requires a database call to check if it's valid.

- JWT are to prevent database calls but a regular request will still hit the database anyway.

- JWT are very large payloads passed around in every request taking up more bandwidth.

- If user is banned or becomes restricted then it still requires database calls to check the state of user.

- JWT spends CPU cycles verifying signature on every request.

- JWTs just aren't good as session tokens which is how a lot of web developers try to use them as. Use a session ID instead.

Where JWT works best:

- when a client can interact with multiple services and each service doesn't need to do a network request to verify (ie federated protocols like OpenID). The client verifies the user's identity via the 3rd party.

- as a 1 time use token that's short lived, such as for downloading files where user gets a token requested from auth server and then sends it to the download server.

6 comments

I think they're quite good at authentication. They're less good at authorization when you want to update that faster than expiry times.

Once you go down the path of checking a DB along side the JWT your design has gone off the rails. Either the expiry works for you or it doesn't. Don't try to "fix" it.

Don't blacklist, use shorter lived tokens and have the client refresh as needed. A 10-15m token is plenty long life and not so long as it's a huge risk window, more than even a shorter window,.
Stakeholder: “so you are saying that after a user is denied access they can still access the resources?” Dev: “yes, but only for 15 minutes. Also, it makes our system more simple and decreases database calls, increase performance, ...” Stakeholder: “nope”
Meanwhile on sales call with Microsoft:

"Authentication takes a few minutes to replicate throughout our systems so a SLO request should be resolved within a few minutes". Stakeholder: Ok sounds good

+1 on this... it can be up to a 30 minute lag in some orgs... oh, you have access to those systems for a while until things sync up... similar for LDAP/AD sync with Nix/windows.
Which makes sense when I think about how some of the more trigger happy orgs made a point of shutting peoples accounts of as they were being walked into a room...
For parts of the site where you need to boot somebody instantly... just hit up the authentication server on every request to validate the session. For parts of the site where it doesn’t matter so much, wait for the token to expire....

It isn’t all or nothing.

I did exactly this on the last implementation of JWT I did. Common actions wouldn’t hit the database if the token was less than an hour old, but actions like changing email address or password would always check the database.
This just made me realize. There is an even simpler way to achieve the same result without a database.

The token includes the time when it was created (iat attribute) so critical actions could check that the token is less than 3 minutes old.

Yeah that’s what I did, with iat info. But I did that for every request, and critical actions always hit the db.
I actually have this conversation a lot, and the answer is usually "okay" and rarely "nope". Stakeholders have to weigh many more pros and cons.
Usually in one of those scenarios, you will try to hold, say a fired employee for that long in order to deactivate all their accounts and access anyway... in reality it's not much of an increased risk...

You still could blacklist, but realistically most areas don't need a dedicated revocation check. Some critical areas might, depending on the space.

Authentication and authorization are different things, if the latter is done properly a bit of waiting for the former is no biggie.
This tradeoff comes up all the time on highly scaled systems, and stakeholders rarely if ever say "nope" in my experience.
So where do you store the ones that should not allow to be refreshed? How short lived they should be in case of "Reset Password" scenario, when you need to kick out malicious user?
You can still do a redis or memcached, or even an rdbms table for that matter on revoked tokens... and do a lookup for critical systems... You don't need to do that on EVERY request though. It's really not less difficult than a session server/service/database, and more likely to scale better.
GP was talking about every request hitting the DB. A once in 10 min refresh should not be an issue.
that's the whole point. That's the trade off you make, you DON'T implement those features. When you need that, it doesn't make sense to use JWT. Right tools for the right job...
But isn't the requirement for using the refresh token that it must be kept secure? For example, if you users authenticate in a browser, they get back an access token and a refresh token. If that refresh token were stolen on the wire or from within the browser, how would you prevent someone from using that refresh token perpetually if you're not blacklisting?
In this case, you would have to use rotating refresh tokens (https://tools.ietf.org/html/rfc6819#section-5.2.2.3). They are essentially one-time use refresh tokens and help in token theft detection. So essentially, both, the access and the refresh token keep changing. You can learn more about implementation details and its benefits here: https://supertokens.io/blog/the-best-way-to-securely-manage-...
This is essentially an inverse blacklist (aka refresh token whitelist) which requires server-side state. The part that these things seem to miss in my mind, especially in situations where security requirements are high, is that the authentication process is equivalent to the refresh process.

For example, you can have a complicated authentication process where you require a password, 2FA, etc. These things help ensure that the user is that user. But once that process completes, it is replaced with access_token+refresh_token. Any user can take those item and impersonate that user. Attempts to lock this down require server-side state with the ability to revoke stolen tokens if detected.

Don't get me wrong, the same issues arise if you were using cookie-based session id's or the equivalent. But once you're doing this stateful token stuff, there appears to be a lot of additional complexity without a lot of additional benefit over the traditional way.

However, if you aren't worried about this level of security, there is clearly a benefit to using this newer style.

> to revoke a JWT you have to blacklist it in the database so it still requires a database call to check if it's valid.

The blacklist is smaller than storing every token and not needed if you use a short expiration and refresh often.

> JWT are to prevent database calls but a regular request will still hit the database anyway.

It's one less query per request plus not all requests need the database immediately.

> JWT are very large payloads passed around in every request taking up more bandwidth.

They are 100~ bytes instead of 10~, not "very large".

> If user is banned or becomes restricted then it still requires database calls to check the state of user.

This is the blacklist you mentioned as the first reason.

> JWT spends CPU cycles verifying signature on every request

Pretty sure this is neglible. Similar to SSL requests.

> JWTs just aren't good for authentication which is how a lot of web developers try to use them as. Use a session ID instead.

Opinion. I weighed the pros and cons and JWTs are still worth it for my authentication use cases.

More power to you. I prefer to use the best tool for the job which in this case are session IDs since they are simpler, have been battle tested, and proven to work for over the last two decades.
I too love the best tool for the job, which is why blanket statements saying session IDs should always be used for authentication are very puzzling to me.

HTTPS, HMAC and asymmetric keys are battle tested and proven to work as well, that was one major point of the article.

I didn't say that they should "always" be used for authentication but that session IDs fulfills most web app user authentication needs. Most devs that implement JWT treat them as stateful which defeats the purpose of them. JWT has it's use cases when done correctly.
You said "JWTs just aren't good for authentication" which is pretty definitive.

I'm not sure you read the article because the points you are making were addressed.

Session mechanisms are all too often different across every web stack though, so if you have a variety of services and applications behind a single logon, you need to solve an O(n) problem that JWT makes O(1).
There will be very few black listed tokens, and they are ephemeral. You can use memory replicated datasets, such as CRDTS or just broadcast the whole BL token list to all nodes.

For the size argument, you can use cbor instead of json. (CBOR Web Token) CWT https://tools.ietf.org/html/rfc8392

if you have gotten to the point where you need to hit the database to verify the JWT, something is wrong. Either need to turn down your JWT expiry time, or refresh your JWT more often, or both.
Not everyone needs to blacklist JWT's. If you use short sessions this will almost never be a problem.