Generate hashed password in findOneAndUpdate - node.js

Here is my query for findOneAndUpdate
const { email, password, id } = req.body
Artist.findOneAndUpdate({ _id: id }, { $set: req.body }).then((artist) => {
return res.json({
success: true,
message: "Invitation sent."
});
})
And here is my schema
var artistSchema = new mongoose.Schema({
name: { type: String, default: '' },
password: { type: String, default: '' }
})
artistSchema.pre('findOneAndUpdate', function (next) {
console.log('------------->>>>>> findOneAndUpdate: ');
console.log(this.password) // why undefined?
next();
});
I want to create a hashed password when user update details

const { email, password, id } = req.body;
Artist.findByIdAndUpdate(id, { $set: req.body }).then(artist => {
return res.json({
success: true,
message: "Invitation sent."
});
});
Example with bcrypt
var artistSchema = new mongoose.Schema({
name: { type: String, default: "" },
password: { type: String, default: "" }
});
artistSchema.pre("update", function(next) {
bcrypt.hash(this.password, 10, function(err, hash) {
if (err) return next(err);
this.password = hash;
next();
});
});

let crypto = require('crypto');
let mongoose = require('../mongoose'),
Schema = mongoose.Schema;
Then Schema
let schema = new Schema({
name: {
type: String,
default: ''
},
hashedPassword: {
type: String,
required: true
},
salt: {
type: String,
required: true
}
});
Then methods and virtuals
schema.methods.encryptPassword = function(password){
return crypto.createHmac('sha1', this.salt).update(password).digest('hex');
};
schema.virtual('password').set(function(password){
this._plainPassword = password;
this.salt = Math.random() + '';
this.hashedPassword = this.encryptPassword(password);
}).get(function(){ return this._plainPassword; });
You can check password like that
schema.methods.checkPassword = function(password){
return this.encryptPassword(password) === this.hashedPassword;
};
Export module
module.exports.Artist = mongoose.model('Artist', schema);
Then just save like before
const { email, password, id } = req.body;
Artist.findOneAndUpdate({ _id: id }, { $set: req.body }).then((artist) => {
return res.json({
success: true,
message: "Invitation sent."
});
});
But I sugest you also to use statics. For example:
schema.statics.updateUser = function (data){
// your code
}
And then you can use
Artist.updateUser(req.body).then((res) => {
// response
})

The answer: Writeconsole.log(JSON.stringify(this._update));
My solution for check blank password.
userSchema.pre('findOneAndUpdate', function() {
console.log(JSON.stringify(this._update));
if (this._update.password.length == 0) {
this._update = {
"fullname": this._update.fullname
};
}
else {
this._update = {
"fullname": this._update.fullname,
"password": bcrypt.hashSync(this._update.password, bcrypt.genSaltSync(8), null)
};
}
});

Related

VS Code Terminal Shows Error: data and salt arguments required when i hit the api

This is My User Model
const mongoose = require('mongoose')
const bcrypt = require('bcrypt');
const userSchema = mongoose.Schema({
contact: {
type: Number,
required: true,
},
email: {
type: String,
required: true,
unique: true
},
score: {
type: Number,
default:0,
},
password: {
type: String,
required: true,
},
role: {
type: String
},
blocked: {
type: Boolean, default: false
}
}, { timestamp: true }
)
userSchema.statics.hashPassword = function hashPassword(password){
return bcrypt.hashSync(password,10);
}
userSchema.methods.isValid = function(hashedpassword){
return bcrypt.compareSync(hashedpassword, this.password);
}
module.exports = mongoose.model('user',userSchema)
This is my Controller
const User = require('../models/user')
const Otp = require('../models/otp')
const jwt = require('jsonwebtoken')
const sendMail = require('../mail/mail')
const bcrypt = require('bcryptjs')
exports.getCheck = (req, res, next) => {
res.json({ msg: "All ok" })
}
exports.registerStudent = async (req, res) => {
// const x = await check(req,res,req.body.email);
const user = new User({
contact: req.body.phone,
email: req.body.email,
role: "student",
password: User.hashPassword(req.body.p1),
});
User.find({ email: req.body.email }, (err, users) => {
if (err) {
console.log("err in finding email ");
res.json({ msg: "some baler error!" });
}
if (users.length != 0) {
console.log("already user with this email");
res.json({ msg: "already user exist with this email!" });
}
else {
user.save((error, registeredUser) => {
if (error) {
console.log(error);
res.json({ msg: "some error!" });
}
else {
let payload = { subject: registeredUser._id }
let token = jwt.sign(payload, 'secretkey')
res.status(200).json({ token: token })
}
})
}
})
}
PLeasee HELP me out i'm getting confused
IGNORE
I ran into the same problem. 1. I saved my ATLAS_URI ID to a file called .env 2. My .env file was in the wrong directory, that's how the problem cause 3. Solution: I used "ls -a" command to make sure my .env file is in the same location as my server
IGNORE
PLeasee HELP me out i'm getting confused

