Local Authentication Using Passport in Node.js

In Passport Authentication for Node.js Applications, we talked about authentication using Passport as it relates to social login (Google, Facebook, GitHub, etc.). In this article, we’ll see how we can use Passport for local authentication with a MongoDB back end.

All of the code from this article is available for download on GitHub.

Prerequisites

  • Node.js — Download and install Node.js.
  • MongoDB — Download and install MongoDB Community Server. Follow the instructions for your OS. Note, if you’re using Ubuntu, this guide can help you get Mongo up and running.

Creating the Project

Once all of the prerequisite software is set up, we can get started.

--ADVERTISEMENT--

We’ll begin by creating the folder for our app and then accessing that folder on the terminal:

mkdir AuthApp
cd AuthApp

To create the node app, we’ll use the following command:

npm init

You’ll be prompted to provide some information for Node’s package.json. Just hit enter until the end to leave the default configuration.

HTML

Next, we’ll need a form with username and password inputs as well as a Submit button. Let’s do that! Create a file called auth.html in the root folder of your app, with the following contents:

<html>
  <body>
    <form action="/" method="post">
      <div>
        <label>Username:</label>
        <input type="text" name="username" />
        <br/>
      </div>
      <div>
        <label>Password:</label>
        <input type="password" name="password" />
      </div>
      <div>
        <input type="submit" value="Submit" />
      </div>
    </form>
  </body>
</html>

That will do just fine.

Setting up Express

Now we need to install Express, of course. Go to the terminal and write this command:

npm install express --save

We’ll also need to install the body-parser middleware which is used to parse the request body that Passport uses to authenticate the user.

Let’s do that. Run the following command:

npm install body-parser --save

When that’s done, create a file index.js in the root folder of your app and add the following content to it:

/*  EXPRESS SETUP  */

const express = require('express');
const app = express();

const bodyParser = require('body-parser');
app.use(bodyParser.urlencoded({ extended: true }));

app.get('/', (req, res) => res.sendFile('auth.html', { root : __dirname}));

const port = process.env.PORT || 3000;
app.listen(port , () => console.log('App listening on port ' + port));

