Prevent updating user's password if the input was left empty? - node.js

I'm struggling to prevent updating user's password in database if the password input was left empty.
Here is the route responsible for updating user data:
router.put('/update', passport.authenticate('jwt', {session: false}), (req, res) => {
let user = req.user;
user.firstname = req.body.firstname;
user.lastname = req.body.lastname;
user.username = req.body.username;
user.email = req.body.email;
user.password = req.body.password || null;
User.updateUser(user, (err) => {
if (err) {
res.json({
success: false,
message: 'User details couldn\'t be updated.'
});
} else {
res.json({
success: true,
message: 'User updated'
});
}
});
});
And here is the User model method which generates a hash of a password and saves the new data in the database:
module.exports.updateUser = function (user, callback) {
if (user.password) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) throw err;
user.password = hash;
});
});
}
user.save(callback);
};
I check if the password value was given but I don't know how to keep the old encrypted password in the database if there is no new value given for the password. If user doesn't fill the password input, it is being saved as null, as expected though...
I hope there is an approach to achieve this, I just can't figure out at the moment as I'm a beginner.
Thank you in advance!

I guess that you are using Mongoose to communicate with the Database.
Change this Line of your code :
user.password = req.body.password || null;
with this :
if(req.body.password != null) {
user.password = req.body.password
}else{
/* find each user with a last name matching 'user.userame', selecting
/*the `password` field and returning the result in 'usr'
*/
User.find({'username' : user.username}, 'password', function (err, usr) {
if (err) return handleError(err);
user.password = usr.password;
})
}

Based on #Neil Lunn's suggestion about checking the documentation, I came up with a solution. I changed the updateUser method to this:
module.exports.updateUser = function (user, callback) {
if (user.password) {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) throw err;
user.password = hash;
user.save(callback);
});
});
} else {
User.findById(user.id).update({
username: user.username,
email: user.email,
firstname: user.firstname,
lastname: user.lastname
}, callback);
}
};
If the password is present, then update everything as is, if no password provided, then update only the needed fields except the password.
Maybe this is not the best solution, but it works for now.
Thank you!

Related

Hash password before mongodb update

So i've spent a while trying to solve this. Basically I have a user profile update page, when the user inputs the new credentials I want to update my mongo db. When I update it everything does through normally and my mongo server gets updated but when I log in, I use bcrypt to match the hashed password and unhashed password and this is what is giving me my error because the updated password isn't hashed.
Update mongo:
const { email, password, password2 } = req.body;
const _id = ObjectID(req.user);
User.updateOne(
{ _id },
{ $set: { email: email, password: password } },
(err) => {
if (err) {
throw err;
} else {
req.flash('success_msg', 'profile updated');
res.redirect('profile');
}
}
);
This is my try at hashing the password. It gives an error in the console(Error: Argument passed in must be a single String of 12 bytes or a string of 24 hex characters). I tried to solve this by making the object id .toString but it still gave an error:
const { email, password, password2 } = req.body;
const _id = ObjectID(req.user);
User.updateOne(
{ _id },
{ $set: { email: email, password: password } },
(err, user) => {
const updatedPassword = password;
if (err) {
throw err;
} else {
bcrypt.genSalt(10, (err, salt) => {
bcrypt.hash(updatedPassword.password, salt, (err, hash) => {
if (err) {
throw err;
}
updatedPassword.password = hash;
updatedPassword.save();
});
});
}
req.flash('success_msg', 'profile updated');
res.redirect('profile');
}
);
I'm a kinda new to express so sorry if this is messy. Also if you find a solution please explain it and if you need more code I'll post it.
You likely deserialize the user in a middleware not shown and so the following line should change from:
const _id = ObjectID(req.user);
to
const _id = new ObjectID(req.user._id);

How to fix "TypeError: cb is not a function" error for comparing passwords

I've been starting to add user authentication into my app and when adding the login route, i've been getting the error "TypeError: cb is not a function". I know it is coming from my login route as all my other routes work fine.
I have tried researching the issue and trying a few fixes that i've found but none have worked. So i'm starting to believe i've messed up somewhere and I can't find where.
Login Route:
router.post('/login', function (req, res) {
User.findOne({ username: req.body.username }, function (err, user) {
if (err || user == null) {
console.log(err);
res.redirect('/login');
}
if (!user.comparePassword(req.body.password)) {
req.flash('invalidDetails', 'Wrong username or password!');
res.redirect('/login');
} else {
req.session.userId = user._id;
req.flash('loggedIn', 'User ' + req.body.username + ' has been logged in!');
return res.redirect('/');
}
});
});
User Model:
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
bcrypt = require('bcrypt'),
SALT_WORK_FACTOR = 10;
var UserSchema = new mongoose.Schema({
email: {
type: String,
unique: true,
required: true,
trim: true
},
username: {
type: String,
unique: true,
required: true,
trim: true
},
password: {
type: String,
required: true
}
});
UserSchema.statics.authenticate = function (email, password, callback) {
User.findOne({ email: email }).exec(function (err, user) {
if (err) {
return callback(err);
} else if (!user) {
var err = new Error('User not found.');
err.status = 401;
return callback(err);
}
bcrypt.compare(password, hash, function (err, result) {
if (result === true) {
return callback(null, user);
} else {
return callback();
}
});
});
};
UserSchema.pre('save', function (next) {
var user = this;
// only hash the password if it has been modified (or is new)
if (!user.isModified('password')) return next();
// generate a salt
bcrypt.genSalt(SALT_WORK_FACTOR, function (err, salt) {
if (err) return next(err);
// hash the password along with our new salt
bcrypt.hash(user.password, salt, function (err, hash) {
if (err) return next(err);
// override the cleartext password with the hashed one
user.password = hash;
next();
});
});
});
UserSchema.methods.comparePassword = function comparePassword(candidatePassword, cb) {
bcrypt.compare(candidatePassword, this.password, function (err, isMatch) {
if (err) {
return cb(err);
}
cb(null, isMatch);
});
};
var User = mongoose.model('User', UserSchema);
module.exports = User;
I'm expecting it to compare the password with the password that has been entered into the login form with the password that is stored in the database and log in the user if the password is correct or redirect back to the login page with the invalidDetails flash message if the password is incorrect.
But what i'm actually getting is the "TypeError: cb is not a function" error when trying to login.
You are not passing a callback that's why.
If you want a promise based method you can write something like this
AuthSchema.methods.comparePassword = function(candidatePassword) {
const currentPassword = this.password;
return new Promise((resolve, reject) => {
bcrypt.compare(candidatePassword, currentPassword, function(err, isMatch) {
if (err) return reject(err);
resolve(isMatch);
});
})
};
And now you can call that with await

