HACKER Q&A
📣 CoffeeOnWrite

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?



👤 munchbunny
In my experience, don't use JWT's for carrying application state.

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.


👤 altmind
JWT were aiming to be stateless, yet they cannot be stateless if you need them to be revocable. If you keep using plain sessions you got a SPOF, but you are immune, for a whole lot of consistency and cred management problems.

http://cryto.net/~joepie91/blog/2016/06/19/stop-using-jwt-fo...


👤 rvz
Today I would still use alternatives like PASETO [0] or Branca [1] tokens which the latter is a fernet-like secure alternative to JWTs.

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...


👤 013a
I think, philosophically, they're interesting, but in practice the statelessness ends up not being a huge benefit.

In the few deployments I've seen, you end up observing this pattern of, first, not enough information is placed in the JWT to eliminate stupidly common database calls on every request (example: "the JWT contains a UserId, but we need a RoleId that we store on the user document to determine their authorization level. for, like, every API call.")

Ok. So, two options: you can give up, admit JWTs were a weird choice, and just do everything possible to make those common database calls fast (which is a very solvable and scalable problem); or, you can go down the rabbit hole. Lets put a RoleID in the JWT. But, really, a RoleID doesn't tell us anything about the user's authz; it saves us one table join, but we need the underlying permissions/claims/authorizations/etc. We could put those in there.

In other words, "stateless" is a misnomer. You just moved the state; from a single, consistent, and scalable database, to a "distributed database" where every node is a JWT held by your users.

JWTs are probably fine. I just think the most claimed benefit they offer is mostly overblown. But, in the 2% of use-cases where that benefit could be exploited to the maximum, they're worth using, and they're also "fine" in the 98% of other use-cases.


👤 authaway
JWTs aren't just bad because they embody poor cryptographic engineering, but they are also bad because stateless authentication in itself is poor security engineering for almost every application.

> 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.


👤 jrockway
I am fine with using JWT-alikes for bearer tokens. Good for one request or a very time-limited duration.

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.)


👤 DGAP
If you like Ptacek, read this: https://latacora.micro.blog/2019/07/24/how-not-to.html

But more importantly, why do you need stateless auth tokens?


👤 lightwin
JWT helped me to achieve authentication and authorization without needing to make a trip to database on every API call. I use to store user role code in the token.

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.


👤 ceejayoz
Frankly, I just went back to sessions.

Everyone thinks they're building the next Facebook, but most of us aren't gonna stress out even a single Redis instance.


👤 AndrewStephens
Who cares what the current thinking is - define your problem and implement the solution that makes the most sense to you.

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.


👤 codegeek
One of the secrity experts here at HN always recommends against it for auth. Go to the search box on HN and type "author:tptacek jwt". Make sure you are searching within comments. You will see his multiple responses over the years.

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


👤 madhadron
My sentiment: use sessions in cookies the way people have for years if what you're doing is managing sessions.

There isn't really such a thing as stateless auth tokens. For authentication you need revocation. For authorization, you can't stick that in a token that you send out because permissions can change. So you end up having state that you distribute everywhere anyway, so do it in the easiest way possible. Scaling fast lookups of a session key is much simpler problem.


👤 StreamBright
I think there are two great options:

- https://paseto.io

- http://macaroons.io

I use simple http only cookies with the following setup:

    "cookie_name=cookie_value; Domain=sub.domain.com; Path=/; Secure; HttpOnly; SameSite=Strict; Max-Age=31557600"
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.

👤 MadWombat
Everyone seems to be posting the "stop using JWT" article from 2016. It has a lot of merit, but I think there is small logic hole in it. When the second part attempts to address the issues of token revocation, its only argument is "what if your blacklist is down?". But that is silly. Blacklist is part of your authentication process, if it is down you don't authenticate. Its like asking how would you serve your API if your API server is down. You won't, your server is down.

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.


👤 andyroid
JWTs and sessions are not in any way orthogonal in a system as a whole. We use JWTs primarily for scalable backends - if a session is setup or not in the front end is not a concern for the API backend services. In a microservice environment there could be many of these - that’s the whole point after all. And with JWTs being sent to these they can simply verify the integrity of the caller without asking a central authority. Coupled with OAuth2 and OIDC as protocols for delegation/federation and you have a system that scales extremely well. Of course, it might not be the first concern that needs to be addressed in a small startup but that doesn’t mean it doesn’t have a place.

👤 Znafon
In my experience, the idea that JWT is stateless is wrong. When your client log-out you will need to keep the token in a blacklist and check all requests against it to make sure it is not reused until it expires.

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.


👤 jrobn
We need FIDO2 or some other standardized security key protocol in our devices.

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.


👤 hashamali
JWT bearer tokens have worked well in my experience, though I haven't used PASETO. The main benefit is being able to use the same authentication mechanism for mobile and web apps.

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.


👤 gremlinsinc
I was torn, but recently I found Inertia.js and love it. It uses server session for auth, and makes vue a bit like turbolinks, and I'm able to use vue without router/vuex, so for a fullstack but better at backend guy like myself it makes more sense. Look at their pingcrm for a good intro using laravel.

👤 andersonmvd
Just use stateful tokens because if you are dealing with sessions and you are doing it right, you WILL want to revoke them at the user's will or at some suspect activity in a session.

I've wrote how to revoke a JWT (https://dadario.com.br/revoking-json-web-tokens/ - caveat: requires a db lookup), but if you are going through the trouble of revoking a JWT, just use a stateful session instead. It's way easier.

You can use JWT to pass along user information to other components within your architecture, that's fine :)


👤 bfrog
I feel like JWT is a poor version of aead cookies like rails uses.

👤 mooseburger
I'm not sure I understand the people claiming you should use server side session tokens instead. How am I supposed to make that work with Angular? I have to be able to send to the client the user privileges somehow.

👤 lifeisstillgood
I would be grateful if anyone could point to a "how to build a PKI based (client and server certificate based) ecosystem"

👤 rolltiide
Where’s that guy from that Oauth company that always pushed JWT, I think he was with Okta

Its fine to be wrong its also hilarious


👤 blackflame
The biggest thing to worry about is a hash length extension attack. https://blog.skullsecurity.org/2012/everything-you-need-to-k... This requires avoiding the use of a MAC hashing algorithm such as sha1 or even sha256 because it's possible to append data and generate a valid signature from a previous oracle. Alternatively, you could use SHA2 to generate a 512-bit key and then truncate the key to 256 or use an HMAC

👤 TomMarius
Even if your tokens are stateful, they should be signed.

👤 markandrewj
This isn't a technical answer, or me saying that there are not better options, however with JWT's being one of the corner stones of Oauth and OIDC, it would be hard to argue that there are not successful implementations.

👤 KaiserPro
We use encrypted JWT tokens. I'm not sure it was the best choice, but it was the simplest for our needs.

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.)


👤 vbezhenar
I'm doing everything by hand. Like byte array with timestamp (4 bytes), IP-address (4 or 16 bytes), user id (4 bytes) and so on, then this array is signed and converted to string using base64. I looked into JWT and it was absurdly long. I don't like to waste bytes.

👤 Zamicol
JOSE and COSE fill a void not filled by anything else on the market.

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...