Denn
Denn

Reputation: 487

Hashed password update with mongoose express

I have reviewed many discussions on this matter but none seems helpful to me.

I am using mongoose 5.5 to save user data as shown below:

My schema looks like this:

const mongoose = require("mongoose");
const Schema = mongoose.Schema;
const bcrypt = require("bcryptjs");

const userSchema = Schema({

  userName: {
    type: String
  },
  firstName: {
    type: String
  },
  surName: {
    type: String
  },
  password: {
    type: String,
    required: true
  }
});

userSchema.pre('save', async function(next){

try {
  if(!this.isModified('password')){
      return next();
  }
  const hashed = await bcrypt.hash(this.password, 10);
  this.password = hashed;

} catch (err) {
    return next(err);
  }
});

module.exports = user;

My registration code looks like this:

exports.register = async (req, res, next) => {

try {
    const user = await db.user.create(req.body);
    const {id, username} = user;
    res.status(201).json({user});
    
} catch (err) {
    if(err.code === 11000){
        err.message ='Sorry, details already taken';
    }
    next(err);
  }
};

Login code looks like this:

exports.login = async (req, res, next) => {

try {
    const user = await db.user.findOne({username: req.body.username});
    const valid = await user.comparePasswords(req.body.password);

    if(valid){

        const token = jwt.sign({id, username}, process.env.SECRET);
        res.json({id, username, token});
    }
    else{
        throw new Error();
    }        
    
} catch (err) {
    err.message = 'Invalid username/password';
    next(err);
  } 
};

Registration and login works well, my challenge is updating a password. I would like to compare current password with what user provides (like in login), if it is valid then update new password.

something like this:

exports.changepass = async (req, res, next) => {
    const user = await db.user.findOne({username: req.body.username});
    const valid = await user.comparePasswords(req.body.password);

    if(valid){

           " ?? update password and hash ?? "
    }
    else{
        throw new Error();
    }       

}

Upvotes: 3

Views: 2739

Answers (4)

Jeffrey Ram
Jeffrey Ram

Reputation: 1168

If you're using findOneAndUpdate() to update, try using the pre("findOneAndUpdate") middleware to modify the password similar to your pre("save"). The pre("findOneAndUpdate") middleware will be called every time you use Model.findOndAndUpate() to update your models.

You can do the same with updateOne() with pre("updateOne")

Sample:

// userSchema--------------------
...
userSchema.pre('save', async function (next) {
    try {
        if (!this.isModified('password')) {
            return next();
        }
        const hashed = await bcrypt.hash(this.password, 10);
        this.password = hashed;
    } catch (err) {
        return next(err);
    }
});

userSchema.pre('findOneAndUpdate', async function (next) {
    try {
        if (this._update.password) {
            const hashed = await bcrypt.hash(this._update.password, 10)
            this._update.password = hashed;
        }
        next();
    } catch (err) {
        return next(err);
    }
});

// changepass--------------------
...
if(valid){

    //" ?? update password and hash ?? "
    const result = await db.user.findOneAndUpdate(
        { username: req.body.username },
        { password: req.body.newPassword },
        { useFindAndModify: false }
    ); 
}

Upvotes: 6

mdmostafa
mdmostafa

Reputation: 852

I solved it using both middleware Schema.pre('save') for saving and Schema.pre('findOneAndUpdate') for updating the data.

In UserSchema,

// Hashing data before saving into database
UsersSchema.pre("save", async function (next) {
  try {
    // When password is hashed already, no need to be hashed
    if (!this.isModified("password")) {
      return next();
    }

    const hashedPassword = await bcrypt.hash(this.password, 10);
    this.password = hashedPassword;
  } catch (err) {
    return next(err);
  }
});

// Hashing data before updating into database
UsersSchema.pre("findOneAndUpdate", async function (next) {
  try {
    if (this._update.password) {
      const hashed = await bcrypt.hash(this._update.password, 10);
      this._update.password = hashed;
    }
    next();
  } catch (err) {
    return next(err);
  }
});

In userController,

    try {
    const user = await User.findOneAndUpdate(
      {
        _id
      },
      userInputValue,
      {
        // For adding new user to be updated
        new: true,
        // upsert: true,
        // Active validating rules from Schema model when updating
        runValidators: true,
        context: 'query'
      }
    );

    if (!user) return res.status(404).send("User Not Found");

    const userData = {
      user: {
        _id: user._id,
        name: user.name,
        email: user.email,
        role: user.role,
        createdAt: user.createdAt
      },
      success: {
        title: 'User Info Update',
        message: `You have updated the user ${user.name}'s info successfully.`
      }
    };

    return res.status(200).send(userData);

    // res.send(user);
  } catch (err) {
    res.status(500).send(err);
  }

Upvotes: 1

Tun Cham Roeun
Tun Cham Roeun

Reputation: 164

userSchema.pre('findOneAndUpdate', async function (next) {
  const user = this;
  if (user._update.$set.password) {
    user._update.$set.password = await bcrypt.hash(user._update.$set.password, 8);
  }
  next();
});

Upvotes: 1

ArpanDev7
ArpanDev7

Reputation: 21

Use this code:

schemaName.pre("updateOne", async function(next) {
   try {
      if(this._update.password) {
          this._update.password = await bycrpt.hash(this._update.password, 10);
      }
      next();
  } catch (err) {
      return next(err);
  }
})

Explanation:

When you're using findOneAndUpdate() or updateOne() to update, you need to use the pre("findOneAndUpdate") or pre("updateOne") middleware respectively to modify the password. pre("findOneAndUpdate") or pre("updateOne") middleware will be called every time you use Model.findOndAndUpate() or Model.updateOne() to update your model.

Upvotes: 1

Related Questions