how doe export work in javaScript wold. hash undefined bcryptjs - javascript

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

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
});

Related

await not working on mongoose model .findOne({email : req.body.email})

I am new to express and mongodb... I am trying to use user.findOne({email}) function returns the user with email with await keyword but the node js throws error saying
let user = await User.findOne({ email: req.body.email });
^^^^^
SyntaxError: await is only valid in async function
const express = require('express');
const User = require("../models/user");
const {body, validationResult} = require("express-validator");
const router = express.Router();
// Listening the request at assigned route
router.post('/', [
// Validation array
body("name", "Enter name").isLength({ min: 5 }),
body("email", "Enter valid email").isEmail(),
body("password", "password must not be less than 5").isLength({ min: 5 })
] ,
(req, res) => {
// Errors in a single json...
const errors = validationResult(req)
// If there error return the error json...
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
// Check if there any user with same email...
let user = await User.findOne({ email: req.body.email });
if (user) {
return res.status(400).json({ error: "A user with this email already exist..."});
}
// Create user...
// user = await User.create({
// name : req.body.name,
// email : req.body.email,
// password : req.body.password
// })
// res.json(user)
res.send("Successful request...👍");
})
module.exports = router
You can use await only in async functions.
You can then just write:
(req, res) => {
like
async (req, res) => {.
The other way would be to not use async (but that'd work too), is to use a callback function:
User.findOne({ email: req.body.email }).then((user)=>{ /* Your logic here */})
In general, I prefer to not mix up async/callbacks, so I stick to only use async or only use callbacks :)
Make the callback function asynchronous by using keyword
async (req, res) => {}
two problems are here:
if you want to use await your callback should be async:
async () => { await ... }
user in your code is referring to promises not use value came from promise. to do so you need to execute the query:
let user = await User.findOne({ email: req.body.email }).exec();
it is also better to use try catch to avoid your application breaks on bad request.
all in all:
async (req, res) => { // missing async
const errors = validationResult(req)
if (!errors.isEmpty()) {
res.status(400).json({ errors: errors.array() }); // no need for return
}
try {
let user = await User.findOne({ email: req.body.email}).exec(); // missing exec()
if (user) {
res.status(400).json({ error: "A user with this email already exist..."});
}
res.send("Successful request...👍");
}
catch (error) { console.log(error) }
})

Where is the result of async function in node app?

So i'm trying to build some basic signup on the backend side with nodejs and MySQL.
let params = [email, password];
const salt = bcrypt.genSaltSync(10);
var promise = bcrypt.hash(password, salt);
let query = 'insert into users (email, password) values (?,?)';
connection.promise().query(query, params, (err, result, fields) => {
console.log(err);
if(err && err.code === 'ER_DUP_ENTRY') {
res.status(400).json({
"message": "There is an account already associated with this email adress!"
});
res.end();
}
else {
res.status(200).json({
"message": "User created!"
});
res.end();
}
});
My problem is when I use the bcrypt.hash function, I dont know how to get the hashed password because the function returns me a promise. So how I access the hashed password so later I can introduce it in my db? I know I can use promise.then() but still don't have an idea about what to do after to get my password. Any ideas, this seems easy but still I am not smart enough
You should use await or Use bcrypt.hashSync() method instead of bcrypt.hash(),
const salt = bcrypt.genSaltSync(10);
const hashedPassword = bcrypt.hashSync(password, salt);
//or
const hashedPassword = await bcrypt.hash(password, salt);
Refer Doc: https://www.npmjs.com/package/bcrypt

MongooseJS object undefined inside callback function

I'm trying to write an endpoint for an API that will return all orders for a given user. My issue is that when I try to query the database using mongoose's findById function, the 'user' object is undefined in the callback function and I can't query the orders subdoc. To add to the confusion, I can get it to work if I don't use a callback function, but then I don't have proper error handling.
var mongoose = require('mongoose');
var router = express.Router();
var order_model = require('../models/order');
var user_model = require('../models/user');
router.get('/:userid/order/', function (req, res) {
// This works???
var u = user_model.findById(req.params.userid);
res.json(u.orders);
});
The following code throws the error "TypeError: Cannot read property 'orders' of undefined".
var mongoose = require('mongoose');
var router = express.Router();
var order_model = require('../models/order');
var user_model = require('../models/user');
router.get('/:userid/order/', function (req, res) {
// This throws an error.
user_model.findById(req.params.userid).then(function (err, user) {
if (err) {
res.send(err);
}
res.json(user.orders);
});
});
user.js
var mongoose = require('mongoose');
var ordersSchema = require('./order').schema;
var userSchema = new mongoose.Schema({
name: String,
email: String,
showroom: String,
orders: [ordersSchema]
});
module.exports = mongoose.model('User', userSchema);
order.js
var mongoose = require('mongoose');
var lineItemsSchema = require('./lineitem').schema;
var ordersSchema = new mongoose.Schema({
trackingNumber: Number,
lineItems: [lineItemsSchema]
});
module.exports = mongoose.model('Order', ordersSchema);
Any help / explanation of this behavior would be appreciated. Thanks!
The first parameter of the then callback is user, not err.
Either use a traditional callback:
user_model.findById(req.params.userid, function (err, user) {
if (err) {
res.send(err);
return; // Need to return here to not continue with non-error case
}
res.json(user.orders);
});
Or chain a call to catch on the promise to separately handle errors:
user_model.findById(req.params.userid).then(function (user) {
res.json(user.orders);
}).catch(function(err) {
res.send(err);
});
I usually query like this, it work perfect :
user_model.find(_id : req.params.userid)
.exec((err, user) => {
if(err){
//handle error
}
return res.status(200).json(user.orders);
})
});

