Tutorial: User Authentication with Passport.js

Amanda Atkins
4 min readFeb 4, 2020

--

Passport is a robust NodeJS package that easily allows a developer to incorporate user accounts and login/logout functionality into applications. For the purposes of this tutorial, we’re going to combine Passport with Express (and its session add on) and Bcryptjs, and use Passport’s Local Strategy. The local strategy will query a database for users and leverage Express, but this tutorial will not delve into the setup of databases or Express. For the purposes of this, the app variable refers to our express() app.

Required Packages

Initialize your Node application and install these packages:

Express Configuration

The express-session package enables us to create sessions and set cookies when our users are logged in. This allows the login to persist across multiple pages of the application.

First we’ll do the following:

app.use(session({ 
secret: "your secret line of secretness"
}));

This tells Express to use sessions, and how. The ‘secret’ is used to encrypt and decrypt cookies in order to check that a user’s login session is still active. It should be very secure so it’s not easily guessable by man or machine.

To finish our Express configuration for Sessions and Passport, we’ll include these two lines after the session setup:

app.use(passport.initialize());
app.use(passport.session());

The first line simply initializes Passport within our Express application. The second line sets Passport as a middleware between the cookie that Express-session sets and Passport’s own user authentication checks.

Passport Configuration

Now we write a configuration file specifically for Passport. We’ll include the following requirements:

const passport = require("passport");
const LocalStrategy = require("passport-local").Strategy;

We’ll also import our database models here as we will use them to check our database for user information. Next we’ll tell passport to use its Local Strategy to authenticate the user, and we’ll define what that means for our application.

passport.use(new LocalStrategy(
{
usernameField: "username",
passwordField: "password"
},
function(username, password, done) {

userDB.findOne({ where { username: username } })
.then(theUser => {
if (!theUser) {
return done(null, false, { message: "User does not exist" });
}
if (!theUser.validPass(password)) {
return done(null, false, { message: "Password is not valid." });
}
return done(null, true);
});
}
));

The LocalStrategy constructor has an options parameter and a callback function parameter. The options usernameField and passwordField tell LocalStrategy the key names for the corresponding items in the POST body. LocalStrategy uses username and password by default, so it’s safe to not include the options parameter if your request is sending the information using those keys.

The callback function receives the username and password via the login Express route when the user submits their information. Within this function, we query our database to see if the user exists, and then if the user does exist, we’ll validate that the supplied password matches the stored password. If it does, then we can log the user in by setting a cookie with the Express Session.

Bcryptjs is used to ensure a password is secure when stored in our database through hashing and salting. Part of our setup above will check that the password submitted by the user is valid according to Bcryptjs’s methods. See the Bcrypt password validation section below for more details.

Lastly, we’ll include the following:

passport.serializeUser(function(user, done) {
done(null, user.id);
});
passport.deserializeUser(function(user, done) {
User.findById(id, function (err, user) {
done(err, user);
});
});

These functions add the authenticated user’s information to the session store and to the body of every request. Now our Express app need only check the request body to ensure that a user is logged in on a page where being logged in is required. See the next section isLoggedIn helper function that is used to ‘hide’ a page from a non-logged in user.

The Login Route and isLoggedIn Helper Function

When the Express route for login is requested, we’ll want to use the Passport configuration we just set up to handle the information the user sent us.

app.post(
'/api/users/login',
passport.authenticate("local"),
function(req, res) {
res.json(req.user);
}
);

Using Express routing conventions, we’re denoting that any POST request to the url /api/users/login should first authenticate the user via passport.authenticate("local") before fulfilling the request. Our route will automatically pass the POST body to Passport authentication, which should include the username and password the user supplied. Passport Local will then handle the information according to what’s written in the callback function we dissected above.

We can create routes that are only accessible to a logged in user by creating a helper function.

function isLoggedIn(request, response, done) {   if (request.user) {
return done();
}

return res.redirect("/login");
};

This function will be used in our Express routes, which will pass the request and response objects to it, like so:

app.get('/profile',isLoggedIn, (req,res) => {
// Work
});

The isLoggedIn function checks for that .user key that Passport sets to the request when it serializes a user. If the user key is set, great! We’ll continue with our request. If not, we’ll redirect to the login page.

Bcrypt, my Password

Bcryptjs is used to securely encrypt a password using hashing and salting. For the uses in this tutorial, we’ve added its functionality to our User database model:

User.addHook("beforeCreate", newUser => {
newUser.password = bcrypt.hashSync(newUser.password, bcrypt.genSaltSync(10), null);
});

Our ORM supports a hook that runs before a new row is added to a table. Now every time a user registers for our site, we will encrypt the password they supplied before it is stored. We’ll use Bcryptjs’s .hashSync() function to hash the password and also .genSaltSync() to salt the password to make it further secure.

User.prototype.validPass = function(pw) {
return bcrypt.compareSync(pw, this.password);
};

Lastly we add a ‘validPass’ function which serves to take the password supplied on login and compare it to the password in the database using Bcryptjs’s compareSync() function, which encrypts and salts according to Bcrypts algorithms.

For more information on User Authentication with Passport, peruse the Passport documentation.

--

--