mongoose password gets hashed twice - node.js

I am new to Mongoose and Node JS, and have this problem with password being hashed again after a user is updated.
Here is the code for hashing:
UserSchema.pre('save', function (next) {
var user = this;
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
});
and this is for updating the user
router.post('/insert', function (req, res) {
User.findById(req.session.userId, function (err, user) {
if (err) { throw err; }
if (user) {
user.patients = req.body.patients
user.diagnosis_list = req.body.diagnosis_list
user.medicine_dose_list = req.body.medicine_dose_list
user.medicine_list = req.body.medicine_list
user.save(function (err) {
if (err) throw err;
res.send(user.toJSON());
})
}
})
})
I understand that presave has to be prevented to be called on updating the user, I just have no clue how to do that. Is there some other way I can update the user without having presave being called maybe?

if (!user.isModified('password')) {
return next();
}
bcrypt.hash(user.password, 10, function (err, hash) {
if (err) {
return next(err);
}
user.password = hash;
next();
})
http://mongoosejs.com/docs/api.html#document_Document-isModified

Related

Hashing password before update a user in mongoose

I create the user, hash his password and save on mongo. My problem begins when I try to update that user. For now, when I update the hash isn't generated, cause I really don't know how to do it.
The middleware to get the user that I'm talking about:
exports.userByID = function(req, res, next, id) {
User.findOne(
{
_id: id
},
function(err, user) {
if (err) {
return next(err);
} else {
req.user = user;
next();
}
}
);
};
The user controller, to update an user:
exports.update = async function(req, res, next) {
User.findByIdAndUpdate(req.user.id, req.body, function(err, user) {
if (err) {
return next(err);
} else {
res.json(user);
}
});
};
The pre 'save' on User's model:
UserSchema.pre("save", function(next) {
var user = this;
if (user.password) {
var md5 = crypto.createHash("md5");
user.password = md5.update(user.password).digest("hex");
console.log("Password após o save (hasheando):" + user.password);
}
next();
});
I'm using passport authentication ('local'). Already tried user.save() on the controller update:
user.save();
res.json(user);
But, without success.
This is may be because you are not storing the new_password in the mongo.
In update controller you have to do like this:
User.findByIdAndUpdate(req.user.id, req.body, function (err, user) {
if (err) {
return next(err);
} else {
user.password = req.body.new_password;
user.save(function (err, user) {
if (err) {
res.send("Error: ", err);
} else {
res.send("password updated successfully!");
}
})
}
});
Before saving the password just hash it and update it in DB. it will be something like below.
exports.update = async function(req, res, next) {
let { body} = req;
if(body['password']){
var md5 = crypto.createHash("md5");
body['password']= md5.update(body['password']).digest("hex");
}
let updateUser = await User.findByIdAndUpdate(req.user.id, body)
};

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.

How can I use or create an onupdate middleware using mongoose schema?

With mongoose we can use some middlewares when we manipulate data. For example, this is a famous use of the save middleware:
Schema.pre('save', function(next){
const user = this;
if (!user.isModified('password')) {
return next();
}
bcrypt.genSalt(10, (err, salt) => {
if (err) {
next(err);
}
bcrypt.hash(user.password, salt, null, (err, hash) => {
if (err) {
next(err);
}
user.password = hash;
next();
});
});
});
I would like to know how to do the same when you try to update some records. I read in several forums that can use: Schema.pre('update'), I've tried but it doesn't work for me. The idea is to re-encrypt the new password when the user wants to change it. I know than I could do this in the controller, but I would like to take away that responsibility and do it from the schema.
The result must be something like:
Schema.pre('onUpdate', function(next){
const user = this;
if (!user.isModified('password')) {
return next();
}
bcrypt.genSalt(10, (err, salt) => {
if (err) {
next(err);
}
bcrypt.hash(user.password, salt, null, (err, hash) => {
if (err) {
next(err);
}
user.password = hash;
next();
});
});
});
I know this doesn't actually answer the question but if your use-case is to update the password only, you can use the following:
let userDetails = await User.findOne({ userID: userIDFromRequest });
userDetails.password = request.newPasswordFromRequest;
await userDetails.save();
Without having to worry about the hashing.
Also, if you really need the update hook try the code below (Not tested):
UserSchema.pre('update', function(next) {
let update = this._update;
if (update.$set && update.$set.password) {
this.update({}, { password: generateHash(update.$set.password) });
}
next();
});
You can read more here: http://mongoosejs.com/docs/middleware.html#notes

Bcrypt Not Saving? - PassportJS

For some reason the code below does not save to the database? Can anyone tell me why this will not work to save a user's new password using PassportJS?
UserSchema.pre('save', function(next) {
var user = this;
var SALT_FACTOR = 5;
if (!user.isModified('password')) return next();
bcrypt.genSalt(SALT_FACTOR, function(err, salt) {
if (err) return next(err);
bcrypt.hash(user.password, salt, null, function(err, hash) {
if (err) return next(err);
user.password = hash;
next();
});
});
});

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