Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Mongoose changes password every time I save with pre-save hook

I'm using a pre-save hook with bcrypt to encrypt passwords on the system. It works fine when creating or changing a password. The problem is that it seems to re-encrypt the password every time I change and save a different field, for example e-mail.

Probably easier to explain with code. Here's the model:

const UserSchema = new Schema({
    email: {
        type: String,
        required: true,
        lowercase: true,
        unique: true,
        trim: true
    },
    password: {
        type: String,
        required: true
    }
})

And the hook:

UserSchema.pre('save', function(next){
    const user = this;
    console.log(user);
    bcrypt.genSalt(10, 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();
        })
    })
});

And here's my code to update the e-mail address:

module.exports = function(req, res){
    User.findOne({ _id: req.body.user}, function(err, doc){
        if(err){
            console.log(err);
            return;
        }

        doc.email = req.body.data;
        doc.save(function(err, returnData){
            if (err){
                console.log(err);
                return;
            }
            res.send(returnData);
        })

    })
}

So when I call doc.save in the final example, it updates the e-mail address as intended but it also re-encrypts the password, meaning if the user then logs out, they can't log back in again.

Can anyone help with how to get around this?

like image 910
Chris Avatar asked Jul 28 '17 11:07

Chris


2 Answers

Try this:

UserSchema.pre('save', function(next){
    if (!this.isModified('password')) return next();

    const user = this;
    
    bcrypt.genSalt(10, 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();
        })
   })
});
like image 180
Steve Holgado Avatar answered Oct 11 '22 15:10

Steve Holgado


OK, I managed to figure it out - just needed a little bit of conditional logic in the pre-save hook:

UserSchema.pre('save', function(next){
    if(!this.isModified('password')){
        return next();
    } // Adding this statement solved the problem!!
    const user = this;
    bcrypt.genSalt(10, 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();
        })
    })
});
like image 34
Chris Avatar answered Oct 11 '22 13:10

Chris