Hacker News new | comments | ask | show | jobs | submit login
Token-Based Authentication with Node (mherman.org)
37 points by mjhea0 on Jan 2, 2017 | hide | past | web | favorite | 16 comments

We naively started with JWT for auth, and gleefully ripped it out after several months because of all of its cons.

1. Overall, no net benefit. We still had to query a k/v store on every action to check that the token wasn't revoked.

2. Less secure because there's no way to make it act like an HTTP-only cookie (no access from JS).

3. No way to revoke all of one user's tokens because there is no list of those tokens. We want to limit (or be aware of) the number of active logins and let the user sign out from all locations (as you can in Gmail). If the token is only stored on the client, there's no way to do this. You could store a list of all granted tokens and only use it for these revocations, but that's yet another level of complexity. (When we dropped JWT, the diff was -1200+95 LOC because we had to handle a lot of scenarios like this.)

4. Adding tokens to client-side requests is a pain. The common example for e.g. Angular is to use an HTTP interceptor to add a header. More code, but it works... Except for img and a (anchor/link) elements where you can't add a header. So you put the token in the URL query instead, and now you're vulnerable to session leaking if a user shares a URL with someone else. And you have to manually update those embedded tokens when they expire, as many JWT folks recommend you make the expiry short.

Good riddance.

Yep. Revocation is tricky with JWT, but it's not that bad.

The expectation should be reduce in database io, not getting rid of it entirely.

Overall you could issue tokens with short lifespan, say 5 minutes. And having the client refreshing tokens periodically. Reading database every 5 min is certainly an improvement over reading it on every API call. You could also only check revocation table if the performed operation is security-critical or sensitive enough. I wouldn't care if the user is trying to perform a read-only operation on some public data with a revoked token that is going to expire in a few minutes anyway.

>3. No way to revoke all of one user's tokens because there is no list of those tokens

I don't see why one would not maintain such a list, since generally issuing a token requires database io anyway. Storing a token when issued is just one additional write op.

We decided to use JWT internally, and force clients through an API gateway that converts between a classical session key (stored in a fast, distributed key/value store) and an internal JWT.

This means that internal microservices don't need to deal with session management at all (including revocation), and only need to worry about validating the public key of the signer.

Yet if anything is accidentally exposed (let's say a microservices fetches a user-provided URL, and the URL is localhost), the user still cannot spoof an identity without having the signer's private key.

The internal JWT also has a very short TTL, so accidental leakage wouldn't cause much trouble.

I would not use JWT for session management.

One benefit over simple token formats (e.g. using UUIDs as tokens) is that you are able to quickly invalidate the token, because you don't need to hit the database at all.

Second benefit is, at least for me, standardization.

Edit: fixed typo

How do you invalidate a JWT without hitting the database though? The JWT is already signed with its claims, so there's no way your application can know it's revoked unless there's a list of revoked tokens, which defeats the whole purpose of using JWT. Or am I missing something?

No, no, you got me wrong. The point is in faster invalidation, not validation. That means you are immediately able to tell an invalid token from a not-yet-determined one (but not from a valid one). Then you have to hit the database, but the number of hits is - in some cases drastically - reduced.

It sounds like you mean if you change the signature used to sign the tokens, then existing tokens will fail validation without hitting the db? If so, that's not too practical because all of your users are suddenly signed out. What myself and the two other posters I think encountered is that you have to store revoked/invalidated tokens in a blacklist, and then check that a token in a request is not in that blacklist. (That was my first point, at least.)

No, I simply meant that instead of checking if a token is valid, you can first check if a token is obviously invalid (e.g. expired, wrong claims, wrong audience...), and immediately return if so, thus reducing the load on your application servers and databases; and only then query the database to check if the token has been revoked.

You are not missing something. We're both in the same boat looking for this island of treasure.

You can build a bloom filter on schedule which will tell you definitely if the token has not been revoked without hitting the database.

A good security strategy is to implement custom security layer as a last resort. Doing so will have a much higher chance of introducing bugs that will allow attacker into your environment.

Yeah, in this case I'd tend to agree that using a good framework like Passport and then the JWT[1] strategy is probably a good move.

It's never a bad idea to understand the basic structure of how a framework might be doing the work for you behind the scenes though.

[1] https://www.npmjs.com/package/passport-jwt

This isn't at all directed at this post in particular – but I'm curious, what's with the huge movement towards JWT? I get their use-case, but it seems like most implementations shoe-horn JWT's to act exactly like access tokens stored within a database. Revoking a JWT doesn't make any sense to me.

I have to agree. A JWT contains within it 'claims' that are the authorization for allowed behaviour against the API. Authentication happens before the JWT is issued.

I think the problem is that it's not about issuing just one JWT, you need a 'refresh' token and a 'security' token. The security token expires quickly, but a new security token can be issues using the refresh token which has a long expiry.

You can even make the refresh token single use with the action of refreshing the security token returning you a new refresh token. This, however, needs to be handled carefully within the UI as two requests using the same refresh token would invalidate the 'session'.

The reasoning is that the api can trust the claims within the security token, but has no need to access external sources to validate the token. The refresh token can have security policies applied to it as well as 'user logged out' checks against the refresh token. This keeps the API requests extremely fast with only an intermittent (once a minute) need to get a new security token.

Of course, if your application hits the API less than once a minute, you've now doubled the number of requests it needs to make, because it will need to refresh the token before every call.

I suspect it's mostly cargo-cult thing.

JWT itself, however, is a signed-JSON format with detached signature which is very useful well beyond access token use-case.

JWT as access token at large scale will require frequent key rotation so inability to revoke becomes a problem but it's not a show stopper because one can work around it using various tactics such as bloom filter I mentioned above.

Applications are open for YC Summer 2019

Guidelines | FAQ | Support | API | Security | Lists | Bookmarklet | Legal | Apply to YC | Contact