Hacker News new | ask | show | jobs
by nichch 1861 days ago
The amount of JWT tutorials available right now is absolutely overwhelming. I found myself worried actually that session tokens were insecure just because I could not find recent documentation/tutorials on them.

I asked for suggestions on r/node for alternatives to JWT and the first comment was "Just use JWT".

3 comments

Too funny I haven’t been doing front end for very long, maybe a few years..

I knew of the existence of JWT so that’s what I ended up rolling with on my first true frontend heavy project. JWT token tutorials are an excellent example of my theory that “the internet doesn’t know shit, don’t trust it”. Case in point google “JWT ReactJS”. 8/10 tutorials are storing them in localstorage. At the time I knew literally nothing but I knew that was dumb as hell

> At the time I knew literally nothing but I knew that was dumb as hell

Why? If you're worried about XSS, remember you're already toast in a million ways - including the ability to spoof a login/password form.

One reason is if you store refreshTokens in localStorage, a hacker's XSS attack can send the refreshToken to himself, and keep refresing the token forever (or until somebody actively invalidates the session) - and thus use the token for whatever evil purpose.
Can't you just read document.cookies to get the token at that point?
I believe refresh tokens are supposed to be HTTP Only
> [...] asked for suggestions on r/node for alternatives to JWT

Heard good things about paseto[1]. It claims to be convenient as JWT minus the security issues entailed. Check it out, has some decent libraries for most main-stream languages. Didn't try it myself yet.

[1] hxxps://github.com/paragonie/paseto

Because JWT is the right answer for most scenarios. It's the best of both worlds when done correctly. You aren't blocking most of your requests waiting on your auth backend and for requests that actually need to have up-to-the-instant knowledge of a token's validity, you can always elect to hit up the auth backend anyway.

It provides a hell of a lot of flexibility in terms of how you design your authentication architecture. For example it can let you use the same set of tokens for all your API's as you use for "web" traffic. It can reduce page latency. It can substantially reduce the footprint required for your auth backend.

It's pretty awesome. It is just mis-understood because people don't understand it. They assume that you have to invent some kind of "token revokation" system when people log out... nope. I assert unless you are a bank or something, 99% of your authenticated traffic is read-heavy and can tollerate 5 minutes worth of somebody getting ahold of a token that was used for a logged out session. All the write traffic and "sensitive" read traffic can just hit up the backend server to do real-time token validation.

For example here on HN 99% of authenticated requests are just to view this page, all this traffic doesn't need to insta-verify the auth token. The "post comment", "vote" and "change password" traffic makes up >1% of the traffic and can be "insta-verified" against the auth server. Who gives a shit if somebody impersonates that session for up to five minutes if they still can't comment or vote? The odds of it happening are minuscule and the damage isn't really bad...

If the simple thing is sufficient, don't do the complex thing.

You say that JWT is the right answer for most scenarios, I think that's not true. Aside from revoking, there's risk in complexity and I did my best in providing evidence that complexity causes real-world issues. You allude to this when you said "if done right", but that's a big if. I think this tends to fall deaf on some people's ears, because nobody feels that they might be the one to mess up JWT and inadvertently introduce vulnerabilities. Perhaps that is true for you, but the added complexity _is_ causing real-world security problems for actual developers.

The advantages I'm hearing is 'not blocking requests' and 'reduces latency' (which sounds like the same thing), but how much does this really matter? How much latency do you think this is? Has this really been a bottleneck for you?

You might well be part of the small group where every 5ms matters, but it would be disingenuous to suggest that that's true for "most scenarios". Most systems can bear an extra Redis fetch.

I'll concede it might just work better/simpler in your architecture, especially since you mention a dedicated auth backend, but shaving a few ms of a request is not a good enough universal reason given the drawbacks.

> If the simple thing is sufficient, don't do the complex thing.

I fail to see how JWT is complex. It is actually quite simple--it's bog standard public key cryptography. Hell for most requests, even your damn load balancer can validate the auth token, your front-end servers might not even see the request for an expired token!

I would assert all these homegrown "just throw a redis instance at it" solutions are far more complex. Now you gotta deal with cache invalidation and that isn't fun. Plus it is a network request, and that takes a long time... time which I could spend doing something more useful for my customer.