Mongoose Schema.method() is not working, and showing an error message

I am taking password input from the user and encrypting the password using crypto, then saving into the database. This is my code, here I am storing the encrypted password into the encry_password property that comes from the userSchema. But, this is giving me error that "this.securePassword" is not a function.
const mongoose = require("mongoose");
const crypto = require("crypto");
const { v1: uuidv1 } = require("uuid");
const userSchema = new mongoose.Schema({
name: {
type: String,
required: true,
maxlength: 32,
trim: true,
},
lastname: {
type: String,
maxlength: 32,
trim: true,
},
email: {
type: String,
trim: true,
required: true,
unique: true,
},
usrinfo: {
type: String,
trim: true,
},
encry_password: {
type: String,
required: true
},
salt: String,
role: {
type: Number,
default: 0,
},
purchases: {
type: Array,
default: [],
},
}, { timestamps: true });
userSchema.virtual("password")
.set((password) => {
this._password = password;
this.salt = uuidv1();
this.encry_password = securePassword(password, uuidv1());
console.log(this.encry_password);
})
.get(() => {
return this._password;
});
// const authenticate = function (plainPassword, encry_password) {
// return securePassword(plainPassword) === encry_password;
// };
const securePassword = function (plainPassword, salt) {
if (!plainPassword) return "";
try {
return crypto.createHmac("sha256", salt).update(plainPassword).digest("hex");
} catch (error) {
return "";
}
};
module.exports = mongoose.model("User", userSchema);
Route for user signup
exports.signup = (req, res) => {
console.log(req.body);
const user = new User(req.body);
user.save((err, user) => {
if (err) {
console.log(err);
res.status(400).json({
err: "Note able to save the user in database"
});
} else {
res.json(user);
}
});
};
first of all, in this situation you shouldn't use virtual
Virtuals
Virtuals are document properties that you can get and set but that do not get persisted to MongoDB. The getters are useful for formatting or combining fields, while setters are useful for de-composing a single value into multiple values for storage.
but in the scope of virtual, this cannot access to method, you can not access to the method like your manner, it's a example of method usage in mongoose
const Animal = mongoose.model('Animal', animalSchema);
const dog = new Animal({ type: 'dog' });
dog.findSimilarTypes((err, dogs) => {
console.log(dogs); // woof
});
you can check the method documantation:
if you want just access to securePassword in your manner you can like this and delete method mongoose complately because this is not the place to use method:
UserSchema.virtual("password")
.set((password) => {
this._password = password;
this.salt = uuidv1();
console.log("This is running");
this.encry_password = securePassword(password, this.salt);
console.log(encry_password);
})
.get(() => {
return this._password;
});
const authenticate = function (plainPassword, encry_password) {
return securePassword(plainPassword) === encry_password;
};
const securePassword = function (plainPassword, salt) {
if (!plainPassword) return "";
try {
return crypto
.createHmac("sha256", salt)
.update(plainPassword)
.digest("hex");
} catch (error) {
return "";
}
};
if you want to create authenticate service, change your manner, and don't use virtual for password and use pre save
before saving information about users in db this tasks will be done
check the pre documentation
userSchema.pre("save", async function (next) {
try {
this.password = securePassword (plainPassword, salt);
} catch (error) {
console.log(error);
}
});
after created a hash password save informations like this :
const userSchema = new mongoose.Schema({
.
.
.
password: { //convert encry_password to password
type: String,
}
.
.
.
}, { timestamps: true });
//every time want to user save this method called
userSchema.pre('save', function (next) {
this.salt = uuidv1()
this.password = securePassword(this.password, this.salt)
next()
})
//for have a clean routes, you can create a static methods
userSchema.statics.Create = async (data) => {
let model = new User(data);
let resUser = await model.save(); //save your user
return resUser;
};
const securePassword = function (plainPassword, salt) {
if (!plainPassword) return "";
try {
return crypto.createHmac("sha256", salt).update(plainPassword).digest("hex");
} catch (error) {
return "";
}
};
let User = mongoose.model("User", userSchema)
module.exports = {User};
change controller like this :
let {User} = require("./path of user schema")
exports.signup = async (req, res) => {
try {
console.log(req.body);
const user = await User.create(req.body); //create a user
res.json(user);
} catch (error) {
console.log(err);
res.status(400).json({
err: "Note able to save the user in database",
});
}
};
NOTE : in req.body, name of password field, should be password
It looks like the scope of the securePassword function is defined inside userSchema, and you're trying to call it in userSchema.virtual.