We’re doing almost the same as in the previous tutorial. First we require Express and create our Express app by calling [express()](http://expressjs.com/en/api.html#express). The next line is the only difference with our previous Express setup. We’ll need the body-parser middleware this time, in order for authentication to work correctly. Then we declare the route for the home page of our app. There we send the HTML file we created to the client accessing that route. Then, we use process.env.PORT to set the port to the environment port variable if it exists. Otherwise, we’ll default to 3000, which is the port we’ll be using locally. This gives you enough flexibility to switch from development, directly to a production environment where the port might be set by a service provider like, for instance, Heroku. Right below we called [app.listen()](http://expressjs.com/en/api.html#app.listen) with the port variable we set up and a simple log to let us know that it’s all working fine and on which port is the app listening.

That’s all for the Express setup. Now we have to set up Passport, exactly as we did the last time. I’ll show you how to do that in case you didn’t read the previous tutorial.

Setting up Passport

First, we install passport with the following command:

npm install passport --save

Then, add the following lines at the bottom of your index.js file:

/*  PASSPORT SETUP  */

const passport = require('passport');
app.use(passport.initialize());
app.use(passport.session());

app.get('/success', (req, res) => res.send("Welcome "+req.query.username+"!!"));
app.get('/error', (req, res) => res.send("error logging in"));

passport.serializeUser(function(user, cb) {
  cb(null, user.id);
});

passport.deserializeUser(function(id, cb) {
  User.findById(id, function(err, user) {
    cb(err, user);
  });
});

Here, we require passport and initialize it along with its session authentication middleware, directly inside our Express app. Then, we set up the '/success' and '/error' routes which will render a message telling us how the authentication went. If it succeeds, we’re going to show the username parameter, which we’ll pass to the request. We’re using the same syntax for our last route, only this time instead of using [res.SendFile()](http://expressjs.com/en/api.html#res.sendFile) we’re using [res.send()](http://expressjs.com/en/api.html#res.send), which will render the given string as text/html on the browser. Then we’re using serializeUser and deserializeUser callbacks. The first one will be invoked on authentication, and its job is to serialize the user instance with the information we pass to it (the user ID in this case) and store it in the session via a cookie. The second one will be invoked every subsequent request to deserialize the instance, providing it the unique cookie identifier as a “credential”. You can read more about that in the Passprot documentation.

As a side note, this very simple sample app of ours will work just fine without deserializeUser, but it kills the purpose of keeping a session, which is by all means something you’ll need in every app that requires login.

Creating a MongoDB Data Store

Since we’re assuming you’ve already installed Mongo, you should be able to start the mongod server using the following command:

sudo mongod

From another terminal, launch the Mongo shell:

mongo

Within the shell, issue the following commands:

use MyDatabase;

db.userInfo.insert({'username':'admin','password':'admin'});

The first command creates a data store named MyDatabase. The second command creates a collection named userInfo and inserts a record. Let’s insert a few more records:

db.userInfo.insert({'username':'jay','password':'jay'});
db.userInfo.insert({'username':'roy','password':'password'});

Retrieving Stored Data

We can view the data we just added using the following command:

db.userInfo.find();

The resulting output is shown below:

{ "_id" : ObjectId("5321cd6dbb5b0e6e72d75c80"), "username" : "admin", "password" : "admin" }
{ "_id" : ObjectId("5321d3f8bb5b0e6e72d75c81"), "username" : "jay", "password" : "jay" }
{ "_id" : ObjectId("5321d406bb5b0e6e72d75c82"), "username" : "roy", "password" : "password" }

We can also search for a particular username and password:

db.userInfo.findOne({'username':'admin','password':'admin'})

This command would return only the admin user.

Connection Mongo to Node with Mongoose

Now that we have a database with records in it, we need a way to communicate with it from our application. We’ll be using Mongoose to achieve this. Why don’t we just use plain Mongo? Well, as the Mongoose devs like to say on their website:

writing MongoDB validation, casting and business logic boilerplate is a drag.

Mongoose will simply make our lives easier and our code more elegant.

Let’s go ahead and install it with the following command:

npm install mongoose --save

Now we have to configure Mongoose. You know the drill: add the following code at the bottom of your index.js file:

/* MONGOOSE SETUP */

const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/MyDatabase');

const Schema = mongoose.Schema;
const UserDetail = new Schema({
      username: String,
      password: String
    });
const UserDetails = mongoose.model('userInfo', UserDetail, 'userInfo');

Here we require the mongoose package. Then we connect to our database using mongoose.connect and give it the path to our database. Then we’re making use of Schema to define our data structure. In this case, we’re creating a UserDetail schema with username and password fields. After that, we create a model from that schema. The first parameter is the name of the collection in the database. The second one is the reference to our Schema, and the third one is the name we’re assigning to the collection inside Mongoose.

That’s all for the Mongoose setup. We can now move on to implementing our Strategy.

Implementing Local Authentication

It’s time to configure our authentication strategy using passport-local. Let’s go ahead and install it. Run the following command:

npm install passport-local --save

Finally, we’ve come to the last portion of the code! Add it to the bottom of your index.js file:

/* PASSPORT LOCAL AUTHENTICATION */

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

passport.use(new LocalStrategy(
  function(username, password, done) {
      UserDetails.findOne({
        username: username
      }, function(err, user) {
        if (err) {
          return done(err);
        }

        if (!user) {
          return done(null, false);
        }

        if (user.password != password) {
          return done(null, false);
        }
        return done(null, user);
      });
  }
));

app.post('/',
  passport.authenticate('local', { failureRedirect: '/error' }),
  function(req, res) {
    res.redirect('/success?username='+req.user.username);
  });

Let’s go through this. First, we require the passport-local Strategy. Then we tell Passport to use an instance of the LocalStrategy we required. There we simply use the same command that we used in the Mongo shell to find a record based on the username. If a record is found and the password matches, the above code returns the user object. Otherwise, it returns false.

Below the strategy implementation is our post, with the [passport.authenticate](http://www.passportjs.org/docs/authenticate/) method which attempts to authenticate with the given strategy on its first parameter, in this case 'local'. It will redirect us to '/error' if it fails. Otherwise it’ll redirect us to the '/success' route, sending the username as a parameter. That’s how we get the username to show on the line req.query.username we saw earlier.

That’s all we need for the app to work. We’re done!

Restart your Node server and point your browser to http://localhost:3000/ and try to log in with “admin” as username and “admin” as password. If it all goes well, you should see the message “Welcome admin!!” in the browser.

Conclusion

In this article, we learned how to implement local authentication using Passport in a Node.js application. In the process, we also learned how to connect to MongoDB using the Mongoose.

We only added the necessary modules for this app to work — nothing more, nothing less. For a production app, you’ll need to add other middlewares and separate your code in modules. You can take that as a challenge to set up a clean and scalable environment and grow it into something useful! Please do share it with me if you do. See you on the next one!

Sponsors