Where is the result of async function in node app? - javascript

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

Related

NodeJS: function not returning res.json

So I am making a post api for registration/signup. If a user is successfully been registered, an access token will be provided to the user for saving it in frontend.
Everything works, the username, password is saving in database along with the token. But the access token is not returning. I have used mongoDB as my database and used mongoose. Here what I have done so far:
Edited code
const UserModel = require("../models/userModel");
var bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const registration = async (req, res) => {
try {
const { email, password } = req.body;
if (!(email && password)) {
res.status(400).send("All input is required");
}
const existingEmail = await UserModel.find({ email: email });
if (existingEmail.length === 0) {
const userToken = jwt.sign({ email: email }, process.env.SECRET, {
expiresIn: "90d",
});
let hashedPassword = await bcrypt.hash(password, 8);
const user = await UserModel.create({
email,
password: hashedPassword,
token: userToken,
});
await userRegistration.save(function (err, result) {
if (err) {
console.error(err);
} else {
console.log(result);
}
});
res.json(userToken);
} else {
res.json("email has already been registered");
}
} catch (err) {
res.json(err);
}
};
module.exports = registration;
if I test the api in thunder client on vscode, it is returning {}, an empty object. Please tell me what I have done wrong?
const existingEmail = await UserModel.find({ email }); This line of yours will provide you the array of all the users because email property has nothing, it will be just like .find({})
If you are checking if the email inserted by user is already in your database or not, I suggest you do it like this: const existingEmail = await UserModel.find({ email : email});
This will return the document with email property's value equal to the email you received in req.body i.e. email : xyz#gmail.com
And In this line const userToken = jwt.sign({ email }, process.env.SECRET, {expiresIn: "90d",});
You are again making same mistake. The object you pass in payload, has email property, but no value/email is assigned to that property.
It's just like email : undefined
Here, do it like this jwt.sign({email : email}, process.env.SECRET, {expiresIn: '90d')})
So, I made a simple mistake in the code. I was trying to save userRegistration which was not defined. That's why the bug was occurring. I should be more careful about this.
Apart from what has been mentioned in the other answers, to me it looks like you are not giving the token to res.json anywhere.
Your function is returning the token, but I dont think its going anywhere. You need to pass the token to res.json, not return from the function.
You are using await as well as .then() which looks wrong to me. You have to use just one of them.
Update:
jwt.sign returns a string so userToken contains string. You are giving that string to res.json which is expecting a json. You need to pass an object to it.
Kindly try the below mentioned code.
const UserModel = require("../models/userModel");
var bcrypt = require("bcryptjs");
const jwt = require("jsonwebtoken");
const registration = async (req, res) => {
try {
const { email, password } = req.body;
if (!(email && password)) {
res.status(400).send("All input is required");
}
const existingEmail = await UserModel.find({ email });
if (existingEmail.length === 0) {
const userToken = jwt.sign({ email }, process.env.SECRET, {
expiresIn: "90d",
});
let hashedPassword = await bcrypt.hash(password, 8);
const user = await UserModel.create({
email,
password: hashedPassword,
token: userToken,
});
const userRegistrationResponse = await userRegistration.save();
const responseObj = {
...userRegistrationResponse,
accesstoken: `${userToken}`
};
res.json(responseObj);
} else {
res.json("email has already been registered");
}
} catch (err) {
res.json(err);
}
};
module.exports = registration;

`SyntaxError: await is only valid in async function` while trying to register a user in node rest api

I'm trying to create a register route in my NodeJS rest api, but whenever I try to send the data to the db, it's showing me some error like SyntaxError: await is only valid in async function.
I know what's the meaning of the error but, don't know why this error is coming here.
Here's my register route code:
router.post("/register", async (req, res) => {
const firstName = req.body.firstName;
const lastName = req.body.lastName;
const email = req.body.email;
const phone = req.body.phone;
const gender = req.body.gender;
const country = req.body.country;
const password = req.body.passwordHash;
bcrypt.hash(password, 10, (err, hash) => {
if (err) {
console.log("error: " + err);
} else {
const newUser = new User({
firstName,
lastName,
email,
phone,
gender,
country,
passwordHash: hash,
});
try {
const savedUser = await newUser.save()
res.send(savedUser)
} catch (err) {
res.status(400).send(err)
}
}
});
});
Can someone please help me?
await is inside the (err, hash) => function. So you need async (err, hash) =>, not async (req, res) =>.

