Easily Implement User Authentication in NodeJS

Using the good ol’ session cookie 🍪 😋

Stop using JWT for session. Always opt for the good ol’ session cookie unless you have compelling reasons to use the JWT token extensively in your application.

Managing a user’s session over the battle-and-time-tested cookie gives you more controls. A case in point is that, you are able to invalidate a specific malicious user’s session immediately without affecting all users which would be the case with JWT where you had to either wait till his JWT token expires or change your secret signing key which practically kicks out everyone.

Also, better security — By making it httpOnly , secure, Samesite etc. cookies. You just gotta look out for the CSRF attack. It’s somewhat mitigated by the Samesite but there is also the ‘CSRF token’ you can do. You can google about it. Very standard and well documented stuff.

Anyway, in this post, I will show you how to quickly implement:

  • User’s session

User Session

When a user has been authenticated, she is given a ‘session id’ in the form of a browser cookie which she will send along when she makes a request to our APIs. When she does, we need to know that the session id is a valid one. The way we do that is we use the session id to query a store(e.g. Redis) for its associated session object, and check if its property called isLoggedIn is true or false. Utilizing Redis to store a user’s session data will save us with their credentials whenever they try to call one of our protected APIs.

That probably didn’t make any sense. Let’s be more concrete.

First of all, you need some npm packages:

npm i express-session redis connect-redis
In your app.js main express file

You can set how soon your session cookie expires by setting maxAge in express-session . Note, by default, the ‘maxAge’ is not set in ‘express-session’; it’s forever until browser shuts down. Now, if it wasn’t set there and you used connect-redis , by default, ‘connect-redis’ will give it a lifetime of 1 day(86400 seconds). However, the expiration timer is reset by default by ‘express-session’ when a user interacts with the server. In most cases, you would want that behavior.

User Registration

When users register in your application, you need to somehow store their password and a way to authenticate them when they try to log in later. Nothing mind-blowing about that.

Let’s start by installing an npm package called argon2-ffi.

npm install --save argon2-ffi

You might have seen ‘bcryptjs’ being mentioned a lot in tutorials, but it’s not longer advisable to use it for hashing passwords. Instead, use ‘Argon2’ which is recommended by the OWSP. To use it in node, you have two libraries to choose: 1) node-argon2, and 2) argon2-ffi. I’m on Option-2 because it was painless to install on my Windows machine. 😅

These hashing libraries provide us two important functions with respect to user authentication: 1) To ‘hash’ a user’s plain-text password for storage, and 2) To compare a user’s entered plain-text password with its corresponding hashed password for authentication. We will create a wrapper file for those functions:

Let’s illustrate the practicality of hashing from the perspective of registering a new user:

Believe it!

User Authentication

To authenticate a user during sign in, we need to compare the password entered by user to its hashed counterpart:

Most crucially, if the passwords are matched, we will set a isLoggedIn flag to the user’s session object(Line-27). This flag will be checked against when the user subsequently call any of our authenticated APIs — If the flag is true, allow the user to proceed without asking for their credentials again; If false, prompt the user to login in.

Protect your private routes

In our auth.js wrapper file above, we export a function called authCheck .

Here it is again:

function authCheck(req, res, next) {
if (!req.session.isLoggedIn || !req.session.user.user_id) {
next(
createError(401, "You are not authorized", {
refererUri: new URL(req.headers.referer).pathname,
})
);
return;
}
next();
}

Notice that we are checking against the isLoggedIn property to determine if a particular user is authenticated.

We will use the authCheck as a middleware for our private routes:

router.get(
"/logout",
Auth.authCheck,
asyncHandler(async (req, res) => {
req.session.destroy();
res.json({});
})
);

User Sign Out

This is embarrassingly easy:

router.post("/logout", (req, res) => {
req.session.destroy();
});

Once a user’s session has been destroyed, she won’t be able to get pass our authCheck middleware since the isLoggedIn check will fail.

And there you have it, all the critical parts of a user authentication system.

OK that’s all I got! Bye 👋

Welcome to China by Kheoh Yee Wei

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store