Why to not use JWT (2021)
apibakery.com
apibakery.com
Way to ruin the joke! The joke is that that are only two hard things in computer science: cache invalidation, naming things, and off-by-one errors.
See, now it's a joke.
1st Cache invalidation
3rd Concurrency
2nd Naming things
4th Off-by-one errors
Also, nit: those are ordinals (since they specify the order). Cardinals would be one, two, three, four.
It’s a joke because it’s not literally true – the other problems in computing can be incredibly hard, sometimes taking years of work to figure out – but like any good joke it does also have a kernel of truth and hopefully makes the listener do some reflection.
Adding “off by one errors” is a riff which only makes sense once people are already familiar with the original joke. It can be funny but also kind of misses/obscures the original point.
Then again, she had just (again) made an off-by-one error in her Python code, which she had variables named "stuff" and "things."
> Sessions were built on top of cookies. The server kept context about the user's "session" [...] and indexed it with a random session ID. The session ID was sent as a cookie and for each subsequent request, server looked up the session state.
> Then came mobile apps and single-page JavaScript apps, and plain cookies and sessions started being inadequate. Developers started using tokens, where a token was an unguessable random string that behaved like an identity card. Possession of it proved your (client's) identity (bearer token).
Both a session ID and a token are random strings clients send with requests to indicate who they are. Why were sessions inadequate, and how do tokens solve this problem?
>Why were sessions inadequate
I suspect there are more reasons, but one is likely CORS and the tendency for the auth infra to be separate from the app infra.
JWT: Server doesn't keep a list. Someone comes with a JWT, which is signed by the server. Server grants access purely on the fact that there is a signed token.
JWTs are useful if you don't want to sync the server-side list of valid session ids (many microservices for example).
Opaque sessions are smaller, though.
JWT tokens are not random strings. They contain claims about the bearer that are cryptographically signed to prevent the bearer from altering them.
JWTs are a signed claim that your service can trust, and use for authorization after just verifying the signature.
A session id, you need to take the id and look up your session in some mechanism on your backend.
JWTs are great for different services saying 'yes, this connection is actually this person, trust them.' Inter-server communication, oauth, etc, are good examples.
Session ids only work for the issuing service. Django sessions, php sessions, etc, are good examples.
One of the biggest problems with JWTs is that a lot of people are ONLY storing a user_id, or a session_id in them. On the server the decode the token and proceed with the standard session_id workflow, still hitting the db.
Invalidation is another thing. You still end up hitting the database, and if the JWT is only ever used by your own service, why bother?
Then there's people just shooting themselves in the foot and not actually signing them and just trusting them without any crypto signatures.
You would be surprised, a very short time ago I saw a (pretty large/common) web FinTech service provider that included the private key to verify the signature encoded within the JWT. It was SHOUTING to be hacked. I don't understad how people can be so irresponsible.
People do some dumb stuff. And the dunning kruger effect is all too real.
No it doesn't make sense.
a JWT signature is the header and payload encrypted and attached. You would do that encryption via a private key, said public key would be used to verify it was encrypted by the private key.
What does "to SIGN" mean in this context?
The right thing to do is use web standards (like OIDC) and libraries and existing tools (keycloak, ory suite, et al), same as with cookies. You can do the wrong things with both JWTs and cookies, and any other client-identifying technology.
The problem is that these tokens should not be used by your JS code (they should never make it to the browser for security reasons). So how exactly does a user access their data if the front-end code running on their browser does not know the token? That's where sessions come in. The webapp hosting the front-end JS code (for example Node.js or Java-Spring) creates a session whenever a request is made. When the user logs in, their token (which the server retrieves though whatever authentication method is being used) is stored in the webapp as session data. That is to say, for each session, the webapp can store information about that session.... e.g. the token. The token is then used by the webapp to access the user's data via the API calls to the backend. The backend, will then use the token to verify the user has access to the requested data (or the requested resources).
A bearer token, in my experience, is a cryptographically generated random key (presumably unguessable) which the user can create, use and revoke at will.
Let's imagine a call-graph budget of 300ms. Every hop after the request lands on the back end takes a bite of that. Let's imagine a straight stack of calls three deep with no branching. Front-end makes a RESTful API call to Service A. Service A calls B, B calls C, and C queries a database.
Diving down to the bottom, the database call + networking will take 50ms (it's a big query), service C spends 5ms filtering, and another 10ms reorganizing the data into its response format and returns. 65ms eaten.
Service B deserializes (3ms) and acts on the data (10ms), then serializes the response (2ms). 80ms eaten.
Service A gets this back and chews on it for a bit to put it into shape for the front end. There's also a layer of proxies and caches the request and response goes through, so another 20 ms here. 100ms! Great! We're under budget (because we're not counting network latency back to the UI)!
Now imagine, that for every one of these hops, we add a 10ms call to a session service, and processing time (hopefully tiny) to review, and error handling. We've also only covered happy path, we've not considered call retries, branching calls to resolve additional data, each with a +10ms check for that session.
Now that time budget is starting to chafe. Validation of signed tokens with claims eliminates that 10ms tax on every call, and it is a big deal.
How do I know? I work on a system with hundreds of microservices. We use JWTs for back-end authentication. Thank goodness the article clears us to use JWTs. :)
With a bearer token each microservice still needs to call out to an auth service to validate the token, right?
If you have an opaque token, then yes, you save nothing and still require a call to a central service.
In doing so you just validate the token against the public key. You can then rotate these keys and have a list of them to validate against and age off keys which would be the last tokens expiration +1 day.
----
i've been wondering the same thing. the only explanation i have are life times and explicitness. sessions are usually based on the browser session, i.e. cookies without an explicit expiration date are invalidated if the browser is closed. also, session cookies are always sent on any request (simplification) to the matching hosts. this means you have to re-login to get a fresh session every time you reopen the browser (unless you set an expiration date for the session cookie i guess).
i guess bearer tokens have a very long life time independent from the browser session and the user can invalidate them manually through some settings option. moreover, the token is added to the relevant backend calls manually (instead of automatically by the browser).
i'm not sure though how a bearer token would prevent CSRF. imo it's roughly the same as a session with a long expiration time.
You said it yourself. Bearer tokens have to be added manually, while cookies are automatic. So simply issuing a request to a backend will not include the bearer token, and you (as the attacker) have no way of actually gaining that token from your cross-site context.
ok, now that i think about it: providing an url that triggers the (malicious) action is a necessity for session based auth but only a possibility in token based auth.
So XSRF protections make it so that third party content cannot successfully initiate those actions. Prime examples are:
- SameSite cookies, where the initial request from another domain will not have cookies presented.
- The local content adding session-related parameters unknown to third parties, such as an OAuth access token in a header or a session cookie repeated as a form parameter.
These protect against the third party being able to initiate actions and pick up any background implicit authorizations being provided by the browser, such as session cookies or HTTP Basic authorization headers.
That's it. That's all they are. If they're not passed in that manner, they are not a bearer token.
Session IDs are bound to cookies from a given origin. Thus, every request needs to include the original cookie, and have the same origin, to transport authentication data. But cookies cannot be accessed from JavaScript, only be sent implicitly if these things are true.
This made it difficult to work with separate hosts (www.my.app vs. api.my.app).
In contrast, Bearer tokens are explicit: JavaScript code can use and attach them to requests as appropriate, thus allowing for more control. The also are a little easier to work with (no reason to keep a cookie jar, stateless stuff works).
JWTs in turn aren't simply random strings anymore -- they contain some claims about the bearer, which are cryptographically signed, so whoever receives the token can be sure the claims haven't been tampered with. This allows for very powerful, interconnected services -- but most people use them to pass a session ID around, so... here we are :)
This is still possible with session cookies if you use wildcard origin cookies i.e. *.my.app
Also randomly generating them would result in colisions if you had a lot of traffic.
There's really no difference between a random session id and a random token. But people typically think "session id == cookie" and "token == header".
More secure session cookies are also HTTP-only, which means JavaScript code can't access them. Which can be a security feature in a lot of cases, but makes SPAs harder.
Session cookie is invisible to JavaScript, so handling unauthenticated state is a bit weird.
Bearer token is visible to JavaScript, so SPA can make some assumption about authentication status. However, it still must handle 401s because token could have been revoked or expired (JWT solves the expired problem). You also could encode some metadata like username and email and instead of having a single round-trip to fetch it once, now you will be sending kilobytes of cookies with every request.
CORS is a black magic to them. How to properly set cookie is also back magic - who would know that you can set cookie in way that would include subdomains, so it will be sent to www.acme.org AND to api.acme.org.
It was also weird to set a session cookie on outgoing requests when service a talks to service b.
With sessions, you have to keep track of every client that may be used to access an account. With tokens, those clients self-identify. You have to store less.
Some services unify multiple clients into one session but this is uncommon in my experience.
These objects can be transferred through untrusted channels, and retain their verifiability. That can be useful when moving data between organizations, or systems that do not talk directly (e.g. a mobile app that interacts with multiple unrelated backends).
RFC 7519 says:
JSON Web Token (JWT) is a compact, URL-safe means of representing
claims to be transferred between two parties.
Not all claims are authorization claims. Not all claims require ad hoc invalidation. Some claims can even be permanent!Authors ad infinitum have pointed out the risks in using JWTs for authorization (linked web article says "authentication", but both could apply).
Those risks are:
- Invalidation/expiration controls are limited
- Receiver might not properly validate signature
- Protocol dumbly allows "none" algorithm
Only the first is an operational concern, the others are just "bad code works badly" problems.The moral of the story is: If your claims do not fit the timed-expiration model of JWTs (e.g. some authorization claims), then don't use JWTs!
Software should be hard to misuse. If you have a library intended for universal usage, and the library users have to remember to add some code every time they call it, then it's a bad library.
If you have an specification intended for universal usage, and the implementers have to remember to always do 3 operations together, than it's a bad specification.
JWT is just a bad standard all around.
JWT itself is also poor by having things like "none" at all. Which means in every valid place to use JWT, you should really be using PASETO.
User-initiated log out is easy, you just clear the token. Promiscuous token gathering is harder (but SSL..!) and makes the case for invalidation or short expiration periods. And forced logout is the legit operational problem case.
Again though. If JWT does not fit your authnz model, don't use it. JWT works well in many applications outside of authnz, and some inside it as well.
Now it's really hard to argue with architects / developers why cookie authentication / bearer token makes more sense than JWTs.
Because that's a nonsensical argument? JWT is just a token + validation. Nothing more. You can use JWTs in cookie authentication, you can use them as bearer tokens. The only thing JWTs are doing is carrying a payload and signing it.
Now, if you want to talk about Oath2 or OIDC then maybe there's a different argument to be had.
JWTs aren't for transmitting sensitive data or encrypting things. JWTs are about having claims that can't be forged by the client.
So why would you put a JWT in a cookie? To give the end user a set of claims that they can't change, they can only read and give back to the server. Those claims can include things like user id, or session id, or whatever you might imagine.
Now, could you accomplish the same thing with an encrypted cookie? Absolutely. I'm not arguing about what you can or can't do with stuff. But rather again commenters saying "JWT is such and such and forces so and so". It's nothing but a signed set of claims.
I did not know that encryption had made it in the JWT spec. My comment was supposed to convey surprise, not some sort of snark or sarcasm. I read the spec after the OP comment and learned something new today.
I feel like the people who bash JWTs have never actually built a real-life application using them. Yes, there are footguns. But they are dead-simple to mitigate, and they are far from the only footguns in the world of web applications.
Yes, you can't fully "log out" without a centralized database. But who cares? A JWT in an HttpOnly and Secure (requires HTTPS) cookie is very well locked-down. I'm not worried about an attacker being able to retrieve it, because if they can then the client is pretty well owned at that point and the attacker can do whatever they want.
I imagine that is the main argument. People use JWT because it's standardized on the authentication protocol... The same authentication protocols that are horrible in many more ways than simply using a bad token format.
Yet everybody jumped into them when Google commanded.
No additional checks are performed on the back-end to verify whether the token has been revoked as that would reintroduce a round trip to the database you're trying to avoid in the first place.
This reduces the necessary database roundtrips, while still supporting a logout flow.
For this to be exploitable, you'll have to jump several other hoops, like accessing localStorage of another application, for example.
My concerns with JWT from early on is that the data stored in them was potentially stale. Front-end developers would always request fresh data at each interaction. Second, the JWTs were so long. We had to keep passing these long JWTs around.... mainly for testing stuff out, we had long lived tokens, especially in dev, so I think we passed them around to replicate API calls. So you felt how long they were.... and in my head I kept thinking about all this useless data being passed around taking up CPU/network/memory resources. So I would just remove JWT and replace the tokens with UUIDs. Everyone was happy about it, but they were confused as to why they were needed in the first place. I would just respond with, well when you find out let me know and I can add them back.
Have a B2B product where you'll have, maybe, someday, and I'm exaggerating here, X00,000 DAUs? Just set up cookie auth and track the sessions in Redis. Session revocation is super-super simple and you'll easily be able to handle any security vendor questionnaire asking how you lock out terminated accounts.
GraphQL everywhere is another one (like JWTs, it has its uses but people went way overboard) and Tailwind is the next one.
There's multiple safe and easy ways to do authentication/authorization without providing a signed green card to the client which you don't have the ability to cancel at any time. As long as you keep control in a database.
IMO the only hyped up piece of tech that was actually good in the last decade is containers (but not the orchestration, just containers).
AWS S3 was probably the first "serverless" technology to really take off. You don't have to worry about how many hosts you need to adequately handle sharing a file with millions of people. You don't need to start up multiple hosts in order to get backups in multiple data centers and then handle synchronization. Nope, just put a file there, and everything else is handled.
AWS Lambda and other FaaS providers made it so you don't have to configure scaling hosts to handle sudden large loads. It's also great for things that only need a couple minutes of CPU time per month, like say, periodically processing some data in batches. You don't have to deal with standing up a host that runs a cron job and then paying for CPU time you're not using.
Serverless databases mean you don't have to worry about patching the underlying operating system.
Of course, serverless has its limitations. FaaS is not suitable for long-running tasks, for example. It also tends to result in vendor lock-in.
EDIT: If you're going to fire back with "AlL tHoSe ThInGs ArE sTiLl RuNnInG oN sErVeRs", don't waste your time. It's a tired argument that misses the point. Yes, they run on servers, but you don't have to care about the underlying servers.
Infinitely scaling database means inevitable tradeoffs. Serverless doesn't take those tradeoffs away.
It doesn't do anything to justify the hype.
You get complexity, worse DX, vendor lock-in and often higher price for the sake of not having to do security updates. This is the worst deal in the history of deals, maybe ever.
About cost, even though that isn't "completely utterly false" it is misleading. If you are a startup and want to move as fast as possible without incurring tons of tech debt, and don't yet have millions of daily users, you should use serverless functions because the total cost will be relatively small (relative to the insanely high cost of good devs that is -- always optimize for DX and velocity until you actually have scale). If you ever get to the point where serverless is costing you real $$, then refactor then - you've incurred almost no tech debt by waiting and being very very fast prior to that may just be the reason you got to that scale at all.
Go ahead and build a serverless app utilizing a framework that is well aligned with that approach (may I recommend Remix?) and I bet you'll change your tune. My gut tells me you have no firsthand experience because your points are absolutely off base.
What is the big problem that serverless solves?
What does it promise and what does it actually deliver?
Infinite scalability? It only solves the easiest part of it.
Cost reduction? As you seem to admit, generally this is only true at small scale.
Security updates? Fair enough, but this has never been the real problem. If you're at a point where security updates are that important you will unlikely leave them in the hands of a third party.
My bet is that what you're attributing to the benefits of serverless are not inherent to the serverless itself and are just benefits of infrastructure built around it.
I wouldn't be surprised if large portion of serverless market is javascript. And rust driven wasm. So monoculture right now.
GraphQL is side-effect of spoiled-by-VCs frontend ecosystem. Frontend folks love it doesn't mean it's good for end-to-end system.
JWT, I don't have enough experience on it, so I've never been sold.
Using A cookie-based/token-based session strategy, you can do that quite easily.
JWT has a freeform payload that can literally be anything you want. Nothing about it prevents you from using a pattern like this.
A simple session ID can be (and often was) forged to grant access to things end users shouldn't have. Put the session id in the JWT and now an attacker can't easily change the session they are associated with.
There are enough CVEs on this specific topic that I tend to disagree. I've been around on the internet long enough to witness a HUGE number of session hijacking attacks. [1]
> Using JWT doesn't substantially make this easier for you and operationally you are going to have to invest in significant key management infrastructure.
IDK about making it easier, but rather using JWTs for this makes it harder to get wrong.
> If you don't need the stateless nature of JWT you should just not use them.
Agreed. Use them when they make sense and not when they don't. I'm not arguing that JWTs are panacea. They are neither good nor bad, just a tool.
[1] https://owasp.org/www-community/attacks/Session_hijacking_at...
JWTs don't make it any harder to hijack sessions, in fact, they often make it easier.
Session sniffing and man in the middle attacks don't discriminate between JWT and cookies/session IDs. Nowadays they are very hard to achieve with the vast majority of the web operating over HTTPS with optional HSTS.
Cookies containing the session ID can be marked as HTTP only, unlike JWTs which are often stored in localStorage, which makes them vulnerable to extraction via XSS vulnerabilities.
> using JWTs for this makes it harder to get wrong
Any popular web framework these days should provide secure built-in functions to generate and validate cryptographically secure signed cookies containing the session ID.
JWT libraries have had critical vulnerabilities in the past, such as allowing usage of the badly designed "alg: none" feature of the JWT specification.
https://auth0.com/blog/critical-vulnerabilities-in-json-web-...
Whereas generating new session ids will always need fresh entropy.
If the signing of the JWT wasn’t deterministic, then it would be hard to verify.
It'd be like building a React app and calling getElementById(id) to update DOM values. You _can_ do it but...
Who's intended pattern? Where is this stated as being the "right" way to use JWTs?
I'm seeing a lot of claims about the intent behind JWTs but frankly I think it's because people are skipping over having a fundamental understanding about WHAT JWTs are and instead are cargo culting on what they believe they should be.
I can't see their value against a bearer token + session tracking on the server for most cases (e.g. it won't be a huge performance hit to do a lookup of some sort on each request).
The two apps I'm referring to have a few thousand users who only make occasional requests.
I think a lot of apps fall into this broad category and I don't see what extra value JWT is providing. Encoding user data is pretty convenient (though more opaque) but if you want to be able to ad-hoc invalidate them you need refresh tokens or a session list. Not only does that re-introduce needing to do a sort of lookup and server user tracking, the encoded data on the token is no longer a positive, since you bifurcated knowledge of the user (token + list), and all its data would be more discoverable by including it where you are now tracking sessions anyways.
Help me out if I'm missing something. My mind is open.
If you treat like it is supposed to be treated and check the cryptographic signature instead of going to a central database, then you can't invalidate it (at least before it expires). You can ask the client to remove it, but what if they don't? (eg. they are an attacker)
Meaning, if someone gives you a valid JWT that says "I'm user 123" you can trust that this is user 123.
That's all it is. When reading in intent on how it should be used or what you should do with it, that's something beyond what the thing actually is.
So any pattern you can imagine with a session ID, you could implement with a JWT. Just throw the session ID in as part of the payload. Just because you are using JWTs doesn't mean that you don't have to work with any sort of external resource while you are processing it.
What purpose does it serve then? You can still avoid lookups when a JWT makes a claim. You know the JWT is valid so you know that all claims made within it are true. If it claims "I'm user 123" then they are user 123. If it claims "My account number is 435" then that's the account number. You don't have to make a lookup to see those facts are true. If it says "My session is 433" then you can validate that session 433 is still active and act accordingly.
JWT isn't a panacea but it also isn't anything more than a bag of claims. It doesn't claim to be anything else either.
What if the account number changed since the token was created?
This works only for immutable claims. If the truth behind those claims changes in the mean time, you have to be able to invalidate the token, or accept the fact that it serves stale information.
Isn't that obvious? I mean, if we were talking about sessions and you put in the session set information that can change outside the session, wouldn't the same problem exist?
I just don't see this as a fundamentally unique problem for JWTs.
An authenticated session id is just a very very long session id.
JWT is a token, often stored as a cookie.
This is both true and also misses the point. Session Cookies are ususally stateful. In other words, the server validates the session by querying a service or database. JWTs however are stateless. You don't query a service or database to validate the service. As a consequence if the JWT is not expired and is still using a valid key there is no out of the box way to invalidate the JWT. You can build custom invalidation measures on top of JWT but it's not a natural consequence of the design in the same way that a session cookie is.And now you're starting to understand why JWT's aren't worthy of the hype. A truly stateless JWT implementation is insecure since you can't invalidate existing tokens.
The usual compromise is make JWTs have a short (<= 15 min) expiration and provide the client with a refresh token that doesn't expire, but is stored server-side. When the user logs out, the refresh token is invalidated server-side so a new JWT can't be issued. You're re-introducing state with this solution, but you're making it so you don't have to bang on a database with EVERY request, just one every ~15 min or whatever your expiration window is.
Even if you didn't you still need to retrieve the user and password from some storage to validate the key, which invalidates the reason for JWTs in the first place, since you supposed to be able to validate them without access to an auth service/db
I.e. if the user's password has been compromised, then the attacker with the password could have started a new session using it. The question is, how do you (allow the user to) revoke the auth that was granted by the compromised password and invalidate that fraudulent session.
As an aside, if you do pick JWT’s, you could create a revocation queue, publish all revocations to that queue, and read that queue asynchronously in every server to keep a token blocklist. You lose statelessness but retain speed (no db call).
Yes, to perform session invalidation with JWT requires some amount of centralized state, so a perfectly-decentralized auth system is not actually possible.
However, using JWT means this centralized state is much less heavy than traditional sessions. Instead of saving ALL sessions and ALL session data on the server, JWT only requires saving a small fraction of session ids (those that have been recently invalidated) and NO session data. By using JWT, you have simplified your memory needs and reduced them by multiple orders of magnitude.
IMO the only flexible way is a denylist of tokens or JTI which adds the burden of more infrastructure.
My take is to use an opaque token unless you are using OAuth and paying someone else for all these extra capabilities, at which point things become easy.
ex. If you are using Firebase Auth or Auth0, they manage the token revocation for you so the problem sort of melts away and you are left with only benefits of JWT. Just have to blacklist the JTI claim and it'll automatically be denied in the future.
The idea is that you have a disgruntled employee with a token that expires in 5-10 minutes. You don't have invalidation checks, so what damage can that employee do in 5-10 minutes? Keep in mind for many companies, exporting a client list is a big deal.
Why would you not have validation checks? The point of a JWT is they're stateless. When you get one, if you have the key, you can validate it without access to the auth service.
If you take your JWT, and then are using the claims to check the database, it is 100% functionally the same as a session token.
JWTs are not something you issue to use on your own service. It's for another service to verify the user on their service based on a factor coming from your service.(yes i'm aware there are other uses)
If the only thing in your payload is a session_id or a user_id. Stop using JWTs.
This is not an authentication issue (JWTs) this is a classic authorization issue (Permissions/Roles).
It's not the authentication layer's fault if you allow everyone root access.
JWTs are just fine. Bearer tokens are just fine. You can write shitty session code just as easily as shitty OAuth2 code.
I replied to a sibling comment. What I do is use the JWT from oauth or whatever sso, verify it, and log the user in as normal. Using the JWT as a replacement for a username/password.
I can invalidate the session or block the user as normal.
If you stick to OAuth and OIDC you have the option to validate the tokens against the userinfo and introspect endpoints, but that's, just another "database"
Assuming the tokens issued to employees have an N minute lifetime stop replacing expired tokens for that employee N minutes before you do whatever it is that might disgruntle them enough to make them try to trash your systems?
> it's not required if you're going to access the DB anyways on authenticated requests.
then what's the point? You're just re-inventing bearer tokens.
This makes no benefits as to bearer token or any random string that the server "knows" is a valid authenticated request via internal store, like a DB.
So instead of storing all session tokens indefinitely, you only store invalidated tokens for a short period of time.
You're storing and checking a smaller subset when doing stateless + invalidation cache.
Whether or not you need that memory optimization is up to you and your machines.
Personally it's about the same effort to implement and I prefer stateless + simple redis cache for invalidations.
If you are already distributing lists around your application servers (that impacts the format and top performance of the redis cache), then yes, it's quite simple.
And most likely you'll want a simple k/v distributed cache for other things so it's no extra work.
JWT allows for a user to authnz with a third party trusted by the second party. An example of this is HL7 FHIR SMART app launch, where an outside web application (2nd party) is opened from within an electronic medical records system (3rd party).
[1]: https://docs.digdir.no/docs/Maskinporten/maskinporten_summar...
If you use JWT, users that get a new token are free to use your app without any further timeouts and reduce the load on the auth service. With a session system, the auth service reduces performance on the entire app.
Why would that even be allowed to happen in the first place? Sessions should always have some level of redundancy even if the primary retrieval is done in-memory. An outage would imply that an auth service is simply offline. Loss of sessions is a sign of catastrophic failure and possibly inadequate architecture.
> you can hit issues where all your users DDOS your auth service
Even in a case of a breach where all sessions must be cleared, JWT is one potential solution to mitigate DDOS. The others are to not have client-side code that blindly DDOSes your server and to have some level of DDOS protection in front of the rest of your architecture.
Moreover, even if you solve this problem for authentication, there's no obvious reason why you can't end up in a similar situation if some other service in your architecture is down. With JWT, all you've done is take the one service that is fundamentally one of the least complicated (storing hashes in memory) and treated it as if it's a critical point of failure. Sure, it can be, but auth is also one of the easiest things to horizontally scale, distribute, and synchronize at a relatively low cost. In JWT world, you've gone from storing even just random session numbers and now have to manage encryption keys, TTL, and possibly invalidation records (nearly defeating the entire purpose). That leaves even more room for things to go wrong.
> If you persist the sessions you have the same problem except they DDOS the DB.
Again, so what? You're supposed to do things to mitigate DDOS on your services regardless of whether you're using JWT. If your auth service can be that easily DDOSed (presuming this is an average website and not Facebook scale), then it would only take some coordinated enemies with a bone to pick to DDOS you without even using legit JWTs.
Not just failure, you could also see this with just big spikes in traffic like a release day.
Either way, sounds like you're convinced the cases are common enough that mitigations should be table stakes.
https://news.ycombinator.com/item?id=33020993
I disagree that stateful based sessions are simpler.
I disagree size of cache is a red herring.
You don't have to be FB to want to optimize your memory usage.
In the end I prefer the more efficient (mostly stateless) solution and it's really easy to implement.
But to each their own of course.
And you can expire the invalidated keys faster (set the invalidated key expiration to the expiration of the JWT)
Not many people revoke sessions, but a lot of people create sessions. Much more efficient to only store and check revocations. The rest can be stateless.
Obviously the situation is the same when managing a token blacklist if you truly have a hard requirement that sessions be instantly invalidated at a specific point, but there's a good chance you don't. Maybe it's OK to presume signed tokens are still good for some amount of time if your blacklist server is unreachable, or maybe waiting until the JWT expires after 60 minutes is too long, but a one or two minute delay is acceptable. Or maybe you only check the blacklist for high-risk API requests.
It's not ideal. Invalidation is definitely a weakness of JWTs, but there's still a lot of value in baseline statelessness.
JWTs add complexity and have zero benefit. The author never said it can't be made to work, obviously it can. It's just completely pointless in most applications.
Makes it easy to track issued tokens and revoke them too.
Most software devs using cookies would likely struggle to revoke an individual cookie - you can certainly architect that in, but if you're like most of us, nobody thought about it early on and now it's kinda problematic to retrofit. Same reasoning: "Well, it will expire on its own soon enough." JWT's actually make it easier to guarantee expiration since it's part of the token itself instead of being reliant on server-side mechanisms.
It's definitely a pain in the butt to juggle refresh tokens & access tokens as a client-side dev, and almost everyone's API docs are ambiguous and confusing about token lifetimes (refresh tokens often have lifetimes too). But that's another issue.
Your access/ID token is short-lived (~5 minutes). This token is trusted without confirming if the user still has access.
Your refresh token has a longer lifetime (hours to months) and can be used to trade for another access token (and a new refresh token, invalidating the old one), but every time you do this trade your authentication server can also check if the user still exists, is not banned, has not signed out and still has the same claims (username, email, groups...) and either not issue a new token or a token with different claims.
There are proxy servers that will do this entire thing in the background for you and hand you the claims of the current access token in HTTP headers.
- Clear client side state where you can. - Write signed out/expired tokens to something with a cheap heavy read/eventual consistency model - Fail to signed in if unavailable - Acknowledge that you are gaining latency/availability/ lower costs by trading some precision
I am aware of a very large website most folks use every day that did this for more than a decade and it worked fine.
However, I think in some scenarios it doesn't matter. For example if all the user data is encrypted, an attacker can retrieve it from the server using the JWT but not decrypt it - how useless!
Besides, when it comes to criticisms like this, I would gladly like to be pointed to real-life incidents where JWT as the security-bottleneck was fatally exploited.
So i tried to build a simple CRUD webside with authentication using JWT just to practice. The section "Problems with JWT" perfectly explains the issues I had. I think a way too many people just pick up the next random fad and without thinking they start using it.
If you're looking for something different from JWT, allow me to suggest Coze.
1) you have some kind of api gateway, it would validate the session, issue JWT/PASETO that lives a minute or so, downstream services would use that token to talk to complete the request.
2) When you need something like AWS S3 presigned URL.
Everything else is just an opaque session token with extra steps.
Entirely missed the point that refresh tokens will not work when your app (or your user) has told the IdP to sign out - unless you requested offline access.
Not being a fan of JWT authentication I was doubly relieved when I clicked through.
String session tokens generated by a CSPRNG and stored in a cookie work fine for every use-case I can think of. What specifically is the argument for JWTs?
It's easier to have for eg. userId embedded inside signed token. Nobody has to call identity provider, they don't even care from where did the tokens came from.
Our threat model for this software doesn't care too much if token is valid for some seconds after logout. At this point user was probably MITMed or lost control over hardware and has much bigger issues than us.
Could we do this differently? Probably yes. Would there be a positive impact for the company if we dedicated time to this? Very doubtful.
JWT is only a tool. Sometimes it fits, sometimes not. No reason to get religious over this.
Especially when I signed the opt out form requesting to not be placed on the database.
Especially 2.0, when I called the database, and was told that my records could not be deleted.
When the issuer is ready to generate a Health Card, the issuer creates a FHIR payload and packs it into a corresponding Health Card VC (or Health Card Set).
The VC structure (scaffold) is shown in the following example. The Health Cards framework serializes VCs using the compact JWS serialization, where the payload is a compressed set of JWT claims (see Appendix 3 of RFC7515 for an example using ECDSA P-256 SHA-256, as required by this specification). Specific encoding choices ensure compatibility with standard JWT claims, as described at https://www.w3.org/TR/vc-data-model/#jwt-encoding.
it's a standard format for transmitting data signed by an entity.
If your claim is not durable enough for the protocol's expiration setting (e.g. authorization, in some cases), then don't abuse JWTs. Simple!
What would the procedure that can guarantee no mistaken passport will ever get signed look like?
Of course you need some form of revocation, otherwise the first bad apple will spoil the system.
Makes it really easy to scatter low budget devices across the state that require no Internet connection, and gives a red light/green light on whether you're allowed to go shopping or be out in public that day.
All the infrastructure is right there in place. Didn't get that far.
This time.
Anyway, all of that "infrastructure" is people making judgments. It will enable a police state if the people think they should enable it.
"We've ignored things like increased attack surface due to complexity of JSON parsing/validation, misconfiguring JWT libraries to allow no signature, leaking data to users because you thought JWTs were encrypted (they're usually not by default), and poor implementation of API clients that don't properly re-run requests that failed due to expired JWTs. The list goes on, and on, and on ..."
All of these sound like usability issues. Not to downplay them but are there more serious issues with JWTs?
The no signature bit was a huge flaw in almost all JWT libraries across languages a few years ago.
There are better schemes out there.
Failing to see a problem.
The problem is that apart from expiration, the token is never invalidated, for example when the user logs out. An attacker who stole the JWT is still logged into the victim's account until expiration.
How many people do that? Or better, how many people do that and still expect anything to work? (Anyway, iframes don't break those in any way.)