April 09, 2014,

Using Passport.js and Mongoose for Local User Authentication

Building an authentication system in Node.js can be challenging, especially for
integrating OAuth2.0 and authenticated API-based endpoints. Passport.js is great
for connecting services and for a base to integrate OAuth, but getting
Passport.js set up for an existing Mongoose User model can be a little
challenging. In this guide, you’ll see the fastest and easiest way to integrate
Mongoose and Passport.js for local user authentication.

Adding Libraries & the User Schema

For this tutorial, we’re assuming you already have an Express.js project set up.
If you don’t, there are countless great guides for getting started with Express
on various websites. After you’re set up in Express, you’ll need to install a
few libraries that we’re going to be using in this project. The following
command should get everything set up and saved into your package.json file:

To start our app off, we need to create a user schema with Mongoose. If you’re
adding Passport.js into an existing project, you probably already have a
Mongoose schema for users, but make sure to add the authenticate static method
into this schema to make implementing Passport a little easier later on. For
this tutorial, we’re going to use email addresses rather than usernames (because
in the end, it’s one less thing to make our users remember)—you’re free to
replace email with username to ask for usernames instead. Here’s the schema
we’re using (note that we’re automatically hashing the users password):

varMongoose=require('mongoose');varHash=require('password-hash');varSchema=Mongoose.Schema;varUserSchema=newSchema({email:{type:String},password:{type:String,set:function(newValue){returnHash.isHashed(newValue)?newValue:Hash.generate(newValue);}},// ... add any other properties you want to save with users ...});UserSchema.statics.authenticate=function(email,password,callback){this.findOne({email:email},function(error,user){if(user&amp;&amp;Hash.verify(password,user.password)){callback(null,user);}elseif(user||!error){// Email or password was invalid (no MongoDB error)error=newError("Your email address or password is invalid. Please try again.");callback(error,null);}else{// Something bad happened with MongoDB. You shouldn't run into this often.callback(error,null);}});};

Because we want to keep our code organized, we’re adding that new static
function authenticate(email, password, callback) to this model for checking if
credentials match a user in our table. We’re going to use a callback function
with two parameters, error, user (and they’ll translate nicely to Passport in
that order).

Configuring a Passport Strategy

Passport.js uses Strategies for serving
different sources of users. For example, a Facebook strategy would let your
users sign in with Facebook, and in our case, a local strategy will let us
use our own database to store account info. We already have a Mongoose user
schema set up from the last step, so we’re ready to write a strategy for
Passport now. We’re also going to add a serializer and deserializer, which will
tell Passport how to save the login credentials in the browser session and
retrieve them later, and we’re adding the connect-flash module into Express so
we can show error messages for invalid credentials.

// ... set up & connect mongoose here ...varUser=mongoose.model(UserSchema);varpassport=require('passport');varPassportLocalStrategy=require('passport-local');varauthStrategy=newPassportLocalStrategy({usernameField:'email',passwordField:'password'},function(email,password,done){User.authenticate(email,password,function(error,user){// You can write any kind of message you'd like.// The message will be displayed on the next page the user visits.// We're currently not displaying any success message for logging in.done(error,user,error?{message:error.message}:null);});});varauthSerializer=function(user,done){done(null,user.id);};varauthDeserializer=function(id,done){User.findById(id,function(error,user){done(error,user);});};passport.use(authStrategy);passport.serializeUser(authSerializer);passport.deserializeUser(authDeserializer);// ... continue with Express.js app initialization ...app.use(require('connect-flash')());// see the next sectionapp.use(passport.initialize());

Authenticating Users

By integrating Passport.js, authenticating users is now extremely simple. First,
we’re going to need a form that sends an HTTP-POST request to our login route
with a email field and a password field—if they’re named incorrectly, the
form will not work. You can take care of that form page on your own. We’re
going to handle that form with this route:

Also notice that we’re using Flash. We added connect-flash into our project,
an extension for displaying one-time messages and objects. With Flash, we’re
able to get error info during the next request (presumably for the login form
again), as an array (either empty, or an array filled with a string error
message). You can also use Flash for other kinds of messages, like success
messages, or for persisting any Javascript object. Once a single Flash message
is retrieved by your app with the code below, it will be removed and not
redisplayed. With Flash, everything will be as an array (sometimes empty) for
multiple messages.

app.get('/login',function(req,res,next){varerrors=req.flash('error');// req.flash('success', 'You can add messages by including a second parameter with the function.');// ... respond to the request ...});

Success!

Congratulations on building a new Strategy for authenticating your users in
Passport. Stay tuned for the next article on how to implement OAuth2.0 into a
Mongoose local stategy with Passport. If you have any questions about this
article or find any issues, just let me know on
Twitter—@hnryjms.