how to change username password using passport local mongoose - node.js

i'm trying to change the password of username using local mongoose
i tried to use setPassword function but it does not seem to work
router.put('/admin/users/:username', function(req,res){
User.findByUsername.then(function(sanitizedUser){
if (sanitizedUser){
sanitizedUser.setPassword(req.body.password, function(){
sanitizedUser.save();
res.redirect("back");
});
} else {
res.redirect("back");
}
},function(err){
console.error(err);
})
});
is there any other solution other than setpassword
what exactly i did wrong?

I have posted what I tend to do to reset password (part of it includes hashing it but the rest is the same). Except I am using async/await
const { password } = req.body;
let restP = new User();
try {
const newP = await restP.generateHash(password.password);
const resetP = await User.findByIdAndUpdate(
req.params.id,
{ $set: { passwordHash: newP } },
{
fields: { passwordHash: 0 },
new: true
}
)
// return image user object
res.send(resetP);
} catch (error) {
console.log(error);
return res.status(400).send(error);
}

Related

Mongoose How to await updating documents?

im trying to figure out why my code isnt awaiting the update method in mongoose.
First I have tried to find the user
//Find user
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password');
//Update the user property
let newuser;
if (remember) {
if (!user.rememberAcct) {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
}
} else if (user.rememberAcct) {
await user.updateOne({
rememberAcct: false,
});
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
document always updating asyncrounously, but await seems not applying to update. newuser returns the user with old property
Update:
with options {returnDocument: 'after'} or {new: true} is still returning old document.
Refers: https://mongoosejs.com/docs/api.html#model_Model-findOneAndUpdate
What Im missing here?
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
}, {returnDocument: 'after'});
In your snippet, there are 3 mistakes
1st
const user = await User.findOne({
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
}).select('+password') // <-- missing `rememberAcct` to select, without it `user.rememberAcct` will always be `undefined`
2nd
await user.updateOne({ // <-- this should be `User` not `user`
rememberAcct: false, // <-- this filter will select a random user & update query is missing
})
3rd :- Not using user.save() to update user.
here is final solution.
const user = await User.findOne(
{
username,
active: { $ne: false },
accessBanExpires: { $lt: Date.now() },
},
{ password: 1, rememberAcct: 1 }
)
if (!user) throw new Error('user not found')
console.log('user.rememberAcct (before)', user.rememberAcct)
if (remember) {
if (!user.rememberAcct) {
user.rememberAcct = true
await user.save()
}
} else if (user.rememberAcct) {
user.rememberAcct = false
await user.save()
}
// NOTE: above `if/else` statements can be boiled down into single `if/else` statement, no need to check more than once.
// I leave it you figure it out.
console.log('user.rememberAcct (after)', user.rememberAcct)
Your should either use findByIdAndUpdate, or pass a proper query:
// Either:
await User.findByIdAndUpdate(
user._id,
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
// Or:
await User.findOneAndUpdate(
{
_id : user._id,
},
{
rememberAcct: true
},
{
returnDocument: 'after'
}
);
I believe you need to place them into a try catches, let me know if the below works
this ads some error handling as well so you know why it may have failed.
async function yourfunctionorrequest() {
let password
try {
const user = await User.findOne({
username,
active: {
$ne: false
},
accessBanExpires: {
$lt: Date.now()
},
}, function (err, docs) {
// set the specific item you were after to the variable
password = docs.password
})
} catch (e) {
console.log(e)
}
let newuser;
if (remember) {
if (!user.rememberAcct) {
try {
newuser = await User.findOneAndUpdate(user._id, {
rememberAcct: true,
});
console.log('check: ', user.rememberAcct);
} catch (e) {
console.log(e)
}
} else if (user.rememberAcct) {
try {
await user.updateOne({
rememberAcct: false,
});
} catch (e) {
console.log(e)
}
}
console.log(user.rememberAcct, newuser); //Here document always updating asyncrounously, but await seems not applying to update. newUser returns the user with old property
console.log("error message", e)
}
}

got `{}` when set a field in mongodb to `undefined` using mongoose

I'm facing a weird problem
I'm using mongoose 5.12.8, I try to delete a user's image with this route
router.delete('/users/me/avatar', auth, async(req, res) => {
try {
req.user.avatarObj = undefined
await req.user.save()
res.send({ message: "Deleted user's avatar" })
} catch (error) {
res.status(500).send(error.message)
}
});
but when I use get route, i got req.user.avatarObj is still an empty object {}
//get user avatar
router.get('/users/:id/avatar', async(req, res) => {
try {
const user = await User.findById(req.params.id);
console.log(user.avatarObj) // print {} ?????????
console.log(user) //user has no avatarObj field
if (user && user.avatarObj) {
res.set('Content-Type', user.avatarObj.contentType);
res.send(user.avatarObj.data);
} else {
throw new Error('Not found user or image');
}
} catch (err) {
res.status(404).send({ error: err.message });
}
});
this is my userSchema
avatarObj: {
data: Buffer,
contentType: String
}
and some middleware
//hash plain text password before saving
userSchema.pre('save', async function(next) {
const user = this
if (user.isModified('password')) {
user.password = await bcrypt.hash(user.password, 10)
}
next()
});
//remove all tasks before remove user
userSchema.pre('remove', async function(next) {
await Task.deleteMany({ owner: this._id })
next()
});
My question is: Why user.avatarObj is still an empty object?
Thank you guys!

deleting key from object but still showing in response

I am experimenting with node authentication, I have managed to store a username and a hashed password into my database, but I want to return the json back without the hashed password.
I am deleting the password key before sending the JSON back but the password still shows in the returned result.
router.post("/signup", async (req, res, next) => {
const user = await User.exists({ username: req.body.username });
if (user) {
const error = new Error("Username already exists");
next(error);
} else {
const newUser = new User({
username: req.body.username,
password: req.body.password,
});
try {
const result = await newUser.save();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}
}
});
the User model has a pre hook to hash the password before save:
userSchema.pre("save", async function save(next) {
const user = this;
if (!user.isModified("password")) return next();
try {
user.password = await bcrypt.hash(user.password, 12);
return next();
} catch (err) {
return next(err);
}
});
Here is the solution thanks to Mahan for pointing it out.
result returns a Mongoose object so needs turning into a normal Javascript object first.
try {
let result = await newUser.save();
result = result.toObject();
delete result.password;
res.json(result);
} catch (err) {
res.json(err.errors);
}

problem with koa, mongoose await not returning ctx.body

I'm using koa to reset a password, wanting to use .save in order to fire the schema.pre('save' ).
data was returning with findOneAndUpdate, but not when I use .save.
what's the magic combination to make this return the .save doc properly with the await/asyncs?
r.post("/public/auth/resetpass", async (ctx, next) => {
const values = ctx.request.body;
const query = {
email: values.email,
resetPasswordToken: values.resetPasswordToken,
resetPasswordExpires: {
$gt: new Date(new Date())
}
};
const update = {
password: values.password,
resetPasswordToken: null,
resetPasswordExpires: null
};
// let userFound = null;
await User.findOne(query,async function(err, user) {
if (err) {
console.log("*** err");
next(err);
} else {
if (_.isEmpty(user)) {
ctx.status = 200;
ctx.body = {
error: true,
message: "token is incorrect or time has expired for password reset"
};
} else {
user.password = values.password;
await user.save(function(err, doc) {
if (err) {
console.log('***err saving');
next(err);
} else {
//console.log fires, but ctx body doesn't return
console.log ('***saved, writing poco');
ctx.body = userToPoco(doc);
}
});
}
}
});
});
ultimately switched to a promise.
await user.save().then (doc =>{
ctx.body = doc;
});

passport-local-mongoose set new password after checking for old password

I am using passportjs to handle auth of my app.
Once the user is logged in, I want to add the possibility to change the password from inside the app.
this is in my controller:
$http.post('/change-my-password',{oldPassword: $scope.user.oldpassword, newPassword: $scope.user.newpassword})
.then(function (res) {
if (res.data.success) {
// password has been changed.
} else {
// old password was wrong.
}
});
and this is my route handler in express nodejs in backend:
router.post('/change-my-password', function (req, res) {
if (!req.isAuthenticated()) {
return res.status(403).json({
success: false
});
}
UserSchema.findById(req.user._id, function(err, user){
if (err) return res.status(200).json({success: false});
user.validatePassword(req.body.oldPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
user.setPassword(req.body.newPassword, function() {
if (err || !user) {
return res.status(200).json(
{
success: false
}
)
}
user.save(function(err) {
if (err) return res.status(200).json({success: false});
req.login(user, function (err) {
if (err) return res.status(200).json({success: false});
return res.status(200).json({success: true});
});
});
});
});
});
});
here is my user schema model:
// user model
var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var passportLocalMongoose = require('passport-local-mongoose');
var bcrypt = require('bcrypt-nodejs');
var UserSchema = new Schema({
email: String,
password: String,
confirmStatus: Boolean,
token: String,
registerAt: Number
});
UserSchema.methods.validatePassword = function (password, callback) {
this.authenticate(password, callback);
};
UserSchema.plugin(passportLocalMongoose,
{
usernameField: 'email'
});
module.exports = mongoose.model('users', UserSchema);
the problem:
I find my user by Id in my mongoose schema UserSchema then I should check if the oldPassword is valid or not, and then I set the new password.
I successfully find the user and the set the new password. But the part that should check for comparison of the old password field, doesn't work at all. Whatever I enter in the old password field gets accepts as OK and that step is skipped. Whereas, it should throws an error saying that the old password is wrong.
I am also advised to use sanitizedUser in order not to show my salt and etc.
Question is: how can I first do the comparison check of the old password and then do the set new password step? If possible, how can I use the sanitize? And how can I check if the user is not entering the same password as the new password? or if possible, saying that the new password is very similar to the old one?
You can implement the it using the new feature added 3 days ago:
just use the changePassword method, and it handles it through this:
schema.methods.changePassword = function(oldPassword, newPassword, cb) {
if (!oldPassword || !newPassword) {
return cb(new errors.MissingPasswordError(options.errorMessages.MissingPasswordError));
}
var self = this;
this.authenticate(oldPassword, function(err, authenticated) {
if (err) { return cb(err); }
if (!authenticated) {
return cb(new errors.IncorrectPasswordError(options.errorMessages.IncorrectPasswordError));
}
self.setPassword(newPassword, function(setPasswordErr, user) {
if (setPasswordErr) { return cb(setPasswordErr); }
self.save(function(saveErr) {
if (saveErr) { return cb(saveErr); }
cb(null, user);
});
});
});
};
so in your code, you need to replace the validatePassword method by this:
user.changePassword(req.body.oldPassword,req.body.newPassword, function(err) {
if (err){
return res.status(200).json({
success: false
});
}
hope this works for you.

Resources