Understand JWT in 3 Minutes
tahazsh.com
tahazsh.com
Joepie91 once did a small nodejs project for me, that's why I recognized your link.
I'm kinda worried as "not an acquaintance" about his post about depression and that he didn't post anything since 2016.
Anyone know what happened?
His GitHub still seems to be active (https://github.com/joepie91).
I just haven't updated my blog for quite some time, due to a combination of circumstances. I'll probably start blogging again at some point in the near future.
Some useful parts of JWTs is they can contain information that can act as a cache so that you don't have to go to the server to show certain parts of your UI or things like names and so on.
It's worth evaluating them for your use case rather than assuming they are 100% bad because of the reasons outlined in the article.
That doesn't really help, because now you need some other sort of token to authenticate the user for handing out a new short-lived token, and that new token now has the same inability-to-revoke problem. You've just moved the problem.
> Some useful parts of JWTs is they can contain information that can act as a cache so that you don't have to go to the server to show certain parts of your UI or things like names and so on.
You don't need a JWT for this. You can just store that in localStorage directly. JWT is not a storage mechanism, it's more of an encoding format.
No you don't, as stated you can refresh your token before it expires.
> You don't need a JWT for this. You can just store that in localStorage directly. JWT is not a storage mechanism, it's more of an encoding format.
I think you can validate the authenticity of your JWT without having to go to a database. This is much more secure than local storage or even a plain old cookie.
JWT provides a useful set of features and just because in your opinion it isn't useful plenty of people disagree with your points.
And if for some reason you have a weird freak edgecase where you really do need a cryptographically signed stateless token... JWT is unsafe[1] and you want PASETO[2] instead.
[1] https://paragonie.com/blog/2017/03/jwt-json-web-tokens-is-ba...
Edit: To be clear, with "API" I'm assuming you mean "third-party API", in the original sense of the word. If you just mean "a bunch of endpoints for my SPA", then you should use none of these things, and just use sessions. It's not really an "API" in the "API key" sense.
> A: Typically, you would store it in the localStorage, after the user logs in and gets the token.
This seems to contradict Auth0's guidance to not store tokens in localStorage since it's vulnerable to XSS: https://auth0.com/docs/security/store-tokens#don-t-store-tok...
Gives various suggestions depending on your use case.
> A: Yes, it will be valid forever unless the server specifies an expiration time when generating it.
The implications of this is beyond calamitous. This is why Disney+ and Fortnite logins were hijacked in the first place. I can harvest all these JWT logins with a good chance of people forgetting to ever logout. Hence this, with one push of a button I can abuse an API endpoint to mass delete everybody or what not. Best part about JWTs? They are unencrypted by default.
For that reason, Just do not use JWTs.
Citation? I'm in total agreement with the problem of unexpiring tokens, but last I heard the suspected culprit in Disney+ was credential re-use. If there's newer info, I'd love to get a pointer to it.
> For that reason, Just do not use JWTs.
That seems a bit sweeping. You can apply the "unencrypted by default" dilemma to just about everything. Noting that you have to take an extra step doesn't invalidate the entire concept.
From what I can tell most people implement a blacklist of invalid tokens that need to be expired early. This isn't exactly the same as a traditional sessions table since it turns the problem on its head. You're likely to have a lower number of early expired JWTs than valid sessions.
The other way you might be able to solve this is to generate a per-user JWT key and use that to sign the JWT or embed that in the JWT. When you need to force a log out, just regenerate that value for that user.
When a client tries to use an expired JWT the request fails, and the client will then exchange the refresh token for a new JWT/refresh token pair, and finally retry the request with the new token.
The refresh operation can reject the request if the user has been deactivated (it's basically a new login request, using the expired JWT as the username and the single-use token as the password).
Presumably, the token and refresh token are both stored in the client-side app. If that gets compromised, the attacker now has the username/password combo they need to restore the session after the T+5mins has expired.
But I agree it's not a perfect system. This is meant to specifically address the problems of long-lived tokens, since JWTs are hard to revoke without checking a blacklist on the server-side.
The main problem is that localStorage is more vulnerable to some classes of attacks than secure, http-only cookies.
The moment you store data with Javascript it will be visible for any Javascript.
That's not a problem. It's literally how they were designed to be used: expiration time and nonces.
JWTs, like most things, work perfectly fine if implemented properly and you understand what you're doing.
Again, it's not about the tool, it's how you use it.
Just to be clear, JWT do support expiration time and nonces.
For implementations to be vulnerable, they need to intentionally avoid standard security practices.
The tool isn't the one to blame.
The right thing to do should be the easy thing to do. There's little "easy" about the "proper" use of JWT.
You're the one using JWT. You can't blame the tech if you screw up how you apply it.
> you have to ensure that the library you're using is doing sane things
Isn't this concern observed in 100% of cases where a third-party lib is used?
> The right thing to do should be the easy thing to do. There's little "easy" about the "proper" use of JWT.
That is not true at all. It's quite simple: either you know what you are doing or you develop broken software with security problems. If you decide to adopt a security technique and then fail to learn the very basics then the tech is not to blame for the problems you create for yourself.
Fernet [1] was close to being an alternative but I've seen that even Branca [2] tokens look much more like a saner alternative for replacing JWTs.
[0] - https://paseto.io
[1] - https://github.com/fernet/spec
[2] - https://branca.io
Enforced session state consistency creates a hard runtime dependency on the backend service that provides that data and that service needs to scale to handle every single client request with low latency because the token to session state lookup dictates a floor on the response time of every other service you can possibly provide.
It's not "wrong", of course lots of systems work like this, it has some advantages!
It's just a tight runtime coupling that constrains how you can scale in some ways.
The way I think about it is, if you have jwts you can always add additional "hard consistent" session state in the way you describe, but you're not forced to use it for everything.
You have options, you can make tradeoffs. And in engineering, room to make tradeoffs is a great thing to have.
As an example, with basic jwt sessions you can write say, a non-sensitive geodata lookup service for frontend (don't really care if user session is "valid" for an extra minute after "logout") in a lambda say, scale it to bejesus, but still be able to attribute requests to a user and do rate-limiting, and it has no availability dependencies on anything else.
Ok sure though, maybe change profile info (a low volume request), you need to eat a hard invalidation lookup.
Granted, its use case leans towards big systems, or systems that cross boundaries between orgs.
I do see use cases for JWT for machine-to-machine auth though.
The chief difficulty this introduces is manually invalidating tokens. You end up having to store some data and/or ask some other system questions about users (blacklist-related, in either case) after all, reducing the initial benefit. But distributed sessions will run into similar problems anyway.
Basically if your auth lives the same place as all parts of your application the user will ever see, don't bother with it because the benefit is little to none, otherwise, JWT's a solid maybe.
I suppose I'll never learn.
(looks hopefully at the HN crowd)
This can be useful for e.g. checking login status at proxy or cache servers without having them connect to a database or ask other systems (so, works very fast), for microservice bullshit, or for systems with lots of access points and unified login (think Google, with Youtube, Gmail, Docs, and so on—I don't think they use them, but that's the sort of situation in which one might)
The main downside is that you can do stupid crap with them like not set an expiration, and you can't really expire tokens prematurely (say, on user role changes or on user deletion) without re-introducing some centralization. Neither are crippling but they do make it less the magic bullet than one might hope.
JWT allows you to use the browser as the fatter client that it has been slowly growing into. Because JWT is JSON, it can store anything, and you're only limited by how large you want to make your web requests. (they're sent with each request)
At the end of the day, they're both ways to build state into a stateless protocol.
What I haven't seen though, is guidance on how to deploy SPA web apps using cookies properly (with either jwt or session token in http only cookies)
From what I have found, there are only a few options:
1. host api at 'api.mydomain.com' and frontend from 'mydomain.com'. You will have to deal with CORS and options requests which can add significant latency for non-simple CORS requests. Not to mention extra configuration with CORS headers.
2. Serve assets/frontend from the API (ie, rails static assets, Django collect static, etc). Downside is that you have to deploy everything together.
3. Reverse proxy so that api/frontend are on the same domain, and you can use cookies without CORS. Api lives at 'mydomain.com/api' and frontend at 'mydomain.com`
I am currently doing 3, which if you are using AWS, you can use CloudFront as the reverse proxy by using a separate dristriburion for your api and your Frontend and using behaviors to route traffic by path. In fact, that is what AWS recommends: https://aws.amazon.com/blogs/networking-and-content-delivery...
So for 3, for example:
You have CloudFront -> Loadbalancer -> EC2 for your api with a path of /api in CloudFront, you can set CloudFront to never cache. You don't pay for bandwidth between EC2 -> CloudFront, you only pay for bandwidth out of CloudFront:
>Outbound data transfer charges from AWS services to CloudFront is $0/GB. The cost coming out of CloudFront is typically half a cent less per GB than data transfer for the same tier and Region
For your frontend, you can host in s3 -> CloudFront with a catch all path for non-api paths (allows client side push state to work in SPA).
CloudFront in this case also is where SSL termination happens. You can also to full end to end ssl if you enable that in the CloudFront and your API can also terminate ssl at the LoadBalancer.
The other benefits to this approach is that you can usually just use the default auth systems for your web apps. You can also create a CNAME for your API loadbalancer to be api.mydomain.com and use that for non-web clients. In the case of Django Rest Framework, there is both cookie and token auth enabled by default.