Compare passwords BcryptJS
Asked Answered
G

6

26

So I'm trying to build a very basic user login. I'm trying to create a user, then login with those credentials and get back a JSON Web Token. Where I'm stuck is trying to compare the passwords then send a response.

Steps:

Create User:

  1. enter email and password
  2. salt/hash user password
  3. store user into database
  4. return success

Login

  1. find user by request email value
  2. if found compare passwords
  3. passwords good send JSON Web Token

User Model

email:{ 
  type: String,
  required: true,
  unique: true
},
password: {
  type: String,
  required: true
}

User Routes

var express     = require('express');
var router      = express.Router();
var jwt         = require('jsonwebtoken');
var bcrypt      = require('bcryptjs');

// Create User
...
bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash("superSecret", salt, function(err, hash) {
      user.password = hash;
      user.save();
      res.json({success: true, message: 'Create user successful'});
    });
  });
...

// Login
...
bcrypt.compare(req.body.password, 'superSecret', function(err, res) {
  if(req.body.password != user.password){
    res.json({success: false, message: 'passwords do not match'});
  } else {
    // Send JWT
  }
});

So the two problems here is that, I can't send a response nor can I compare the password. Just completely stuck on this, any help would be greatly appreciated.

Give answered 16/10, 2016 at 23:31 Comment(0)
L
24

As described in the doc, you should use bcrypt.compare like that:

bcrypt.compare(req.body.password, user.password, function(err, res) {
  if (err){
    // handle error
  }
  if (res) {
    // Send JWT
  } else {
    // response is OutgoingMessage object that server response http request
    return response.json({success: false, message: 'passwords do not match'});
  }
});

And here is a nice post about Password Authentication with Mongoose (Part 1): bcrypt

Lymphosarcoma answered 17/10, 2016 at 1:57 Comment(3)
That was one of the articles I was referencing before and wasn't registering at first will take another look though. The other issue is running res.[send][json] inside the compare method will result in a unresolved method or function error. So the only way around that seems to be setting up some type of callback View GistGive
There is a problem about variable name conflict, res.json should be response.json, which response refers to OutgoingMessage. I have updated the answer, please check it.Lymphosarcoma
Missing a curly brace after if (res) {Azote
S
8
//required files
const express = require('express')
const router = express.Router();

//bcryptjs
const bcrypt = require('bcryptjs')

//User modal of mongoDB
const User = require('../../models/User')


//Post request for login
router.post('/', (req, res) => {
    //email and password
    const email = req.body.email
    const password = req.body.password

    //find user exist or not
    User.findOne({ email })
        .then(user => {
            //if user not exist than return status 400
            if (!user) return res.status(400).json({ msg: "User not exist" })

            //if user exist than compare password
            //password comes from the user
            //user.password comes from the database
            bcrypt.compare(password, user.password, (err, data) => {
                //if error than throw error
                if (err) throw err

                //if both match than you can do anything
                if (data) {
                    return res.status(200).json({ msg: "Login success" })
                } else {
                    return res.status(401).json({ msg: "Invalid credencial" })
                }

            })

        })

})

module.exports = router
Scope answered 30/10, 2019 at 10:36 Comment(0)
W
6

If we you to use bcryptjs in browser(HTML) then you can add bcryptjs CDN to do this.

CDN - https://cdn.jsdelivr.net/npm/[email protected]/dist/bcrypt.js

Example-

HTML- (Add above CDN in tag)

JS-

    var bcrypt = dcodeIO.bcrypt;

    /** One way, can't decrypt but can compare */
    var salt = bcrypt.genSaltSync(10);

    /** Encrypt password */
    bcrypt.hash('anypassword', salt, (err, res) => {
        console.log('hash', res)
        hash = res
        compare(hash)
    });

    /** Compare stored password with new encrypted password */
    function compare(encrypted) {
        bcrypt.compare('aboveusedpassword', encrypted, (err, res) => {
            // res == true or res == false
            console.log('Compared result', res, hash) 
        })
    }

If you want to do same in Nodejs

/** Import lib like below and use same functions as written above */

    var bcrypt = require('bcryptjs')
Waterrepellent answered 8/5, 2019 at 13:4 Comment(2)
And bob's your uncle. Valid in 2021Enclose
Sure, use download and trust security functions posted by third parties.Maes
G
3

From what I can see your logic is correct.

If you are using mongoose I suggest you to use the pre 'save' hook.

User Schema

userSchema.pre('save', function(next) {
  // only hash the password if it has been modified (or is new)
  if (!this.isModified('password')) {
    return next();
  }
  // generate a salt
  return bcrypt.genSalt(10, function(error, salt) {
    if (error) {
      return next(error);
    }

  // hash the password using the new salt
    return bcrypt.hash(this.password, salt, function(error, hash) {
      if (error) {
        return next(error);
      }
      // override the cleartext password with the hashed one
      this.password = hash;
      return next();
    });
  });
});


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

And in your routes:

Login

...
return user.comparePassword(password, function(error, isMatch) {
  var payload = {
  iat: Math.round(Date.now() / 1000),
  exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
  iss: 'Whatever the issuer is example: localhost:3000',
  email: user.email
  };

  var token = jwt.encode(payload, 'secret');
  if (isMatch && !error) {
    // if user is found and password is right create a token
    return res.json({
      success: true,
      token: `JWT ${token}`,
      user: user,
      msg: 'Authentication was succesful'
      });
    }
    return next({code: 401, msg: 'Password is incorrect'});
  });
});

Create user

// Pre hook will take care of password creation
return user.save()
.then(function(user) {
  var payload = {
  iat: Math.round(Date.now() / 1000),
  exp: Math.round((Date.now() / 1000) + 30 * 24 * 60),
  iss: 'Whatever the issuer is example: localhost:3000',
  email: user.email
  };

  var token = jwt.encode(payload, 'secret');
  return res.status(201).json({user, token: `JWT ${token}`, msg: 'User was succesfully created'});
})
.catch((err) => next(err));
Gerrigerrie answered 17/10, 2016 at 1:32 Comment(4)
So I'm taking what you suggested and breaking it down piece by piece. However, looks like user is undefined is happening when calling User.save()... Thinking that has something to do with the 'pre hook'. I've added the two gists so it's a bit easier to maintain the routes and model. Thanks again for the help user model user routeGive
So I've updated the 2 Gist, the User object is now passing into that pre save hook, however this.password is undefined. I've tried user.password but to no avail eitherGive
Looks like return bcrypt.genSalt(10, function(error, salt) { is where the ability to call this becomes undefinedGive
So the password is being hashed now, but the callback never returns back to the routes. return bcrypt.hash(){ ... newUser.password = hash; return next(); // everything stops here, callback never happens ... }Give
D
0
bcrypt.compare(req.body.password, user.password, function(err, results){
                if(err){
                    throw new Error(err)
                 }
                 if (results) {
                    return res.status(200).json({ msg: "Login success" })
                } else {
                    return res.status(401).json({ msg: "Invalid credencial" })
                }
               })
Deeannadeeanne answered 7/12, 2019 at 5:18 Comment(0)
M
0
const bcrypt = require("bcryptjs");
const salt = bcrypt.genSaltSync(10);

const hashPassword = (password) => bcrypt.hashSync(password, salt);
const comparePassword = (password, hashedPassword) =>
  bcrypt.compareSync(password, hashedPassword);


Microcrystalline answered 22/1, 2023 at 12:11 Comment(1)
As it’s currently written, your answer is unclear. Please edit to add additional details that will help others understand how this addresses the question asked. You can find more information on how to write good answers in the help center.Watford

© 2022 - 2024 — McMap. All rights reserved.