Nodejs | authentication using Passportjs and passport-local-mongoose

Passport is the authentication middleware for Node. It is designed to serve a singular purpose which is to authenticate requests. It is not practical to store user password as the original string in the database but it is a good practice to hash the password and then store them into the database. But with passport-local-mongoose you don’t have to hash the password using the crypto module, passport-local-mongoose will do everything for you. If you use passport-local-mongoose this module will auto-generate salt and hash fields in the DB. You will not have a field for the password, instead, you will have salt and hash.

Why salting of password is needed

If the user simply hashes their password and if two users in the database have the same password, then they’ll have the same hash. And if any one of the passwords is hacked then the hacker can access every account using the same password because users with the same password will have the same hash fields.
So before we hash it, we prepend a unique string. Not a secret, just something unique. so the hash is completely different for every salt.

Steps to perform the operation

First let’s generate an express app, then install the needed modules

> npm install passport passport-local mongoose passport-local-mongoose --save

1.First create a directory structure as below :

--model
----user.js
--route
----user.js
--app.js

2.Create model/user.js file which defines user schema

filter_none

edit
close

play_arrow

link
brightness_4
code

// importing modules
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
  
  
var UserSchema = new Schema({   
    email: {type: String, required:true, unique:true},
    username : {type: String, unique: true, required:true},
});
  
// plugin for passport-local-mongoose
UserSchema.plugin(passportLocalMongoose);
  
// export userschema
 module.exports = mongoose.model("User", UserSchema);

chevron_right


Note that here in this schema we did not add any field for password unlike we do normally. This is because passport-local-mongoose doesn’t need it. Here we did not add any methods to hash our password or to compare our passwords as we do normally for authentication because passport-local-mongoose will do all that for us.

3.Configure Passport/Passport-Local in app.js :

In app.js first, you have to initialize the passport

filter_none

edit
close

play_arrow

link
brightness_4
code

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

chevron_right


The passport will maintain persistent login sessions. In order for persistent sessions to work in the passport, the authenticated user must be serialized to the session and deserialized when subsequent requests are made. With passport-local-mongoose it is

filter_none

edit
close

play_arrow

link
brightness_4
code

passport.serializeUser(User.serializeUser());
passport.deserializeUser(User.deserializeUser());

chevron_right


The serialize and deserialize code can be slightly different if you are not using passport-local-mongoose with the passport.

Now we have to define the strategies for passport. For passport local mongoose the code is

filter_none

edit
close

play_arrow

link
brightness_4
code

const User = require('./models/user');
  
const LocalStrategy = require('passport-local').Strategy;
passport.use(new LocalStrategy(User.authenticate()));

chevron_right


If you are using only passport without passport-local-mongoose the local strategy can be quite different i.e.) you will have to write code to compare passwords and all. Here just this 2 lines are enough

4.Create route/user.js file :

First import the user-schema along with other necessary modules

filter_none

edit
close

play_arrow

link
brightness_4
code

// importing modules 
const express = require('express'); 
const router = express.Router(); 
    
// importing User Schema 
const User = require('../model/user');

chevron_right


5.For registering :

Now for registering the code should be

filter_none

edit
close

play_arrow

link
brightness_4
code