E11000 duplicate key error index: mongo.users.$password_1 dup key:

I had this error even though i have sparse index set to true on that feild password
here is user schema:
var roledef = 'Member';
var mongoose = require('mongoose');
var UserSchema = new mongoose.Schema({
username: {
type: String,
required: true,
unique: true,
sparse: true,
minlength: [5, 'Username must be grater than 5 characters']
},
password: {
type: String,
required: true,
sparse: true,
minlength: [5, 'Username must be grater than 5 characters']
},
email: {
type: String,
required: true
},
profileid: {
type: String,
required: true,
},
roles: {
type: String,
default: roledef,
sparse: true
}
});
const User = mongoose.model('User', UserSchema);
module.exports = User;
// write encryption
and i am routing throug a index file which works
but i dont know if this is the best way to create a user
here is my user controller file :
const userModel = require('../../models');
const UserController = {};
const User = require('../../models/User')
const sha256 = require('sha256');
module.exports.validateregistration = (req, res) => {
console.log("##########################################################".red);
var ip = req.headers['x-forwarded-for'] || req.connection.remoteAddress
if (req.body.encodedUsername && req.body.encodedPassword && req.body.encodedEmail) {
// res.send(result);
log(ip);
var buffU = new Buffer(req.body.encodedUsername, 'base64');
var buffP = new Buffer(req.body.encodedPassword, 'base64');
var buffE = new Buffer(req.body.encodedEmail, 'base64');
var Username = buffU.toString('ascii');
var Pass = buffP.toString('ascii');
var Email = buffE.toString('ascii');
var hashPass = sha256.x2(Pass);
console.log("Register request for " + "Username: " + Username);
const userData = {
username: Username,
password: hashPass,
email: Email,
profileid: sha256(buffU)
};
const user = new userModel.User({
username: Username,
password: hashPass,
email: Email,
profileid: sha256(buffU)
});
const details = userData;
user.save().then((newuser) => {
res.status(200).json({
success: true,
data: newuser
});
}).catch((err) => {
res.status(500).json({
message: err
});
console.log(err);
});
// User.findOne({ username: userData.username, email: userData.email }, (err, resultofcheck) => {
// console.log(resultofcheck)
// if (resultofcheck == null) {
// User.create(userData, function (err, user) {
// if (err) {
// console.log(err);
// res.send("2")
// } else {
// console.log(user);
// console.log("New user created: " + user.username + " Profile id: " + user.profileid);
// res.redirect("/api/profile/" + user.profileid);
// }
// });
// } else {
// console.log(Username + " is taken")
// console.log(err);
// res.send("2");
// };
// });
}
console.log("##########################################################".red);
};
// if (!result) {
// db.collection('users').insert(details, (err, resultofinsert) => {
// if (err) {
// console.log(err);
// res.sendStatus(500);
// } else {
// res.send(user.profileid);
// console.log("New user created: " + user);
// }
// });
// };
// db.collection('users').insert(user, (err, result) => {
// if (err) {
// res.sendStatus(501);
// res.send({ 'error': 'An error has occured!' });
// } else {
// // res.send(result.ops[0].pass);
// console.log(result);
// };
// });
im calling throug another index file but iworks also but i get this error:
{ MongoError: E11000 duplicate key error index:
mongo.users.$password_1 dup key: { :
"e701ea082879498082d025e0cf9857ec0d19e6e86fa39f92ed3286de55d340e6" }
at Function.create (C:\Users\abinash\Desktop\api\node_modules\mongodb-core\lib\error.js:43:12)
at toError (C:\Users\abinash\Desktop\api\node_modules\mongoose\node_modules\mongodb\lib\utils.js:149:22)
at coll.s.topology.insert (C:\Users\abinash\Desktop\api\node_modules\mongoose\node_modules\mongodb\lib\operations\collection_ops.js:828:39)
at C:\Users\abinash\Desktop\api\node_modules\mongodb-core\lib\connection\pool.js:532:18
at _combinedTickCallback (internal/process/next_tick.js:131:7)
at process._tickCallback (internal/process/next_tick.js:180:9) driver: true, name: 'MongoError', index: 0, code: 11000,
errmsg: 'E11000 duplicate key error index: mongo.users.$password_1 dup
key: { :
"e701ea082879498082d025e0cf9857ec0d19e6e86fa39f92ed3286de55d340e6" }',
[Symbol(mongoErrorContextSymbol)]: {} }
and even though the username and email feilds are different and password isnt required to be unique i tryed to use sparse: true but it dont work pls hep point out what i did wrong
the commented outt parts are the things itryed before i found out about mongoose models

