Reputation: 487
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
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
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
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
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);
}
})
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