router.post('/login', function(req, res) {
      
    Users=new User({email: req.body.email, username : req.body.username});
  
          User.register(Users, req.body.password, function(err, user) {
            if (err) {
              res.json({success:false, message:"Your account could 
              not be saved. Error: ", err}) 
            }else{
              res.json({success: true, message: "Your account has 
               been saved"})
            }
          });
});

chevron_right


See in the above code we did not define our password in New user. Instead, we use the password with the User.Register() which is a passport-local-mongoose function. Now if you check your database for your saved user, it will be like below

{
    "_id" : ObjectId("5ca8b66535947f4c1e93c4f1"),
    "username" : "username you gave",
    "email" : "email you gave",
    "salt" : "4c8f6e009c4523b23553d9479e25254b266c3b7dd2dbc6d4aaace01851c9687c",
    "hash" : "337de0df58406b25499b18f54229b9dd595b70e998fd02c7866d06d2a6b25870d23650
cdda829974a46e3166e535f1aeb6bc7ef182565009b1dcf57a64205b5548f6974b77c2e3a3c6aec5360d
55f9fcd3ffd6fb99dce21aab021aced72881d3720f6a0975bfece4922282bb748e0412955e0afa2fb8c9
f5055cac0fb01a4a2288af2ce2a6563ed9b47852727749c7fe031b6b7fbb726196dbdfeeb6766d5cba6a
055f66eeacce685daef8b6c1aed0108df511c92d49150efb6473ee71c5149dd06bfb4f73cb60f9815af0
1e02fde8d8ed822bb3a55f040237cf80de0b1534de6bbafcb53f882c6eb03de4b4aa307828974eb51261
661efb5155e68ad0e593c0f5fab7d690c2257df4369e9d5ac7e2fc93b5b014260c6f8fbb01034b3f85ec
f11e086e9bf860f959d0e2104a1f825d286c99d3fc6f62505d1fde8601345c699ea08dcc071e5547835c
16957d3830998a10762ebd143dc557d6a96e4b88312e1e4c51622fef3939656c992508e47ddc148696df
3152af76286d636d4814a0dc608f72cd507c617feb77cbba36c5b017492df5f28a7a3f3b7881caf6fb4a
9d6231eca6edbeec4eb1436f1e45c27b9c2bfceccf3a9b42840f40c65fe499091ba6ebeb764b5d815a43
d73a888fdb58f821fbe5f7d92e20ff8d7c98e8164b4f10d5528fddbcc7737fd21b12d571355cc605eb36
21f5f266f8e38683deb05a6de43114",
    "__v" : 0
}

You can notice that there is no field for password at all, instead passport-local-mongoose created salt and hash for you, that too you didn’t have to define salt and hash fields in your schema. Passport-local-mongoose will keep your username unique, ie if username already exist it will give “UserExistsError”.

5.For login :

Now for login

filter_none

edit
close

play_arrow

link
brightness_4
code

userController.doLogin = function(req, res) {
  if(!req.body.username){
    res.json({success: false, message: "Username was not given"})
  } else {
    if(!req.body.password){
      res.json({success: false, message: "Password was not given"})
    }else{
      passport.authenticate('local', function (err, user, info) { 
         if(err){
           res.json({success: false, message: err})
         } else{
          if (! user) {
            res.json({success: false, message: 'username or password incorrect'})
          } else{
            req.login(user, function(err){
              if(err){
                res.json({success: false, message: err})
              }else{
                const token =  jwt.sign({userId : user._id, 
                   username:user.username}, secretkey, 
                      {expiresIn: '24h'})
                res.json({success:true, message:"Authentication 
                    successfull", token: token });
              }
            })
          }
         }
      })(req, res);
    }
  }
};

chevron_right


6.Resetting or changing passwords :

You can reset or change passwords using 2 simple functions in passport-local-mongoose. They are setPassword function and changePassword functions. Normally setPassword is used when the user forgot the password and changePassword is used when the user wants to change the password.

for setPassword code is

// user is your result from userschema using mongoose id
 user.setPassword(req.body.password, function(err, user){ ..

For changePassword

// user is your result from userschema using mongoose id
  user.changePassword(req.body.oldpassword, req.body.newpassword, function(err) ...

You can directly call them in your router files



My Personal Notes arrow_drop_up

Full stack developer Node, Angular, Express and MongoDb

If you like GeeksforGeeks and would like to contribute, you can also write an article using contribute.geeksforgeeks.org or mail your article to contribute@geeksforgeeks.org. See your article appearing on the GeeksforGeeks main page and help other Geeks.

Please Improve this article if you find anything incorrect by clicking on the "Improve Article" button below.




Article Tags :

2


Please write to us at contribute@geeksforgeeks.org to report any issue with the above content.