I was following the tutorial from Dev.to. But I stucked on this: bcrypt's compare not working
The code:
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch){
return res.status(400).json({
msg: "incorrect password"
});
}
Is the string coming from user.password a hash?
bcrypt compares your raw string with a hash. Here's a simple working example that you can run in a node file.
const bcrypt = require('bcrypt');
const bcryptTest = async () => {
try {
const password = 'mypassword';
const userPass = await bcrypt.hash('mypassword', 5);
const isMatch = await bcrypt.compare(password, userPass);
console.log(isMatch) // returns true
} catch (e) {
console.log(e)
}
}
bcryptTest();
Related
I have an api for reseting password. This api checks if there is a user with the entered phone number and if there is any it checks whether the inputs like dob, nationality and idnumber are also correct then if they are it then generates a new password for the user. However am using many if statements . So my question is whether this is practical or i should change my syntax for performance sake of the app and general good practices for writing standard code.
I have attached my code below with bunch of if statements lol
const asyncHandler = require("express-async-handler");
const User = require("../../models/user")
const bcrypt = require("bcrypt");
const jwt = require("jsonwebtoken");
const generateStrongPassword = require('../../utils/generateStrongPassword');
const resetPassword = asyncHandler(async (req, res) => {
const { phonenumber, fullname, nationality, nationalidnumber, dateofbirth } = req.body;
const user = await User.findOne({ phonenumber: phonenumber });
if (user) {
if (fullname != user.fullname) {
throw new Error('incorrect fullname');
}
if (nationality != user.nationality) {
throw new Error('incorrect nationality');
}
if (nationalidnumber != user.nationalidnumber) {
throw new Error('incorrect national id number ');
}
if (dateofbirth != user.dateofbirth) {
throw new Error('incorrect date of birth');
}
// if all validations are passed then
// generate new password
const newrawpassword = await generateStrongPassword();
console.log(newrawpassword);
// harsh the password
const salt = await bcrypt.genSalt(10);
const newHashedPassword = await bcrypt.hash(newrawpassword, salt);
const resetPassword = await User.updateOne({ $set: { password: newHashedPassword } })
res.json('password successfully reseted')
} else {
throw new Error('No account has that phone number');
}
});
module.exports = resetPassword;
My code above runs without any errors but the new password isn't saved.
I've been following the bcrypt docs, a blog post and a video and think the three different sources have resulted in my missing something critical.
Any ideas why the new password isn't being saved?
module.exports.submitNewPassword = async (req, res) => {
const slidedHeaderToken = req.headers.referer.slice(-40);
const artist = await Artist.find({ resetPasswordToken: slidedHeaderToken, resetPasswordExpires: { $gt: Date.now() } });
if (!artist) {
console.log("Artist doesn't exist");
} else {
const hashedPassword = async (pw) => {
bcrypt.hash(req.body.password, 12)
}
hashedPassword()
artist.password = hashedPassword;
resetPasswordToken = null;
resetPasswordExpires = null;
console.log("Successfully resubmitted password");
res.redirect('login');
}
}
You should call await artist.save() function after you set some fields
artists is an array of all the artists matching the parameters in the call to .find(). If you just want to find one, use .findOne().
Then after you modify it, use .save() to save it back to the database.
module.exports.submitNewPassword = async (req, res) => {
const slidedHeaderToken = req.headers.referer.slice(-40);
const artist = await Artist.findOne({ resetPasswordToken: slidedHeaderToken, resetPasswordExpires: { $gt: Date.now() } });
if (!artist) {
console.log("Artist doesn't exist");
} else {
const hashedPassword = async (pw) => {
bcrypt.hash(req.body.password, 12)
}
hashedPassword()
artist.password = hashedPassword;
await artist.save();
resetPasswordToken = null;
resetPasswordExpires = null;
console.log("Successfully resubmitted password");
res.redirect('login');
}
}
See the Mongoose tutorial here.
When you make a change to the data using FindOne, you need to save it.
await yourVariable.save()
I have been struggling with this for hours and have tried a lot of different variations I have found around the web and also on stack overflow but I keep getting stuck on the same thing.
This is my registration code:
// REGISTER USER
app.post("/register", async (request, response) => {
const saltRounds = 10;
const emailAddress = request.body.emailAddress;
const password = await bcrypt.hash(request.body.password, saltRounds);
console.log(password)
// CHECK IF A USER EXISTS
const sqlSearch = "SELECT * FROM users WHERE emailAddress = ?"
const search_query = mysql.format(sqlSearch, [emailAddress])
// INSERT NEW USER
const sqlInsert = "INSERT INTO users (emailAddress, password) VALUES (?,?)"
const insert_query = mysql.format(sqlInsert, [emailAddress, password])
await usersDB.query(search_query, async (err, result) => {
if (err) throw (err)
if (result.length != 0) {
console.log("------> User already exists")
response.send("exists")
} else {
await usersDB.query(insert_query, (err, result) => {
if (err) throw (err)
response.send("created")
})
}
})
})
This is my login code:
// LOGIN (AUTHENTICATE USER)
app.post("/login", async (request, response) => {
const emailAddress = request.body.emailAddress
const password = request.body.password
const sqlSearch = "SELECT * FROM users WHERE emailAddress = ?"
const search_query = mysql.format(sqlSearch, [emailAddress])
await usersDB.query(search_query, async (err, result) => {
if (err) throw (err)
if (result.length == 0) {
console.log("--------> User does not exist")
response.sendStatus(404)
} else {
// Get the hashed password from result
const hashedPassword = result[0].Password
await bcrypt.compare(password, hashedPassword, function(err, result) {
if (result) {
console.log("---------> Login Successful")
response.send(`${emailAddress} is logged in!`)
} else {
console.log("---------> Password Incorrect")
console.log(password)
console.log(hashedPassword)
response.send("Password incorrect!")
}
});
}
})
})
I don't really understand what is going wrong in the compare considering the hashes are the same, I also tried pulling the salt rounds out and declaring them as a variable as you can see, this was recommended on another answer. I have changed the compare await in several different ways but they all give the same result.
I did also check the typeof on each var and they are all strings as they need to be.
My output:
The first hash you see is what is going into the database, the password being "test" and the second hash is from the compare statement along with the plaintext being shown.
$2b$10$wXGSrneIiovWHG7wk6a0BOIXwhzelTlCcxeoLsVJ8Au4iiOcoBBhe
---------> Password Incorrect
test
$2b$10$wXGSrneIiovWHG7wk6a0BOIXwhzelTlCcxeoLsVJ8Au4iiOcoBBhe
Any help would be greatly appreciated.
Note: The password column in my DB is a VARCHAR(255)
You can make a 2 seperate function for achieve the bcrypt functions. Here is the helper file which holds the bcrypt functions
const logger = require('./logger');
const bcrypt = require('bcrypt');
const encryptUtil = {};
// It make a hash password
encryptUtil.oneWayEncrypt = async (text) => {
try {
const salt = await bcrypt.genSalt(parseInt(process.env.SALT_ROUND, 10));
const encoded = await bcrypt.hash(text, salt);
return { encoded, salt };
} catch (err) {
logger.error('[ERROR] From oneWayEncrypt in encryptUtils', err);
throw err;
}
};
// It will validate plain text with the hashed one
encryptUtil.validateBcryptHash = async (text, hash) => {
try {
const isExactMatch = await bcrypt.compare(text, hash);
return isExactMatch;
} catch (err) {
logger.error('[ERROR] From validateBcryptHash in encryptUtils', err);
throw err;
}
};
module.exports = encryptUtil;
Here is the usecase of that function in signup and login
const encryptUtil = require('../../../helper/encryptUtil');
const logger = require('../../../helper/logger');
const jwt = require('../../../helper/jwt');
const userUtils = {};
userUtils.signUp = async (obj) => {
try {
const { name, password } = obj;
const email = obj.email.toLowerCase();
const condition = { email };
const querying = {
attributes: ['id', 'name', 'email''],
where: { email },
};
const isEmailExist = await Model.user.findOne(querying);
if (isEmailExist) {
const errorObj = { code: 400, error: l10n.t('ERR_EMAIL_ALREADY_EXIST') };
throw errorObj;
}
const { encoded: encPassword } = await encryptUtil.oneWayEncrypt(password);
const insertObj = {
name,
email,
password: encPassword,
};
const result = await Model.user.create(insertObj);
const userId = result.id;
const token = jwt.getAuthToken({ userId });
return { token, msg: l10n.t('MSG_SIGNUP_SUCCESS'), user: { name, email, userId } };
} catch (error) {
logger.error('[ERROR] From signUp in userUtils', error);
throw error;
}
};
userUtils.login = async (obj) => {
try {
const { password } = obj;
const email = obj.email.toLowerCase();
const querying = {
attributes: ['id', 'name', 'email', 'password'],
where: { email },
};
const user = await Model.user.findOne(querying);
if (!user) {
const errorObj = { code: 400, error: l10n.t('ERR_CREDENTIAL_NOT_MATCHED') };
throw errorObj;
}
// Here it validates the simple text with hashed text which store in a dbatabase
const isExactMatch = await encryptUtil.validateBcryptHash(password, user.password);
if (!isExactMatch) {
const errorObj = { code: 400, error: l10n.t('ERR_CREDENTIAL_NOT_MATCHED') };
throw errorObj;
}
const token = jwt.getAuthToken({ userId: user.id });
const result = {
token,
user: {
userId: user.id,
name: user.name,
email: user.email,
};
return result;
} catch (error) {
logger.error('[ERROR] From login in userUtils', error);
throw error;
}
};
module.exports = userUtils;
guys i create a function to limit the user`s upload images everything works fine but the problem that
i got an Error on the console (Cannot set headers after they are sent to the client)
and when i copy the whole code of the function note(! not the function itself ) . on the controllers file i didnt get the error however ( its the same code and same everything
but the code is too long so it looks bad
please check the image before looking into the code
const Joi = require('joi');
const appError = require('./appError')
module.exports.validateHadith = (req,res,next)=>{
const hadithSchema = Joi.object({
Hadith: Joi.object({
narrator: Joi.string().required(),
description:Joi.string().required().min(15),
hadith:Joi.string().required().min(15),
}).required()
})
const {error} = hadithSchema.validate(req.body);
if(error){
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else{
next()
}
};
module.exports.reviewValidation = (req,res,next) => {
const reviewJoiSchema = Joi.object({
review: Joi.object({
comment: Joi.string().required(),
rating: Joi.number().required(),
}).required()
})
const {error} = reviewJoiSchema.validate(req.body);
if(error) {
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else {
next()
}
}
module.exports.imageValidation = (req,res,next) => {
const imageSchema = Joi.array().min(1).max(6).required()
const {error} = imageSchema.validate(req.files)
if(error) {
const msg = error.details.map(el=>el.message).join(',')
throw new appError(msg,400)
} else{
next()
}
}
module.exports.imageEditValidation = async (updatedHadith,images,req,res,id) => {
const length = updatedHadith.images.length
const max = 6
if(images.length+length <= max) {
updatedHadith.images.push(...images)
await updatedHadith.save()
console.log(updatedHadith)
} else{
req.flash('error','Sorry you can`t have more than 6 images')
return res.redirect(`/hadith/${id}`)
}
}
You need to add await
await imageEditValidation(updateHadith, images,req, res, id)
Currently, it's not awaiting, so it's jumping to req.flash("success", "successfully editing the Hadith") without waiting for it to be returned.
You also need to add a return after `console.log(updatedHadith)
Also need to remove req.flash and res.redirect('/hadith/${id}') statement, and replace it with a return
the validator code will be
module.exports.imageEditValidation = async (updatedHadith,images,req,) => {
const length = updatedHadith.images.length
const max = 6
if(images.length+length <= max) {
updatedHadith.images.push(...images)
await updatedHadith.save()
console.log(updatedHadith)
return true
} else{
req.flash('error','Sorry you can`t have more than 6 images')
return false
}
}
and controller code will be
module.exports.postEditForm =async (req, res, next) => {
const {id} = req.params;
const updatedHadith = await HadithModel.findByIdAndUpdate(
id,
{...req.body.Hadith},
{ runValidators: true }
);
const images = req.files.map(f => ({url : f.path ,filename: f.filename}))
if(await imageEditValidation (updatedHadith,images,req)){
req.flash("success", " The Hadith is successfully Edited :)");
}
res.redirect(`/hadith/${id}`);
}
Ok, I'm getting an undefined value from a function, I don't know why, I'm trying to get the value of a password hash for insert in the database, but the const that have the function has the value "undefined", so what I should change in my code?
async postCompletedetails(req, res) {
const company = req.params.company;
const name = req.params.name;
const password = req.params.password;
const hashPass = await bcrypt.hash(password, saltRounds, function(err, hash) {
if (err) {
throw err
} else {
console.log(hash)
}
})
if (
company !== undefined &&
name !== undefined &&
password !== undefined
) {
const {
token
} = req.headers;
const decoded = jwt.verify(token, process.env.JWT_ACCOUNT_ACTIVATION);
const id = decoded.id;
const update = await pool.query(
`UPDATE user SET Name_user= '${name}', password= '${hashPass}' WHERE ID_user = ${id}`
);
const incompany = await pool.query(
`INSERT INTO company (Name_company) VALUES ('${company}') `
);
const inrelcompany = await pool.query(
`INSERT INTO rel_company_user (ID_company, ID_user) VALUES (LAST_INSERT_ID(), ${id})`
);
return res.json({
code: 200,
message: "todo bien... todo correcto y yo que me alegro",
hashPass,
password
});
} else {
return res.json({
code: 400,
message: "Bro hiciste algo mal",
});
}
}
When you call bcrypt.hash() and pass in a callback function, no Promise is returned. You can leave off that callback and then your await will work as you expect.
Basically, as is common with a lot of APIs, you can choose between the "old school" callback function approach or the more modern Promise/async model. One or the other, but not both at the same time.