Search code examples
mongodbexpressmongoosebcrypt

How do you validate password using mongoose for mongoDB in an express app for logging in a user?


I am trying to have a user log in by their email and password. MongoDb docs shows hashing the password with bcrypt in the user model. It also provides a nice way to validate the password in the model as well. My problem is how to I use that validation from the "controller"? I am very aware "if (req.body.password === user.password)" will not work because one is hashed and the other is not.

I have been searching for answers for hours and can't seem to find that connection on how I use that "UserSchema.methods.comparePassword" method in my post request to log in. This isn't completely a real log in, just trying to get the password to validate and send back a key once logged in. Here are the docs: https://www.mongodb.com/blog/post/password-authentication-with-mongoose-part-1

// This is my UserModel

let mongoose = require('mongoose'),
  Schema = mongoose.Schema,
  bcrypt = require('bcrypt'),
  SALT_WORK_FACTOR = 10
var hat = require('hat');

let UserSchema = new Schema({
  email: {
    type: String,
    required: true,
    index: {
      unique: true
    }
  },
  password: {
    type: String,
    require: true
  },
  api_key: {
    type: String
  }
});

UserSchema.pre('save', function(next) {
    var user = this;

// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();

// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function(err, salt) {
    if (err) return next(err);

    // hash the password using our new salt
    bcrypt.hash(user.password, salt, function(err, hash) {
        if (err) return next(err);

        // override the cleartext password with the hashed one
        user.password = hash;
        user.api_key = hat();
        next();
    });
});


});

UserSchema.methods.comparePassword = function(candidatePassword, cb) {
    bcrypt.compare(candidatePassword, this.password, function(err, isMatch) {
        if (err) return cb(err);
        cb(null, isMatch);
    });
};

module.exports = mongoose.model('user', UserSchema);

// This is the sessions.js

let UserModel = require('../../../models/user.model');
var express = require('express');
var router = express.Router();

router.post('/', (req, res, next) => {
  UserModel.findOne(
   {
     $or: [
            { email : req.body.email }
          ]
   }
)
  .then(user => {
    if (req.body.password === user.password) {
      res.setHeader("Content-Type", "application/json");
      res.status(200).send(JSON.stringify({
        "api_key": `${user.api_key}`
        }));
    } else {
      res.status(404).send("Incorrect email or password")
    }
  })
  .catch(error => {
    res.setHeader("Content-Type", "application/json");
    res.status(500).send({error})
  })
})

module.exports = router

If I just find user by email, everything works fine. Just need to figure out how to use the compare password method in the user model. Thanks!


Solution

  • Maybe have something like this in your model:

    User = require('./user-model');
    
    .......
    
    User.findOne({ username: 'jmar777' }, function(err, user) {
        if (err) throw err;
        user.comparePassword('Password123', function(err, isMatch) {
            if (err) throw err;
            console.log('Password123:', isMatch); // -> Password123: true
        });
    ........
    

    Other resources:

    http://devsmash.com/blog/password-authentication-with-mongoose-and-bcrypt

    https://www.abeautifulsite.net/hashing-passwords-with-nodejs-and-bcrypt

    https://medium.com/@mridu.sh92/a-quick-guide-for-authentication-using-bcrypt-on-express-nodejs-1d8791bb418f

    Hope it helps!