Local Authentication Using Passport in Node.js

In Passport Authentication for Node.js Applications, I 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 backend.

Prerequisites

  • Node.js – Download and install Node.js.
  • Install Express using the command npm install -g express.
  • MongoDB – Download and install MongoDB. 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 setup, we can create our Express server:

express LocalAuthApp
cd LocalAuthApp
npm install

Next, install the passport and passport-local Node modules using the following commands.

npm install passport
npm install passport-local

Next, start the node server using the following command. To verify that everything is setup correctly, point your browser to http://localhost:3000, where you should be greeted with an Express page.

node app.js

Implementing Local Authentication

In the views directory, create login.html, containing the following code.

<html>
  <body>
    <form action="/login" 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>

Inside app.js, add the following route:

app.get('/login', function(req, res) {
  res.sendfile('views/login.html');
});

You can view the new login page by restarting the server and visiting /login. Next, let’s implement the login post method to handle authentication. In app.js, add these require() statements:

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

We also need to add the Passport middleware for authentication to work. Before the line that reads:

app.use(app.router);

Add these two lines:

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

We also need to define the login handler routes, which are shown below.

app.post('/login',
  passport.authenticate('local', {
    successRedirect: '/loginSuccess',
    failureRedirect: '/loginFailure'
  })
);

app.get('/loginFailure', function(req, res, next) {
  res.send('Failed to authenticate');
});

app.get('/loginSuccess', function(req, res, next) {
  res.send('Successfully authenticated');
});

Passport also needs to serialize and deserialize the user instance, so add the following code.

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

passport.deserializeUser(function(user, done) {
  done(null, user);
});

Next, define the local authentication strategy, as shown below. Note that we will add the authentication check logic later.

passport.use(new LocalStrategy(function(username, password, done) {
  process.nextTick(function() {
    // Auth Check Logic
  });
}));

Creating a MongoDB Data Store

Start the mongod server using the following command:

mongod --config /etc/mongodb.conf

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.

Connecting to Mongo from Node

We’ll be using Mongoose to connect to MongoDB from our Node application. From the terminal, type npm install mongoose to install the Mongoose module. Next, add Mongoose to app.js using the following code.

var mongoose = require('mongoose/');

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

We’ll use schemas and models to work with data in Mongo. Schemas define the structure of the data inside a collection, and models are used to create instances of data. So, let’s create one:

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

It’s time to include our logic to authenticate the user using the UserDetails model. Here is how we do it:

passport.use(new LocalStrategy(function(username, password, done) {
  process.nextTick(function() {
    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);
    });
  });
}));

We simply used 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 then the above code returns the user object. Otherwise, it returns false.

Restart your node server and point your browser to http://localhost:3000/login and try to login.

Conclusion

In this article, we learned about 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. All of the code from this article is available for download on GitHub.

Free book: Jump Start HTML5 Basics

Grab a free copy of one our latest ebooks! Packed with hints and tips on HTML5's most powerful new features.

  • Francis Bissonnette

    app.use(passport.initialize());
    TypeError: Cannot call method ‘initialize’ of undefined

    And I didn’t skip any step. Help! Thx!

    • http://www.techillumination.in Jay

      make sure u have passport installed…..and app declared….

  • Luke Bonaccorsi

    No no no no no no no!

    Unencrypted passwords? Seriously?

  • Allan Ebdrup

    unhashed passwords and mongoose…
    Might I suggest salting and hashing passwords with for example bcrypt, and using mongojs instead of mongoose?

    • http://www.techillumination.in Jay

      off course ….but the tutorial just focused on doing local authentication ….salting and hashing is off course necessary

  • http://www.techillumination.in Jay

    thnks Alex :)

  • http://www.techillumination.in Jay

    thnks Eduardo :)

  • Blake

    Thank you Jay!

    Is it possible to use passport to authenticate from a single field, instead of user/password combo? Like, a user is provided a unique code, he enters the code, and passport checks the DB for a match?

  • Larry Eliemenye

    for some odd reasons my “verifier” function never gets called. Hitting the /login route(method is post, form values are username and password just like the default fields) always ends up calling the failureRedirect handler. Any idea why this is happeneing?