1. Cookies remain valid on logout (unless you implement a session revocation mechanism). This can be mitigated by making the JWT live a very short time (e.g. 5 minutes), but you still don't get immediate expiry. This may not be acceptable if you need to comply with certain standards such as PCI/DSS, SOX or HIPAA.
2. Cookie size could grow much larger. It's generally not a huge concern if you don't stuff too much data into your JWT claims, but if you want to optimize traffic for slow networks, JWT is not a great choice.
3. Since 50% of the JWT standard (and 90% of the JWE standard) is made of broken
and semi-broken algorithms, you need to be careful on what keys and libraries you use. Unfortunately, we cannot see the implementation for this article, so we cannot judge how safe it is, but JWT just requires extra care compared to Session IDs.
4. If you're using refresh tokens because your JWT is short lived, you're not really saving yourself any work, because you'd still need a database to store the refresh tokens. This is what GP was referring too.
> using JWTs in cookies is a win because you don’t have to fuss with storing session data on the server.
This is the author's claim, but the code clearly DOES store data on the server, for the refresh token.
Using JWT for "simplicity" is unfortunately common mistake. With JWT you can choose between simple (no storage required, no token revocation) and secure (immediate or delayed session revocation at the price of requiring storage). You cannot have both. OP chose security (by using a refresh token database), but this is no longer a simple solution. If you've already deployed a DB, Session IDs would always be simpler:
1. You could remove the refresh and redirect logic entirely.
2. You would only need one type of cookie.
3. You can remove dependency on JWT libraries, choosing the right cryptographic algorithm and configuring (and securing!) JWT signing keys.
This comes at no cost, since the refresh token DB is already there.
There can be good reasons to use JWT (although there are better standards out there for stateless tokens[1]), but simplicity is not one of them. Stateless tokens can be useful in the following cases:
1. Performance (increasing throughput or reducing latency by skipping the database for most calls).
2. Reliable cross-regional tokens (you don't need to replicate session IDs across regions and suffer from inconsistencies during failovers).
3. Decentralized verification (without relying on a central database).
4. Offline attenuation without network calls (with Macaroons or Biscuits only).
Unfortunately it seems like in 99% of the cases JWT is chosen as the access token format, it's chosen by its perceived simplicity and popularity, and the result is either a toy implementation that doesn't support token revocation, or an implementation that is more complex than it would have been with classic Session IDs.
> This is the author's claim, but the code clearly DOES store data on the server, for the refresh token.
You're right, but I was making a different point about session IDs versus JWTs.
With session ids, each user request requires a server-side lookup to validate the session. For that reason, their ideal place would be something in-memory and that's what I was referring to with "data on the server".
While storing session IDs in a database is an option, in my case it would introduce noticeable latency because I self-host my projects on a cluster of Pis at home and even though I have a fast connection, a roundtrip to my external (I don't self-host it) database still takes a few milliseconds under low load.
JWTs allow me to avoid frequent server-side lookups. I can trust the client's data without hitting the database, except when issuing a new JWT - but even then, that happens every 2-3 minutes per user. While verifying JWT signatures and decoding claims does consume some CPU cycles, this overhead is minimal on my setup compared to the latency of database roundtrips.
Nothing against session ids, but I feel JWTs are better suited for my resource constrained setup.
1. What if you store the JTI in your database and have the ability to immediately mark them as invalid, thus so that the next user request makes them logged out?
2. Storing just the JWT and things like user id should not be that big of deal for user performance. If you're refering to for example Apple sending massive jwt payloads for their IAP service, then i can see what you mean.
3. A standard has broken algorithms? This is news to me.
4. Don't most apps have databases? I don't see why this is a bad thing.
If JWT in httponly cookies are bad, what do you suggest inplace of it? For companies running multiple mobile + web apps
2. Cookie size could grow much larger. It's generally not a huge concern if you don't stuff too much data into your JWT claims, but if you want to optimize traffic for slow networks, JWT is not a great choice.
3. Since 50% of the JWT standard (and 90% of the JWE standard) is made of broken and semi-broken algorithms, you need to be careful on what keys and libraries you use. Unfortunately, we cannot see the implementation for this article, so we cannot judge how safe it is, but JWT just requires extra care compared to Session IDs.
4. If you're using refresh tokens because your JWT is short lived, you're not really saving yourself any work, because you'd still need a database to store the refresh tokens. This is what GP was referring too.
> using JWTs in cookies is a win because you don’t have to fuss with storing session data on the server.
This is the author's claim, but the code clearly DOES store data on the server, for the refresh token.
Using JWT for "simplicity" is unfortunately common mistake. With JWT you can choose between simple (no storage required, no token revocation) and secure (immediate or delayed session revocation at the price of requiring storage). You cannot have both. OP chose security (by using a refresh token database), but this is no longer a simple solution. If you've already deployed a DB, Session IDs would always be simpler:
1. You could remove the refresh and redirect logic entirely.
2. You would only need one type of cookie.
3. You can remove dependency on JWT libraries, choosing the right cryptographic algorithm and configuring (and securing!) JWT signing keys.
This comes at no cost, since the refresh token DB is already there.
There can be good reasons to use JWT (although there are better standards out there for stateless tokens[1]), but simplicity is not one of them. Stateless tokens can be useful in the following cases:
1. Performance (increasing throughput or reducing latency by skipping the database for most calls).
2. Reliable cross-regional tokens (you don't need to replicate session IDs across regions and suffer from inconsistencies during failovers).
3. Decentralized verification (without relying on a central database).
4. Offline attenuation without network calls (with Macaroons or Biscuits only).
Unfortunately it seems like in 99% of the cases JWT is chosen as the access token format, it's chosen by its perceived simplicity and popularity, and the result is either a toy implementation that doesn't support token revocation, or an implementation that is more complex than it would have been with classic Session IDs.
[1] https://fly.io/docs/security/tokens/