34

Following on from this question.

I feel like I'm almost there, but my incomplete understanding of async is preventing me from solving this. I'm basically trying to just hash a password using bcrypt and have decided to seperate out the hashPassword function so that I can potentially use it in other parts of the app.

hashedPassword keeps returning undefined though...

userSchema.pre('save', async function (next) {

  let user = this
  const password = user.password;

  const hashedPassword = await hashPassword(user);
  user.password = hashedPassword

  next()

})

async function hashPassword (user) {

  const password = user.password
  const saltRounds = 10;

  const hashedPassword = await bcrypt.hash(password, saltRounds, function(err, hash) {

    if (err) {
      return err;
    }

    return hash

  });

  return hashedPassword

}

7 Answers 7

100

await dosent wait for bcrypt.hash because bcrypt.hash does not return a promise. Use the following method, which wraps bcrypt in a promise in order to use await.

async function hashPassword (user) {

  const password = user.password
  const saltRounds = 10;

  const hashedPassword = await new Promise((resolve, reject) => {
    bcrypt.hash(password, saltRounds, function(err, hash) {
      if (err) reject(err)
      resolve(hash)
    });
  })

  return hashedPassword
}

Update:-

The library has added code to return a promise which will make the use of async/await possible, which was not available earlier. the new way of usage would be as follows.

const hashedPassword = await bcrypt.hash(password, saltRounds)
Sign up to request clarification or add additional context in comments.

6 Comments

Might be worth making it clear that you're basically wrapping bcrypt in a promise in order to use await. Nice solution though!
I was trying to stay away from using promises, but in this case it clearly needed to be done. Thanks for the solution – it worked :)
@Modermo I would strongly advise using promises, simply because it's the future of async code. All the fancy new async/await stuff is pinned on top of promises, so it's definitely worthwhile wrapping your head around them. Plus, it makes writing complex async code much much easier.
Agreed. The async stuff, in my limited experience, is SO much nicer to work with. In this instance though, I really don't know how I could avoid using a promise because I want hashPassword to be it's own function.
Absolutely, a promise is required for async/await. No other way around it because you can't do something like async return <value> in a callback (although this is something which would be really neat).
|
25

By default, bcrypt.hash(password,10) will return as promise. please check here

Example: Run the code,

var bcrypt= require('bcrypt');

let password = "12345";


var hashPassword = async function(){
    console.log(bcrypt.hash(password,10));
    var hashPwd = await bcrypt.hash(password,10);
    console.log(hashPwd);
}

hashPassword();

Output:

Promise { <pending> }
$2b$10$8Y5Oj329TeEh8weYpJA6EOE39AA/BXVFOEUn1YOFC.sf1chUi4H8i

When you use await inside the async function, it will wait untill it get resolved from the promise.

3 Comments

how do we then retrieve the password ?
@LV98 hashing is a one-way function and you won't be able to retrieve the password from hashed password. Also, if you run the code multiple times, you will see that the hashPwd changes as you generate new salt every time. So if you want to compare the passwords, you must use bcrypt.compare, as it is almost impossible to generate the same hash.
@nick, I think Squirrel.98 is asking how do you/we retrieve the generated hash. I imagine you could just return hashPwd after the await statement runs...
6

use The method bcrypt.hashSync(), It is Synchronous out of the box.

const hashedPassword = bcrypt.hashSync(password,saltRounds);

4 Comments

hashSync() is the synchronous version of hash, so you don't need to await it. Because asynchronous one returns promise, which needs to be awaited. You can use synchronous one like const hashed = bcrypt.hashSync(password, saltRounds)
Oh, actually you are right. I didn't realize that at the moment. I have edited my response. Thank you
@PrivateOmega the docs says, if your using bcrypt on a server, async is preferred: A Note on Timing Attacks Why is async mode recommended over sync mode? If you are using bcrypt on a simple script, using the sync mode is perfectly fine. However, if you are using bcrypt on a server, the async mode is recommended. This is because the hashing done by bcrypt is CPU intensive, so the sync version will block the event loop and prevent your application from servicing any other inbound requests or events. The async version uses a thread pool which does not block the main event loop.
@mike Yes ofcourse, what you wrote down is entirely correct and I know all of that. but I guess at that time, I was just answering to the question.
3

Hashing bcrypt asynchronously should be like this

bcrypt.hash(password, saltRounds, function(err, hash) {
  if (err) {
     throw err;
  }
  // Do whatever you like with the hash
});

If you are confused with sync and async. You need to read more about them. There are a lot of good articles out there.

3 Comments

I think I need to understand the bcrypt.hash async function better to understand what is going on under the hood.
Just went to understand the bcrypt function itself. As Akash said, indeed, bcrypt.hash doesn't return a promise, so I need to wrap it in one.
This may help someone else out there stackoverflow.com/a/52087581/1274820
2

You need to look here in the documentation.

Async methods that accept a callback, return a Promise when callback is not specified if Promise support is available.

So, if your function call takes in a callback then you can't use await on it since this function signature doesn't return a Promise. In order to use await you need to remove the callback function. You can also wrap it in a Promise and await on it but that's a bit overkill since the library already provides a mechanism to do so.

Code refactor:

try {
   // I removed the callbackFn argument
   const hashedPassword = await bcrypt.hash(password, saltRounds)
} catch (e) {
   console.log(e)
}

Comments

0
const hashedPassword = (password, salt) => {
    return new Promise((resolve, reject) => {
        bcrpyt.hash(password, salt, (err, hash) => {
            if (err) reject();
            resolve(hash);
        });
    });
};
hashedPassword('password', 10).then((passwordHash) => {
    console.log(passwordHash);
});

Comments

0

Had same issue... solved by assigning the Boolean value from a function:

compareHash = (password, hashedPassword) => {
if (!password || !hashedPassword) {
  return Promise.resolve(false);
}
return bcrypt.compare(password, hashedPassword);
 };

Here the 2 arguments will not be undefined, which is the cause of issue. And calling the function:

let hashCompare = this.compareHash(model.password, entity.password);

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.