Bcrypts compare and authenticate function authenticate any password - node.js

I'm using bcrypt to authenticate the users in my system. When i register an user, with the code:
UserSchema.pre("save", async function(next) {
var user = this;
if (user.isModified('password')) {
var salt = bcrypt.genSaltSync(10);
await bcrypt.hash(user.password, salt, null, function(err, hash) {
if(err){
console.log(err);
}else {
user.password = hash;
}
});
}
next();
});
Ok, the hash is created and save in mongo. My problema is, when i log in with this user, any password works. Here's is my compare function:
UserSchema.methods.authenticate = async function(password) {
var user = this;
var isAuthenticaded = await bcrypt.compare(password, user.password,
function(err, res){
if(res){
return true;
}else{
return false;
}
});
return isAuthenticaded;
};
I call the function 'authenticate' with passport:
if (!user.authenticate(password)) {
return done(null, false, {message: 'Senha inválida.'});
}
Someone could help?
[EDIT] - I think the problem is asynchronous calls. Modified to syncrhonous and it works! I will apreciate if someone discover where is the problem with asyncrhonous calls

About async implementation.
UserSchema.pre('save', async function save(next) {
if (!this.isModified('password')) return next();
try {
const salt = await bcrypt.genSalt(10);
this.password = await bcrypt.hash(this.password, salt);
return next();
} catch (err) {
return next(err);
}
});
UserSchema.methods.authenticate = async function(password) {
return bcrypt.compare(password, this.password);
};
And now, if user using our async authentication method, we have to await for result.
if (!await user.authenticate(password)) {
return done(null, false, {message: 'Senha inválida.'});
}
You can read more about pre.

Related

Hashing password not working after using await

I am trying to hash a password after updating it but I dont understand why it is just working after the await line. In the res.json I get the hashed password, but just there.
I am new to this so I appreciate any help or advice.
router.put('/:id', async (req, res) => {
let { mail, password } = req.body;
bcrypt.genSalt(saltRounds, function (err, salt) {
if (err) return next(err);
bcrypt.hash(password, salt, function (err, hash) {
if (err) return next(err);
password = hash;
});
});
const newUser = { mail, password };
await User.findByIdAndUpdate(req.params.id, newUser);
res.json({ mensaje: `Updated Password ${password}` });
});
As per my comment, you should look into async/await and callbacks more to understand the call order. As it's not running in the sequential fashion you think it is. But you can try the following.
router.put('/:id', async (req, res) => {
let { mail, password } = req.body;
try{
const salt = await bcrypt.genSalt(saltRounds);
const hashedPassword = await bcrypt.hash(password, salt);
const newUser = { mail, password };
await User.findByIdAndUpdate(req.params.id, newUser);
res.json({ mensaje: `Updated Password ${password}` });
} catch(error) {
res.json(error);
}
});

deleting key from object but still showing in response

I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}

Update password in database mongodb, mongoose

I have this route
router.patch("/me/update-password", authenticate, (req, res) =>{
let newPassword = _.pick(req.body, 'password');
const newP = newPassword.password;
User.findByCredentials(req.user.username, req.user.password).then((user) => {
user.password = newP;
user.save().then(() => {
res.send(user);
}).catch((e) => {
res.status(400).send(e);
});
});
});
/////////////////////////////////////////////////////////////////////////////
UserSchema.statics.findByCredentials = function (username, password) {
var User = this;
return User.findOne({username}).then((user) => {
if (!user) {
return Promise.reject();
}
return new Promise((resolve, reject) => {
// Use bcrypt.compare to compare password and user.password
bcrypt.compare(password, user.password, (err, res) => {
if (res) {
resolve(user);
} else {
reject();
}
});
});
});
};
//////////////////////////////////////////////////////////////////
UserSchema.pre('save', function (next) {
let user = this;
if (user.isModified('password')) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
next();
});
});
} else {
next();
}
});
I find the username and password of the authenticated user and then I want to send a new password which I save as newP.
I want to update the password hash and salt it and then save, so I tried with the method user.save() which hashes and salts the password. But when I send a patch request it wont`t finish.
Does anybody know why ?
I tried everything but I am stuck.

Bcrypt.compare() returning only false, Used Under Promises

I am using Bcrypt for hashing passwords and storing it in the database,
Bcrypt is returning false, when I retrieve the hashed password from the database, and compare with the typed user password.
I am using it under Promise, but bcrypt is returning false on the correct password
Here is the code:
userSchema.statics.findByCredentials = function(email, password) {
const User = this;
return User.findOne({email}).then((user) => {
if(!user) {
return Promise.reject();
}
// console.log(user.password);
return new Promise((resolve, reject) => {
bcrypt.compare(password, user.password, (err,res) => {
if(res) {
resolve(user);
}
else {
reject("Problem here");
}
console.log(res);
});
});
});
};
I am trying promise chain to the main file i.e server.js and return the details to the user, but it's not working.
Here is the route code of express:
app.post('/users/login', (req, res) => {
const body = _.pick(req.body, ['email', 'password']);
// res.send(body);
User.findByCredentials(body.email, body.password)
.then((user) => {
res.send(user);
}).catch((e) => res.send(e));
});
Thanks
Have you tried using the promise api for bcrypt?
It might look like this:
userSchema.statics.findByCredentials = function(email, password) {
const User = this;
return User.findOne({email}).then((user) => {
if(!user) {
return Promise.reject();
}
// console.log(user.password);
return bcrypt.compare(password, user.password)
.then(res => {
if (res) {
return user;
}
throw new Error('Problem here');
});
});
}
If not, you might be getting an error, so you could check the err argument to see if something came back from there. Other than that, as long as user.password is the hashed version of the original password, then it should be working.

BCrypt hash error

this is my code for hashing password and for compare existing password into existing module with a password that has been sended on body request:
//hash password of document that use this schema
bcrypt.hash(user.password, null, null, function (err, hashed) {
if (err) {
throw err;
} else {
user.password = hashed;
//next api
next();
}
})
});
userSchema.methods.comparePassword = function (password) {
//refer at userSchema
var user = this;
//return method of bcryot library that compare two string: original password and password hashed
return bcrypt.compareSync(password, user.password);
};
But compare this error message:
Uncaught, unspecified "error" event. (Not a valid BCrypt hash.)
Resolved !!! Into the database i have a lot of user's password not hashed and when i try to login, with bcrypt.compareSync (password, user.password); it expected that has been hashed password.
You're using null twice. I'd wager that you've wrapped this function inside the bcrypt.genSalt function(if you haven't , do so). You need to pass it the bcrypt salt where the first null is written.
Here's a full example:
userSchema.pre('save', function (next) {
const SALTROUNDS = 10; // or another integer in that ballpark
const user = this;
if(!user.isModified('password')) {
return next();
}
bcrypt.genSalt(SALTROUNDS, (err, salt) => {
if (err) { return next(err); }
bcrypt.hash(user.password, salt, null, (error, hash) => {
if (error) { return next(error); }
user.password = hash;
next();
});
});
});

Resources