Bcrypt error in simple Node router

I have a route for creating users in Node/Express. I am getting a weird error about a method on the model not existing.
Here is the model for users:
'use strict';
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
mongoose.Promsie = global.Promise;
const UserSchema = mongoose.Schema({
username: { type: String, required: true, unique: true },
password: { type: String, required: true },
email: { type: String, required: true },
firstName: { type: String },
lastName: { type: String },
families: [
{
family_key: { type: String, required: true },
family_name: { type: String }
}
]
});
UserSchema.methods.apiRepr = function() {
return {
id: this._id,
firstName: this.firstName,
lastName: this.lastName,
username: this.username,
email: this.email,
families: this.families
};
};
UserSchema.methods.hashPassword = function(password) {
return bcrypt.hash(password, 10);
}
UserSchema.methods.validatePassword = function(password) {
return bcrypt.compare(password, this.password);
}
const User = mongoose.models.User || mongoose.model('User', UserSchema);
module.exports = { User };
Not particularly complicated. BUT, my the route is having trouble with the "hashPassword" method. When I try to use this route, I get an error that says "TypeError: User.hashPassword is not a function"
Here is the route (the issue is close to the bottom):
router.post('/', jsonParser, (req, res) => {
// checking that required fields are present
const requiredFields = ['username', 'password', 'email'];
const missingField = requiredFields.find(field => !(field in req.body));
if(missingField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Missing field',
location: missingField
});
}
// checking the format of string fields
const stringFields = ['username', 'password', 'email', 'lastname', 'firstname'];
const nonStringField = stringFields.find(
field => field in req.body && typeof req.body[field] !== 'string'
);
if (nonStringField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Incorrect field type: expected string',
location: nonStringField
});
}
// checking the trimming on fields
const trimmedFields = ['username', 'password', 'email'];
const nonTrimmedField = trimmedFields.find(
field => req.body[field].trim() !== req.body[field]
);
if (nonTrimmedField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: 'Cannot start or end with whitespace',
location: nonTrimmedField
});
}
// checking length of fields with required length
const sizedFields = {
username: { min: 1 },
password: { min: 10, max: 72 }
};
const tooSmallField = Object.keys(sizedFields).find(field =>
'min' in sizedFields[field] &&
req.body[field].trim().length < sizedFields[field].min
);
const tooLargeField = Object.keys(sizedFields).find(field =>
'max' in sizedFields[field] &&
req.body[field].trim().length > sizedFields[field].max
);
if (tooSmallField || tooLargeField) {
return res.status(422).json({
code: 422,
reason: 'Validation Error',
message: tooSmallField
? `Must be at least ${sizedFields[tooSmallField].min} characters long`
: `Must be at most ${sizedFields[tooLargeField].max} characters long`,
location: tooSmallField || tooLargeField
});
}
// creating the user
let { username, firstname, lastname, families, email, password } = req.body;
return User.find({ username })
.count()
.then(count => {
if(count > 0) {
return Promise.reject({
code: 422,
reason: 'Validation Error',
message: 'Username already taken',
location: 'username'
});
}
return User.hashPassword(password);
})
.then(hash => {
return User.create({ username, firstname, lastname, families, email, password: hash })
})
.then(user => {
return res.status(201).json(user.apiRepr());
})
.catch(err => {
console.error(err)
res.status(500).json({ code: 500, message: 'Internal server error'})
})
})
It does not like the return User.hashPassword(password) part. Any thoughts about what is causing this? I'm copying from a working app. Not sure what I'm doing wrong here.
The methods in node.js can not be used directly using the SchemaName you need to create an object of the schema name and then use the methods of the schema.
Ex:
var AnimalSchema = new Schema({
name: String
, type: String
});
AnimalSchema.methods.findSimilarType = function findSimilarType (cb) {
return this.model('Animal').find({ type: this.type }, cb);
};
var Animal = mongoose.model('Animal', AnimalSchema);
var dog = new Animal({ name: 'Rover', type: 'dog' });
dog.findSimilarType(function (err, dogs) {
if (err) return ...
dogs.forEach(..);
})
Source: http://mongoosejs.com/docs/2.7.x/docs/methods-statics.html
In your code you are trying to access the methods from the model.
Instantiate the model then use the methods.
If need use like the way you are using in the code try using function instead of methods.
module.exports.funtionName = function(/*function params*/){
//function body here
};

