Hacker News new | ask | show | jobs
by jondubois 2728 days ago
If you use a realtime transport like WebSockets, you could keep automatically re-issuing a fresh JWT with a very short (e.g. one minute) expiry every 50 seconds; just push them at an interval to authenticated clients. That way your banning mechanism would only have a one minute a delay. No need to revoke tokens; just let them expire.

In such a system, a user would be logged out after one minute of closing the connection... Probably good enough for online banking. In a way, this is safer than standard sessionId-based auth because once you've issued the token, you don't need to worry about scenarios where the user has gone offline suddenly.

There are very few systems that need banning with down-to-the-millisecond accuracy.

2 comments

What you’ve described is a workflow that will technically work. But I’m weakly confident it’s still suboptimal for most use cases.

How will you deal with the usability problem introduced by expiring all sessions for users who are offline (closed tab, spotty internet connection, etc) for at least 50 seconds? It actually seems like you really should be wondering how to interpret users being offline temporarily.

You could just accept this as working as intended, but you don’t need to accept it if you just use normal session IDs. Is your application really so latency sensitive that it can’t tolerate a DB lookup? What is an example workflow in which users on a web or mobile application cannot be tolerably authenticated with standard DB lookups?

You can also use a refresh token, but this brings you back to the revocation problem, but with longer term tokens. Likewise, there is a material difference between millisecond revocation and sub-minute revocation. There are good reasons to care about sub-minute revocation.

You don't need JWT in this case. You can use a normal token with short expiry and some mechanism to keep it fresh as long as the user doesn't exit the application.
JWT is simpler to implement and more scalable than the sessionId approach so why would you use the more complex solution to get an inferior result?

With JWT, you only need to do a single database lookup when the user logs in with their password at the beginning... You don't need to do any other lookup afterwards to reissue the token; just having the old (still valid but soon-to-expire) JWT in memory is enough of a basis to issue a fresh token with an updated expiry.

It scales better because if you have multiple servers, they don't need to share a database/datastore to manage sessionIds.

I don't know what stack you're working with that makes you say re-issuing JWT every 50 seconds over WebSockets is simpler to implement than the session ID approach people have been using for 20+ years :)
Simpler != cheaper WRT resource consumption. Not having to hit a DB means not having to replicate the DB to respond quickly, and having one fewer point of failure.

If you can live with quickly expiring, quickly reissued crypto tokens like JWT, it's a boon.

But JWT definitely don't work for web auth. They can be used as CSRF tokens, though.

With WebSockets, you also have just one lookup when opening the connection. No scalability issue here.
This is not accounting for reconnect scenarios. With sessionId, if the user loses the connection and reconnects, there will need to be another DB lookup to verify that the sessionId is valid. This is not the case with JWT. The validity of the JWT can be checked without any DB lookups.

Also, this is a security consideration because a malicious user could intentionally send fake sessionIds to make us DoS our database. With JWT, verification of the signature only uses CPU on the workers which are easier to scale.

You can sign session ids to prevent DoS, and you can cache session ids to avoid database lookups, but you can't detect forged or stolen JWT tokens.
You can't forge a JWT without stealing the private key of the valid JWT signer.

You can steal a JWT token the same way you can steal a session token.

Why wouldn’t you rate limit connections? That is something you should be doing anyway, and it neatly resolves the denial of service problem.