Trying to make a PUT request in node.js

I am trying to make a PUT request with node.js using Javascript. Basically, what I am trying to do is make it so that an authenticated user is allowed to update a phone number and password. Normally I would have just used req.body in order to have the body be used to make an update request, however the whole body has a username, password and phoneNumber. I am only needing to update the password and phoneNumber. I have a restrict function that is restricting this request except for a logged in registered user, and I also have a model function for my update which is:
function updateUser(changes, id) {
return db("users")
.update(changes)
.where({id})
}
I also am trying to make sure that the password the user decided to update to (or the password they currently have) is hashed. I am using bcryptjs to hash the password. I have a two post request that both encrypts the password, (which is my register function) and one that compares the encryption (my login function). I will include those both just in case you need any background information:
router.post("/register", async (req, res, next) => {
try {
const {username, password, phoneNumber} = req.body
const user = await Users.findBy({username}).first()
if(user) {
return res.status(409).json({
message: "Username is already in use",
})
}
const newUser = await Users.create({
username,
password: await bcrypt.hash(password, 14),
phoneNumber,
})
res.status(201).json(newUser)
} catch (err) {
next(err)
}
})
router.post("/login", async(req, res, next) => {
try {
const {username, password} = req.body
const user = await Users.findBy({username}).first()
if(!user) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const passwordValid = await bcrypt.compare(password, user.password)
if(!passwordValid) {
return res.status(401).json({message: "Invalid Username or Password",})
}
const token = jwt.sign({
userId: user.id,
}, process.env.JWT_SECRET)
res.cookie("token", token)
res.json({
message: `Welcome to your plant page ${user.username}!`
})
} catch (err) {
next(err)
}
});
When I was trying to start my PUT request, I had started off writing const {phoneNumber, password} = req.body but I am needing to use both phoneNumber and password in the function. Here is an example of what I was starting my code with:
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
} catch(err) {
next(err)
}
})
I got it figured out after finding some help from someone in my class. I was on the right track with const {phoneNumber, password} = req.body. The rest is this (or this is all of the code):
router.put("/:id/updateaccount", restrict(), async(req, res, next) => {
try {
const {phoneNumber, password} = req.body
const userUpdate = await Users.updateUser({
phoneNumber, password: await bcrypt.hash(password, 14)
}, req.params.id)
res.status(200).json({
userUpdate:userUpdate, message: "You have successfully updated your information",
})
} catch(err) {
next(err)
}
})
I again used bcrypt to encrypt the newly updated password

Hashing password on hapi.js is not working

in my database, password are stored using hashing. But when i try to send plain password to compare with stored password on db it's not working. I'm using bcrypt npm to hash password. I'm using hapi.js server.
//user authentication
const validate = async (req, username, password, reply) => {
const user = await usermodel.findOne({username, password}).exec();
const match = await bcrypt.compare(password, user.password);
if(match) {
let isValid = username === user.username;
return isValid;
}
return {
isValid: isValid,
};
};
const init = async () => {
try {
await server.register(auth);
server.auth.strategy('simple', 'basic', {validate});
server.auth.default('simple');
await server.start();
console.log('Server running at:', server.info.uri);
} catch (err) {
console.log(err);
}
};
init();
but unfortunately every time when i give password, i get this error :
Debug: internal, implementation, error
TypeError: Cannot read property 'password' of null
Debug: internal, implementation, error
TypeError: Cannot read property 'username' of null
Hash the password first, then run your query:
const validate = async (req, username, password, reply) => {
const pwHash = await bcrypt.hash(password);
const user = await usermodel.findOne({ username, password: pwHash }).exec();
return user !== null;
};
No further checks are necessary. The findOne() command will go through all users and return the first match for both the username and password field, or null if there is no match.
Which means if the line returns a user, then username === user.username and pwHash === user.password implicitly.
The final line will return true if a match was found in the database, or false if no match was found.
usermodel.findOne({username, password})
That won't match any user, as the password you are using for searching is the unencrypted one, while the ones in the database are encrypted. Instead only search for the username, and exit early if that wasn't found:
const user = await usermodel.findOne({ username }).exec();
if(!user) return { isValid: false };

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

Categories

Resources