Mongo: hash password only being created on insert not update

Below is a JS for Account mongoose model file for my mongo account collection with a couple of methods. When a an account is created the hashedPassword field is created however how its not working when I update the document. Is it the case that the virtuals are only called on insert rather than update ?
'use strict';
var mongoose = require('mongoose'),
Schema = mongoose.Schema,
crypto = require('crypto');
var AccountSchema = new Schema({
email: {type: String,unique: true,required: true},
username: {type: String,unique: true,required: true},
group:{type:Schema.Types.ObjectId,ref:"Groups",require:false},
hashedPassword: String,
salt: String,
name: {type: String,require:false},
active: {type: Boolean,require:false},
});
/**
* Virtuals
*/
AccountSchema.virtual('password').set(function(password) {
this._password = password;
this.salt = this.makeSalt();
this.hashedPassword = this.encryptPassword(password);
})
.get(function() {
return this._password;
});
AccountSchema.virtual('user_info').get(function () {
return { '_id': this._id, 'username': this.username, 'email': this.email };
});
/**
* Validations
*/
AccountSchema.path('email').validate(function (email) {
var emailRegex = /^([\w-\.]+#([\w-]+\.)+[\w-]{2,4})?$/;
return emailRegex.test(email);
}, 'The specified email is invalid.');
AccountSchema.path('email').validate(function(value, respond) {
respond(true);
}, 'The specified email address is already in use.');
AccountSchema.methods = {
authenticate: function(plainText) {
return this.encryptPassword(plainText) === this.hashedPassword;
},
makeSalt: function() {
return crypto.randomBytes(16).toString('base64');
},
encryptPassword: function(password) {
console.log('encryptPassword')
if (!password || !this.salt) return '';
var salt = new Buffer(this.salt, 'base64');
return crypto.pbkdf2Sync(password, salt, 10000, 64).toString('base64');
}
};
mongoose.model('Account', AccountSchema);
In the controller
$scope.register = function(form) {
Auth.createUser({
provider : "local",
_id: $scope.userData._id,
email: $scope.userData.email,
username: $scope.userData.name,
level : $scope.userData.level,
group : $scope.userData.group,
name : $scope.userData.name,
password: $scope.userData.password,
active: $scope.userData.active
},
function(err) {
$scope.errors = {};
if (!err) {
$state.go('users');
} else {
angular.forEach(err.errors, function(error, field) {
form[field].$setValidity('mongoose', false);
$scope.errors[field] = error.type;
});
}
}
);
The services.js where the createUser func is called
/* Services */
app.factory('User', function ($resource) {
return $resource('/auth/users/:id/', {},
{
'update': {
method:'PUT'
}
});
});
app.factory('Session', function ($resource) {
return $resource('/auth/session/');
});
app.factory('Auth', function Auth($location, $rootScope, Session, User, $cookieStore) {
$rootScope.currentUser = $cookieStore.get('user') || null;
$cookieStore.remove('user');
return {
createUser: function(userinfo, callback) {
var cb = callback || angular.noop;
User.save(userinfo,
function(user) {
$rootScope.currentUser = user;
return cb();
},
function(err) {
return cb(err.data);
});
},
currentUser: function() {
Session.get(function(user) {
$rootScope.currentUser = user;
});
}
};
});

Resources