When you look for advice on how to authenticate users of an Express/Node.js API, the most popular answer seems to be "use JSON web tokens".
I took this advice as read when I was building my first few APIs. I dilligently built the middleware for signing, verifying and revoking tokens, plus the client-side code for keeping them.
A few years later I discovered Rails. I was very late to the party, but the simplicity of using Rails session cookies was really attractive after the hassle of building secure authentication again and again.
So, I've recently started building another Node/Express API, and I've decided to use the same session approach. It's been much less stressful.
The setup for express-session
looks like this:
server.use(session({
store: new (require("connect-pg-simple")(session))({
}),
secret: process.env.SESSION_SECRET,
cookie: { maxAge: 30 * 24 * 60 * 60 * 1000 }
}))
And then in my route handlers, it's as simple as saying:
// where 'user' is the user who has just authenticated
req.session.userId = user.id
Because sessions are stored on the server and the client only gets an encrypted cookie, this seems foolproof and easy to maintain.
The cookie is passed in automatically with every request - it just works.
So, I'm wondering why people bother with the extra overhead of JWTs at all?
The reasons I normally hear are:
'JWTs are more scalable when your app needs to grow'
I guess this might be true if you're keeping sessions in the memory of a particular app instance, but any realistic implementation involves an external session store.
Even my minimal example above uses a table on the PostgreSQL database that powers the rest of my app.
That database is an external service—it's horizontally scalable out of the box. This doesn't seem to be a realistic problem?
'JWTs are more secure'
Recently I've seen lots of people suggesting it's easier for JWTs to be stolen by XSS attacks, especially if you keep them in localStorage.
The current wisdom seems to be that you need to store them as a httpOnly
cookie. So...why not just use cookies to start with?
'JWTs are stateless'
This one I do understand - a stateless API is ideal, easier to test.
But what's the harm in a session that stores just the user's ID, say? Is that tiny bit of state really that bad?
To take it a bit further for the sake of argument, let's say we used the cookie-session store.
What's the difference between this and using a JWT in a cookie? Just the way the token is formatted/encrypted?
'JWTs still work when the API is on a different domain'
This one makes sense too - cookies should be restricted to a particular domain, and if we're making requests to a third-party API on a different domain, we'll need to manually handle a token.
But how do we square this with the best practice to store JWTs in cookies rather than localStorage from earlier?
Am I wrong?
What am I missing?
What situations would a JWT be worth the effort in?
Do you have a preference for sessions over JWT, or the reverse?
Top comments (9)
I wouldn't say you are wrong. You send a piece of information to the server -cookie- that authenifies you on that server. The cookie gets verified for authenticity and carries the Id of a user, whose context is then pulled from a session store. You can implement exactly the same with jwt. You send the jwt carrying the user id to the server, server verifies the authenticity, you pull the context of the session from a server side store. You can use JWT to implement stateful apis too.
But jwt gives you so much more. It can carry permissions. You need not pull the permissions on server side, as you have them encoded in the token. What use would this have? Temporary permissions. You can generate a valid token that allows a person to do something they are not allowed to. Say, want to give write acess to an article you want him to author.
And frankly i don't understand what extra overhead there is. You still need to generate and deliver a cookie and then send to the server with each request and then authenticate it server side. You only write different code, not less complicated code. There's support for cookies aut of the box, sure, but is the loss of flexibility jwt offer worth the saved 10 lines of code?
JWT was invented for OAuth2 and make super sense if you want to separate authorization and authentication from your app. E.g if you have several apps and want single sign on between them.
For authorization code flow (one "kind" of OAuth2), the tokens are only exchanged between the app and the OAuth2 server. Session cookies are used in the browser.
For a single-process app, I agree, it makes little sense with JWT. For a micro service architecture where there are many processes and you don't want them blindly trusting each other, JWT is übercool.
Where do you find that "JWT was invented for OAuth2"? JWT (RFC7519) was created significantly later than OAuth2 (RFC6749). However, OAuth2.0 Tokens (RFC8693) accommodates JWT & other tokens.
JWT is part of the JOSE suite of JSON security. If JWT was created for anything, it would be OpenID Connect (akin to OpenID 2.0)
BTW this article skips the usefulness of JWT for native app development. as well as server-server communications (as you infer with micro-services)
Wups, probably my bad. Thanks for the correction.
might do a "microservices: are they more trouble than they're worth?" follow up lol
Sure....
And then you can choose the perspective you want to get the conclusion you desire:
For a single developer or 5: Microservices are overbloated and not worth the effort.
For 100+ developers developing different web applications where you want a seamless user-experience with single-sign-on between them, and that need to be able to release independently and have an otherwise-healthy architecture: Microservices do have their valid use-cases.
Nicely discussed. My feedback: JWT is getting more flexible. I prefer to simply use it for authentication alone.
I love jwt on my express servers because of the middlewares you can code, you can make it SUPER SIMPLE to reject a bad a request with a bad jwt
JWT is more than worth the effort once you learn how to implement it. It is also very popular so easy for other developers to work on the same codebase.