How do I change the code to run synchronously - node.js

I have comparePassword function runs inside checkLogin in synchronous way, but the debugging output shows the opposite, what is my mistake here?
comparePassword = (password, salt, passwordHash) => {
crypto.pbkdf2(password, salt, 10000, 64, "sha512", (err, derivedKey) => {
//console.log('dump comparePassword: ', err);
if (err) {
return {
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
};
} else {
console.log(
"dump dump === : ",
passwordHash === derivedKey.toString("hex") ? true : false
);
if (derivedKey.toString("hex") == passwordHash) {
console.log("return return return1: ", true);
return true;
}
console.log("return return return2: ", true);
return false;
}
});
};
// Check credentials of a seller
exports.checkLogin = async (username, password) => {
let result = 0;
try {
const record = await Seller.findOne({
raw: true,
attributes: { exclude: ["birth", "createdAt", "updatedAt"] },
where: { email: username }
});
if (record !== null) {
let passwordRes = await comparePassword(
password,
record.salt,
record.password
);
console.log("dump passwordRes: ", passwordRes);
if (typeof passwordRes === "object") {
return passwordRes;
}
if (passwordRes) {
delete record.password;
delete record.salt;
return { seller: record };
}
}
} catch (error) {
console.log("error while checking seller credential: ", error);
result = {
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
};
}
return result;
};
Output:
dump passwordRes: undefined
dump dump === : true
return return return1: true
Expectation:
dump dump === : true
return return return1: true
dump passwordRes: true

comparePassword should return an promise so await can wait till the promise is resolved or rejected
comparePassword = (password, salt, passwordHash) => {
return new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 10000, 64, "sha512", (err, derivedKey) => {
if (err) {
reject({
status: 500,
error: "Internal server error",
detail: "Server couldn't check your credential at the moment"
});
} else {
if (derivedKey.toString("hex") == passwordHash) {
resolve(true);
}
resolve(false);
}
});
});
};

I think you can not await a callback in comparePasswort without wrapping it in a promise.
Try something like
comparePassword = (password, salt, passwordHash) => new Promise((resolve, reject) => {
crypto.pbkdf2(password, salt, 10000, 64, 'sha512', (err, derivedKey) => {
// ...
resolve(true); // or reject(false)
})
})

Return a promise or simply make it async:
comparePassword = async (password, salt, passwordHash) =>{}

Related

aws-sdk SNS on NodeJs don't catches error

I try to implement a phone number verification by SMS with aws-sdk SNS and Mongoose,
But when I test it with a wrong phone number, like, some gibberish instead of real phone,
the error goes to the console, but then it won't get caught by catch and just crashes it all.
Here is my code
// requires, etc is above here
const verifyPhone = async (req, res) => {
const { phoneNumber } = req.body;
const { userId } = req.user;
let code = generateCode(6);
const session1 = await mongoose.startSession();
try {
session1.startTransaction();
const newCode = await PhoneCode.create(
[
{
userId,
code,
},
],
{ session: session1 }
);
sns.publish(
{
MessageAttributes: {
"AWS.SNS.SMS.SenderID": {
DataType: "String",
StringValue: "Testing",
},
"AWS.SNS.SMS.SMSType": {
DataType: "String",
StringValue: "Promotional",
},
},
Message: "Your code: " + code,
PhoneNumber: "65f76fguyg",
},
(err, result) => {
if (!err) {
session1.commitTransaction().then(() => {
console.log("transaction committed");
res.status(200).json({ message: "SMS sent", result });
});
} else {
console.log(err);
throw new Error(err);
}
}
);
} catch (e) {
await session1.abortTransaction();
res.status(500).json({ message: "Something went wrong!", e });
} finally {
session1.endSession();
}
};
module.exports = verifyPhone;
I suspect that the issue is in throwing the error from inside of sns.publish 's callback, but I don't understand, how else I could do that
Also, if I enter right phone number, the SMS gets delivered, I get the response, but then
mongoose crashes, saying
UnhandledPromiseRejectionWarning: MongoTransactionError: Cannot call commitTransaction after calling abortTransaction
I've just got to a solution. The solution for me was to promisify the sns.publish, after that all went like a charm.
const promisifySNS = (args) => {
return new Promise((resolve, reject) => {
sns.publish(args, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
Funny enough, if you use more generic promisify, for any function
const promisify = (fn, ...args) => {
return new Promise((resolve, reject) => {
fn(...args, (err, result) => {
if (err) {
return reject(err);
}
resolve(result);
});
});
};
await promisify(sns.publish, opts);
that wouldn't work for some reason.

Express async route logging undefined after await

I am writing an Express endpoint in Typescript to update a user's password in the database, but having some trouble with multiple queries/synchronous code.
My problem is right now I am trying to await the return of a function but it doesn't seem to be awaiting.
My route looks like this:
router.put("/:userId", validateUser, async (req: any, res: Response) => {
const result: any = await updatePassword(req.body.password, req.body.confirmPassword, req.params.userId);
console.log(result) //LOGS UNDEFINED????
if (result.status !== 200) res.status(result.status).send({ message: result.message, code: result.code });
res.send("Success!");
});
and that updatePassword function looks like this:
const updatePassword = async (password: string, confirmPassword: string, userId: string) => {
if (password !== confirmPassword) {
return { status: 409, message: Errors.mismatchingPasswordMessage, code: Errors.mismatchingPasswordCode };
}
const hashedPassword = bcrypt.hashSync(password, 10);
//update the password in the DB
pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId], (err: MysqlError, result: any) => {
if (err) {
console.log(err);
return { status: 500, message: err.message, code: err.code };
}
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
});
}
Why is this the route not correctly awaiting the result of updatePassword??
Thanks in advance for any help.
The problem is, you're mixing async/await and callback style in this part
pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId], (err: MysqlError, result: any) => {
if (err) {
console.log(err);
return { status: 500, message: err.message, code: err.code };
}
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
});
The function updatePassword jumps directly to the next line after this block, which is nothing. So it returns undefined.
You need to replace this block with async/await code. Something like :
try {
const result = await pool.query("UPDATE users SET password = ? WHERE userId = ?", [hashedPassword, userId]);
if (result.changedRows !== 1) {
return { status: 500, message: "Password could not be updated!" };
}
return { status: 200 };
}
catch (err){
console.log(err);
return { status: 500, message: err.message, code: err.code };
}