"this" is not refering the the user object in mongoose model

I have the following code to save user object to database from express,
api.post('/signup', function (req, res) {
var user = new User();
user.name = req.body.name;
user.email = req.body.email;
user.setPassword(req.body.password);
user.save(function (err) {
err ? res.send(err) : res.json({ message: 'User Created!'})
})
})
and below here for the user schema,
var mongoose = require('mongoose');
var bcrypt = require('bcrypt');
var SALT_WORK_FACTOR = 10;
var Schema = mongoose.Schema;
var userSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
unique: true,
required: true
},
password: String,
})
userSchema.methods.setPassword = function (password) {
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return console.log(err);
bcrypt.hash(password, salt, function (err, hash) {
if (err) return console.log(err);
this.password = hash; // <= `this` will not be saved to mongoDB
})
})
}
module.exports = mongoose.model('User', userSchema);
When it performed the save function, it will show that the password was undefined and will save the object to mongodb without password value.
I've checked the this question as well and changed all my functions to not using arrow method but it still got the same error.
The same issue when i was using middleware hook that this reference was not referring to user object. Below here for my another approach,
userSchema.pre('save', (next) => {
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if(err) return next(err);
bcrypt.hash(this.password, salt, function (err, hash) {
if (err) return next(err);
this.password = hash; // <= `this` will not be saved to mongoDB
next();
})
})
})
Any idea for this value to be saved to database when perform save?
You should try below code, Hope it will work for you:
userSchema.methods.setPassword = function (password) {
var pswd=password;
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return console.log(err);
bcrypt.hash(pswd, salt, function (err, hash) {
if (err) return console.log(err);
pswd = hash;
console.log(pwsd); // your decripted password
})
})
}
I will elaborate more in the next days (I want to test a couple of things) but the error, I think, is because the meaning of this in JS is tricky.
Basically this is not a reference to the function's lexical scope, it is binding done in the context where the function is called.
So, as a fast solution while I elaborate more, I would recommend you to encrypt the password in:
user.setPassword(encrypt(req.body.password)); and simplify the mongoose's setPassword function
Hope that helps.
If you want this to refer to the userSchema context, you can use arrow functions for the bcrypt callbacks. Arrow functions will not create a new function scope, so it will preserve the original context.
userSchema.methods.setPassword = function (password) {
bcrypt.genSalt(SALT_WORK_FACTOR, (err, salt) => {
if (err) return console.log(err);
bcrypt.hash(password, salt, (err, hash) => {
if (err) return console.log(err);
this.password = hash; // <= `this` will be the user object
})
})
}
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions

bcrypt error with passport and mongoose

Bcrypt is throwing an Incorrect arguments error which I traced back to this function in user.js
userSchema.methods.comparePassword = (candidatePassword, callback) => {
bcrypt.compare(candidatePassword, this, (err, isMatch) => {
console.log('candidatePassword= ', candidatePassword, '& this= ', this);
if (err) { return callback(err); }
callback(null, isMatch);
});
};
/*
candidatePassword= bird
this= {}
this.password= undefined */
The user object is coming back as an empty object, and therefore this.password is undefined. I assume the this parameter in bcrypt.compare refers to the userSchema instance. The userSchema is declared in passport.js
const passport = require('passport');
const ExtractJwt = require('passport-jwt').ExtractJwt;
const JwtStrategy = require('passport-jwt').Strategy;
const LocalStrategy = require('passport-local').Strategy;
const User = require('../models/user');
const config = require('../config');
var localOptions = {
usernameField: 'email',
};
// Verifies user by checking if a password matches the specified email during signin
var localStrategy = new LocalStrategy(localOptions, function (email, password, done) {
User.findOne({ email:email.toLowerCase()}, function (err, user) {
console.log('/passport.js/localStrategy- user object: ', user)
if (err) { return done(err); }
if (!user) { return done(null, false); }
user.comparePassword(password, function (err, isMatch) {
console.log('/passport.js/localStrategy- password: ', password)
if (err) { return done(err); }
if (!isMatch) { return done(err, false); }
return done(null, user);
});
});
});
// ... jwt strategy ...
passport.use(localStrategy);
/*
user object: { _id: 58a1018dc3f89eb5955b8638,
email: 'bird#bird.com',
password: '$2a$10$lAJ9hoGKt9ggfk1TISfkOedxDIs/waLB5e4PccHAKt286XCKCY0/q',
__v: 0 } */
I'm not sure quite what the issue as it seems a user object is returned with an encrypted password field from mongodb, and user.comparepassword() is called...
I signed the user up with the same Schema object as well.
Any help / tips appreciated!
You are only setting up your model so that it pulls in the candidatePassword but never finds the stored password from the database. Since this is returning an empty object, either the email is not being matched or the password is not being compared to what is stored. Try simplifying the comparePassword function and adding 'sync' to the bcrypt.compare which removes the need for a callback.
In models:
userSchema.methods.comparePassword = (candidatePassword) => {
return bcrypt.compareSync(candidatePassword, this.password);
};

Categories

Resources