Search code examples
javascriptnode.jsmongodbbcryptpassport-local

how doe export work in javaScript wold. hash undefined bcryptjs


Let's say we have two file, user.js users.js in user.js we have. Why can we do module.exports.. we can use in it diff .js file? what does "@returns Promise If callback has been omitted" means it is from the bcrypt.genSalt function? I also have a github repo, so please take a look if you have a bit of time. after cloning it

stuck in terminal

    result { error: null,
  value:
   { email: 'max@mail.com',
     username: 'max',
     password: '1234',
     confirmationPassword: '1234' },
  then: [Function: then],
  catch: [Function: catch] }
hash undefined


const mongoose = require('mongoose');
const Schema = mongoose.Schema;
const bcrypt = require('bcryptjs');


const userSchema = new Schema({
  email: String,
  username: String,
  password: String
});


const User = mongoose.model('user', userSchema);
module.exports = User;
module.exports.hashPassword = (password) => {
 return hash = bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash(password, salt, function(err, hash) {
    });
});
}; 

in users.js we have have

const express = require('express');
const router = express.Router();
const Joi = require('joi');
const User = require('../models/user');   



const userSchema = Joi.object().keys({
  email:Joi.string().email().required(),
  username:Joi.string().required(),
  password:Joi.string().regex(/^[a-zA-Z0-9]{3,15}$/).required(),
  confirmationPassword:Joi.any().valid(Joi.ref('password')).required()
});



router.route('/register')
  .get((req, res) => {
    res.render('register');
  })
  .post(async (req, res, next) => {
    try{
        const result =  Joi.validate(req.body,userSchema);
        console.log('result',result);

        if(result.error) {
          req.flash('error', 'Data is not valid, please try again');
          res.redirect('/users/register');
          return;
        //console.log('result',result);
      }
      //  checking if email is already taken
      const user =  await User.findOne({'email':result.value.email });
        if (user){
          req.flash('error','Email is already in use');
          res.redirect('/users/register');
          return;
        }


     // console.log('hash',hash);

      // Hash the password
      const hash = await User.hashPassword(result.value.password); 
      console.log('hash',hash);

  } catch(error) {
    next(error);
    }
  });
module.exports = router;

based the example given by bcrypt

var bcrypt = require('bcryptjs');
bcrypt.genSalt(10, function(err, salt) {
    bcrypt.hash("B4c0/\/", salt, function(err, hash) {
        // Store hash in your password DB.
    });
});

adding pic for mongo db enter image description here


Solution

  • I think the problem lies in this line:

    const hash = await User.hashPassword(result.value.password);

    This implies that User.hashPassword(result.value.password) should be returning a promise (but it returns a reference to the wrong promise).

    module.exports.hashPassword = (password) => {
        return hash = bcrypt.genSalt(10, function (err, salt) {
            bcrypt.hash(password, salt, function (err, hash) {});
        });
    };
    

    Perhaps modifying the above to return a promise may help.. Like so:

    module.exports.hashPassword = (password) => {
        var salt = await bcrypt.genSalt(10);
        return bcrypt.hash(password, salt);
    };
    

    To answer your question about @returns Promise If callback has been omitted:

    Bcrypt methods are asynchronous. Which means they return immediately and process in the background. When the result is available, the function makes this available to the calling code either via a callback function or a promise.

    Consider the following API for genSalt from the docs:

    genSalt(rounds, minor, cb)

    rounds - [OPTIONAL] - the cost of processing the data. (default - 10)

    minor - [OPTIONAL] - minor version of bcrypt to use. (default - b)

    cb - [OPTIONAL] - a callback to be fired once the salt has been generated. uses eio making it asynchronous. If cb is not specified, a Promise is returned if Promise support is available.

       err - First parameter to the callback detailing any errors.
    
       salt - Second parameter to the callback providing the generated salt.
    

    What that says is genSalt can take three arguments: genSalt(rounds, minor, cb)

    Sample using callbacks

    If the calling code wants the result via a callback, it can pass a function which looks like function(err, salt){} as the cb parameter.

     bcrypt.genSalt(rounds, minor, function(err, salt){
         if(err){
             //Handle error
             return;
         }
         // Salt is available here
         console.log(salt);
     });
    

    Sample using promises

    If the cb parameter is not passed (null or undefined) the function returns a Promise instead.

     var promise = bcrypt.genSalt(rounds, minor);
     promise
         .then(function(salt){
             // Salt is available here
             console.log(salt);
         })
         .catch(function(err){
             // Handle error
         });