Chris
Chris

Reputation: 1883

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?

Upvotes: 5

Views: 3496

Answers (2)

Steve Holgado
Steve Holgado

Reputation: 12071

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();
        })
   })
});

Upvotes: 8

Chris
Chris

Reputation: 1883

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();
        })
    })
});

Upvotes: 4

Related Questions