Best pratices to return from await

This is my signup route :
hashed = await bcrypt.hash(req.body.password, bcryptRounds).catch(() => {
res.status(500).send();
return (null);
});
if (hashed === null) {
return;
}
result = await database.collection("profiles").insertOne({ "username": req.body.username, "password": hashed }).catch(() => {
res.status(500).send();
return (null);
});
if (result === null) {
return;
}
cookie = await SetAuthentification(result.ops[0]._id).catch((err) => {
res.status(500).send();
return (null);
});
if (cookie === null) {
return;
}
SetAuthentificationCookie(cookie, res);
res.status(200).send();
I need to return (null) after every catch and check it to make sure that it didn't fail, but this takes a lot of place and my code is becoming less clear compare to what I had before (but didn't worked in case of error)
hashed = await bcrypt.hash(req.body.password, bcryptRounds).catch(() => {
res.status(500).send();
});
result = await database.collection("profiles").insertOne({ "username": req.body.username, "password": hashed }).catch(() => {
res.status(500).send();
});
cookie = await SetAuthentification(result.ops[0]._id).catch((err) => {
res.status(500).send();
});
SetAuthentificationCookie(cookie, res);
res.status(200).send();
Any idea on how to improve that part to make it clearer ?
Why don't you just wrap everything into try...catch?:
try {
const hashed = await bcrypt.hash(req.body.password, bcryptRounds);
const result = await database.collection("profiles").insertOne({
username: req.body.username,
password: hashed,
});
const cookie = await SetAuthentification(result.ops[0]._id);
SetAuthentificationCookie(cookie, res);
res.status(200).send();
} catch (err) {
res.status(500).send();
}
If any of the promises reject, the code execution jumps to catch block.

problem with koa, mongoose await not returning ctx.body

I'm using koa to reset a password, wanting to use .save in order to fire the schema.pre('save' ).
data was returning with findOneAndUpdate, but not when I use .save.
what's the magic combination to make this return the .save doc properly with the await/asyncs?
r.post("/public/auth/resetpass", async (ctx, next) => {
const values = ctx.request.body;
const query = {
email: values.email,
resetPasswordToken: values.resetPasswordToken,
resetPasswordExpires: {
$gt: new Date(new Date())
}
};
const update = {
password: values.password,
resetPasswordToken: null,
resetPasswordExpires: null
};
// let userFound = null;
await User.findOne(query,async function(err, user) {
if (err) {
console.log("*** err");
next(err);
} else {
if (_.isEmpty(user)) {
ctx.status = 200;
ctx.body = {
error: true,
message: "token is incorrect or time has expired for password reset"
};
} else {
user.password = values.password;
await user.save(function(err, doc) {
if (err) {
console.log('***err saving');
next(err);
} else {
//console.log fires, but ctx body doesn't return
console.log ('***saved, writing poco');
ctx.body = userToPoco(doc);
}
});
}
}
});
});
ultimately switched to a promise.
await user.save().then (doc =>{
ctx.body = doc;
});

Generate new username available, always getting Undefined

I have a function to generate an available username, but always return undefined. I tried in many ways, in this case is a recursive function, but I'm always getting the same result, can you help me? Thanks
This is the code:
function generateNewUsernameAvailable(userName, number){
console.log('FUNCTION generateNewUsernameAvailable '+userName+' with number '+number);
User.countDocuments({ 'userName' : userName+number }, (err, count) => {
if (err) {
return `Error: ${err}`;
}
if (count == 0) {
return userName+number;
}else{
generateNewUsernameAvailable(userName, number+1);
}});
}
module.exports.existsUserName = function(req,res){
let userName = req.body.userName;
console.log('POST existsUserName '+userName);
User.countDocuments({ 'userName' : userName }, (err, count) => {
if (err) {
return res.status(500).send({message: `Error: ${err}`});
}
// Available
if (count == 0){
return res.status(200).send();
} else {
// Generate and return a new username available
console.log(generateNewUsernameAvailable('ricardo', 1));
res.status(400).send({message: 'newUsernameAvailable (Example ricardo1)'});
}
})
}
FindOne is faster than countDocuments/estimatedDocumentCount in this case. Both are promises, I'm going to add a possible solution:
function generateNewUsernameAvailable(userName, number){
return User
.findOne({ 'userName' : userName+number })
.then(function(result){
if (result){
return generateNewUsernameAvailable(userName, number+1);
}else{
return userName+number;
}
})
.catch(function(err) {
console.log(`Error: ${err}`);
throw err;
});
}
module.exports.existsUserName = function(req,res){
let userName = req.body.userName;
console.log('POST existsUserName '+userName);
User.countDocuments({ 'userName' : userName }, (err, count) => {
if (err) {
return res.status(500).send({message: `Error: ${err}`});
}
if (count == 0){
return res.status(200).send();
} else {
generateNewUsernameAvailable(userName,1)
.then(function(results){
return res.status(400).send({message: results});
})
.catch(function(err){
return res.status(500).send({message: `Error: ${err}`});
});
}
})
}

Resources