'Password hashing using bcrypt in nodejs

I am using "bcrypt": "^3.0.4" and I have the latest stable version of node. The issue I am facing is when the user attempts to login. With the correct password the initial login attempt is always unsuccessful while the second attempt is successful. I would like to know where I am getting it wrong. The assumption here is that the user has entered an email/username that is available in the database, mongodb in this case.

User.findOne({ "email": user.email }, (err, ruser) => {
    if (err) {
        res.send(err);
    }
    else {
        bcrypt.hash(user.password, 10, function (err, hash) {
            if (err) {
                console.log(err);
            }
            else {
                bcrypt.compare(user.password, hash).then(function (res) {
                    if (res) {
                        pass = true;
                    }
                    else {
                        pass = false;
                    }
                });
                res.json(pass);
            }
        });
    }
});

Thank you in advance.



Solution 1:[1]

There are two issues with your code.

  1. bcrypt.compare is async and the res.json is outside of the .then, so res.json is actually ran before comparison or pass it set.

  2. pass is not defined with var/let, so pass becomes a global variable and this is serious security implication, because two user logging in at the same time will cause a race condition (e.g. pass from session A may be res.json-ed in session B)

It works as a result of the above two errors, in the first request, res.json is executed before comparison and pass is not set yet, so it returns null. On the second request, again res.json is executed before compare, however this time the pass contains the value of the last compare, so what you are getting is actually last compare's result.

In addition, it is ok if you are just testing, otherwise this code does nothing, because this code generates the hash for the given password, and compares the given password to the generated hash, which definitely should be always true.

Solution 2:[2]

import { hash, compare } from 'bcryptjs';

export const hashPassword = async (password: string): Promise<string> => {
  const hashedPassword = await hash(password, 10);
  return hashedPassword;
};

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Eric Wong
Solution 2 Hussain