Node.js hash updated password

Well, i have simple user edit in node,express,mongodb. But i am unable to hash password to bcrypt. In registration works everything allright but that was tutorial...
Here is part of my routes/users.js
Everything is updated but password is not hashed and i dont know what to do.
router.post("/profile", function (req, res) {
let user = {};
user.firstname = req.body.firstname;
user.lastname = req.body.lastname;
user.email = req.body.email;
user.password = req.body.password;
user.password2 = req.body.password2;
req.checkBody("firstname", "Firstname is required").notEmpty();
req.checkBody("lastname", "Lastname is required").notEmpty();
req.checkBody("email", "Email is required").notEmpty();
req.checkBody("email", "Email is not valid").isEmail();
req.checkBody("password", "Password is required").notEmpty();
req
.checkBody("password", "Password must be longer then 8 chars bitch")
.len(8, 64);
req
.checkBody("password2", "Passwords do not match")
.equals(req.body.password);
var errors = req.validationErrors();
if (errors) {
res.render("profile", {
errors: errors
});
} else {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(user.password, salt, function(err, hash) {
user.password = hash;
});
});
let query = {_id:req.user.id}
User.update(query, user, function(err){
if(err){
console.log(err);
return;
} else {
res.redirect('/');
}
});
}});
Here is my hasing for registration in models/users.js that i was inspired by.
module.exports.createUser = function(newUser, callback) {
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(newUser.password, salt, function(err, hash) {
newUser.password = hash;
newUser.save(callback);
});
});
};
I will be thankfull for any help.
Well after hous its solved.
I just changed it to.
bcrypt.genSalt(10, function(err, salt) {
bcrypt.hash(user.password, salt, function(err, hash) {
user.password = hash;
let query = {_id:req.user.id}
User.update(query, user, function(err){
if(err){
console.log(err);
return;
} else {
res.redirect('/');
}
});
});
});
}});

How update the password in mongodb

app.get('/changePass', function(req, res) {
password = req.body.password;
password1 = req.body.password1;
xyz = req.body.xyz;
User.find({ email: xyz, password: password }, function(err, data) {
if (err) {
throw err;
}
if (data.length === 0) {
return res.json({ success: false, data: 'please enter valid
Old password' })
} else {
// User.update({ password: password }, {
// $set: {
// password: password1
// }
// });
var usr = new User();
usr.update({ "password":password }, password1, { upsert:true
});
//usr.password = password;
usr.save(function(err, data) {
if (err)
throw err;
return res.json({ success: true, data: 'password changed
successfully' });
})
}
})
how i update password in mongodb i am matching password to password and updating password to password1.this is giving me alert with please enter valid old password.
xyz is cookie that contains email.
You should use findOne on model to return userObject.
Then you can update the object that was found like regular javascript object and save it with .save function.
User.findOne({email: xyz, password: req.body.password1}, function(err, user){
if(err)return handleErr(err);
user.password = req.body.password2;
user.save(function(err){
if(err)return handleErr(err);
//user has been updated
});
});

Save password hash only when password is present

I have a form where users can create a room with an optional password field. I want to save the password only if the password field contains something ( is not empty). I have hash middleware that hash the password before saving it to mongodb. Even if the password field is empty it is saving a hash value. I tried to add a condition to check if there is a value only then to proceed with the hashing but this does not seem to work.
Here is my post :
exports.postCreateRooms = function(req, res, next) {
req.assert('workspace', 'Please enter a board name').notEmpty();
var errors = req.validationErrors();
var enableVideo;
if (errors) {
req.flash('errors', errors);
return res.redirect('/dashboard');
}
var url = uuid.v4();
var room = new Room({
roomUrl: url,
roomName: req.body.workspace,
owner:req.user._id,
ownerEmail:req.user.email,
dateCreated: Date(),
lastUpdated: Date(),
users: [req.user._id]
});
if (req.body.password != ''){
room.password = req.body.password;
}
room.save(function(err) {
if (err) {
return next(err);
}
res.redirect('/board='+room.roomUrl);
});
};
here is my hash middleware :
roomSchema.pre('save', function(next) {
var room = this;
if(room.password){
bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(room.password, salt, null, function(err, hash) {
if (err) {
return next(err);
}
room.password = hash;
next();
});
});
}
});
What happens when you have the check in place?
From what I can see, you need a next() call outside of your if block in the middleware, so it knows to proceed even if there isn't a password specified.
It would look like
roomSchema.pre('save', function(next) {
var room = this;
if(room.password){
return bcrypt.genSalt(10, function(err, salt) {
if (err) {
return next(err);
}
bcrypt.hash(room.password, salt, null, function(err, hash) {
if (err) {
return next(err);
}
room.password = hash;
next();
});
});
}
next();
});

Resources