问题
I followed this tutorial from scotch.io on how to build user authentication using node.js (great tutorial by the way). However, when verifyPassword(password)
is called to check user password, the value is always returned as false for some reason.
I'm using brcypt.js and sequelize.js for my express project.
custom methods for model User:
classMethods : {
setPassword : function(password) {
return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
}
},
instanceMethods: {
verifyPassword: function(password) {
return bcrypt.compare(password, this.password, function(err, result) {
if (err) throw err;
return result;
});
}
}
this is how i create a new user:
User.find({where: {email : email}})
.complete(function(err, user) {
// if there are any errors, return the error
if (err){
return done(err);
}
// check to see if theres already a user with that email
if (user) {
return done(null, false, req.flash('signupMessage', 'That email is already taken.'));
} else {
User
.create({
email: email,
password: User.setPassword(password)
})
.complete(function(err, newUser) {
if (err)
throw err;
return done(null, newUser)
})
}
});
verifying password:
User.find({where: { email : email }})
.complete(function(err, user) {
if (err)
return done(err);
if (!user){
return done(null, false, req.flash('loginMessage', 'No user found.'));
}
// if the user is found but the password is wrong
if (!user.verifyPassword(password)){
return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
}
// all is well, return successful user
return done(null, user);
});
I know its better to use async mode in bcrypt but it always gives me "password undefined" when I try something like this:
setPassword : function(password) {
return bcrypt.genSalt(10, function(err, salt) {
if (err) return err;
return bcrypt.hash(password, salt, function(err, hash) {
if (err) return err;
return hash;
});
});
}
what should I do instead?
回答1:
bcrypt.compare appears to be asynchronous, you should extend your compare function to take a callback.
You could do the same for setPassword but you'll have to do your create inside the callback. You're using it in a synchronous matter right now so it will have to be a synchronous function.
You could do something like:
classMethods : {
setPassword : function(password, callback) {
// Pseudo, i don't actually know the bcrypt api
return bcrypt.hash(password, bcrypt.genSaltSync(8), callback);
}
},
instanceMethods: {
verifyPassword: function(password, callback) {
bcrypt.compare(password, this.password, callback);
}
}
And then rewrite your interfacing code to something like:
Uset.setPassword(password, function (err, password) {
if (err) return done(err);
User.create({
email: email,
password: password
}).done(done);
});
user.verifyPassword(password, function (err, result) {
if (err || result) return done(null, false, req.flash('loginMessage', 'Oops! Wrong password.'));
return done(null, user);
});
Encrypting passwords on create is where hooks come in handy, instead of handling the encrypting yourself you could use a beforeCreate hook to encrypt the password before it gets saved.
User.beforeCreate(function (model, done) {
bcrypt.hash(model.password, bcrypt.genSaltSync(8), function (err, password) {
if (err) return done(err);
model.password = password;
done();
});
});
User.create({
email: email,
password: password // Unencrypted
});
The before create will then make sure the value is encrypted before it is inserted to the database.
来源:https://stackoverflow.com/questions/23092581/bcrypt-compare-always-returns-false-when-verifying-passwords