The decision tree for JWT is easy:

- Is the token expired? Yes -- Return 403 (note: your load balancer can do this)

- Is it for a "sensitive request"? Yes? Verify token against auth server

- All other requests (99% of your traffic) - Validate the token locally.

I've provided data in my article of many instances where people got JWT wrong, including an example from Auth0. I wouldn't call myself a security researcher, but many who are have also said this.

Your comment that it's 'actually pretty easy' feels pretty reductive. If you're really making this argument, I feel it would be good to provide some evidence.

I've been doing this for a while, and in my experience a large group of devs would likely not be able to explain public key cryptography, despite having ownership over features and/or applications. This is not just about you and what you find easy, this is about the entire community with a huge variety of experience levels.

I did... I made a decision tree. It's really that easy. The hard part is deciding what requests are sensitive and which aren't and that's a business decision not an engineering decision.

Adding redis or some crazy pub-sub crap to deal with logged out tokens.... that is truly making it far more complex. JWT is all about you deciding which pages truly need real-time "this token is invalid" and which don't.

Once you decide all requests need real-time you either are lying to yourself or JWT truly isn't the correct answer.

> I did... I made a decision tree. It's really that easy

I think we are past an intellectually honest discussion. Disappointing. Good luck with everything.

You keep parroting "complexity", yet in your original argument you stated that for "sensitive" requests, you should just hit the backend anyway.

So that implies that you already have a database setup and you are already using it in your app. That means there is zero extra operational or deployment complexity (compared to implementing a completely new and different bloated system.)

As for development complexity?

  var isTokenValid = await redis.get(`k:${token}`);
  if(isTokenValid != null) return next();
  return res.status(401).send('Unauth.');
I generally agree with most of what you've said about JWT, but I think it's better retrofitted after you have performance issues.

First pass, just do a database query! Most startups never hit the limits of this approach. It's hard to get simpler.

Second pass, cache the state in memcache. If you're on app engine or heroku or some other paas, you already have it available. Even fewer startups hit this limit.

Third pass, it's time to break out JWT. Congratulations, this is a great problem to have.

It's funny how for "can be done right" guy you propose to use 403 for obvious auth required.
Meh.... I don't know the exact code and didn't want to look it up. The point is... you say "dude, this token is invalid... try again".
Per specification, 400-series errors mean “don’t try again”.
First of all, if my requirements are "not JWT" then the correct answer is not JWT.

> You aren't blocking most of your requests waiting on your auth backend

Yeah, at Facebook scale. My database responds in less than 1 ms.

> for requests that actually need to have up-to-the-instant knowledge of a token's validity, you can always elect to hit up the auth backend anyway.

So... what is the point of JWT when I always need to know if a token is valid?

> For example it can let you use the same set of tokens for all your API's as you use for "web" traffic. It can reduce page latency.

Again, my database responds in MS. If you're doing client side rendering anyway, why not just throw in middleware to check the session token? You are trading literally 1 millisecond of latency for unneeded application complexity.

> I assert unless you are a bank or something, 99% of your authenticated traffic is read-heavy and can tollerate 5 minutes worth of somebody getting ahold of a token

Read heavy traffic does not imply that 5 minutes of stolen credentials is okay. Could my app survive if someone stole a token and used it for 5 minutes? Sure. Do I want that to happen? No. Without JWT, I can revoke tokens in milliseconds. I can revoke tokens if IP is changed. I can revoke tokens if user agent changes. I can revoke tokens if a user rotates their device.

> All the write traffic and "sensitive" read traffic can just hit up the backend server to do real-time token validation.

Again, what is the point of JWT if you still need to hit the backend? All read traffic is sensitive to my app.

> For example here on HN 99% of authenticated requests are just to view this page

Sure, then why use JWT at all? Keep the profile name and points in cookies, upvotes in local storage. Who gives a shit if the data is a little stale? Right?

JWT is unnecessary bloat in my opinion. At Facebook and Google scale I can see how saving billions of database calls a day could be useful. For people with less than a million page hits a day, probably not.