Bcrypt password compare fails when login - node.js

when I am trying to login with the defined url , at a point of bcrypting compare password it fails.
login route:-
router.post('/:compId/administration/login' , (req, res, next) => {
Admin.find({'admins.email': req.body.email},{ companyID: req.params.compId })
.exec()
.then(admin => {
if(admin.admins.length < 1) {
return res.status(401).json({
message: "Auth failed. admin not found."
})
}
bcryptt.compare(req.body.password, admin.admins.password, (err, result) =>{
if (err) {
return res.json({
message: "Auth failed. Check email and password"
});
}
if (result && admin.admins.verified === "true"){
const adminEmaill = "xyz#info.com";
const role2 = admin.admins.email===adminEmaill? "superadmin" : "admin";
const token = jwt.sign(
{
email: admin.admins.email,
phoneNo: admin.admins.phoneNumber,
role2,
comID: admin.admins.companyID
},
process.env.JWT_KEY,
{
expiresIn : "1h"
});
return res.status(200).json({
message: "Auth Successful",
token : token
});
}
else{
console.log("admin is not verified");
return res.json({
message: "Admin is not verified"
});
}
});
})
.catch(err =>{
if (err.code == 500)
res.status(500).send(["Something went wrong in login"]);
else
return next(err);
});
});
On response it is not validating my user and throwing message: "Auth failed. Check email and password" in response.
I think I have some error in defining my password path.
My model:-
var adminSchema = new mongoose.Schema({
companyName : {
type: String,
required: "Company name can't be empty.",
required: false
},
companyID: {
type: String,
},
admins: {
email : {
type: String,
required: "Email can't be empty.",
unique: true
},
password: {
type: String,
required: "Password name can't be empty."
},
firstName : {
type: String,
required: "First name can't be empty."
},
lastName : {
type: String,
required: "Last name can't be empty."
},
phoneNumber : {
type: String,
required: "Reqired for further contact. Can't be empty."
},
verified: String,
role: String,
emailResetTokenn: String,
saltSecret: String,
users:[ userSchema ]
}
});
adminSchema.pre('save', function (next){
bcryptt.genSalt(10, (err, salt) => {
bcryptt.hash(this.admins.password, salt, (err, hash) => {
this.admins.password = hash ;
this.admins.saltSecret = salt;
next();
});
});
});
I am not getting why I am getting this ? Is defining of my password is correct ? How can I do this when I have password in nested object ?

You need to call findOne method on mongoose model.
Admin.findOne({'admins.email': req.body.email, companyID: req.params.compId}) ...
The result of find method is an array

Related

Mongoose Save: Document failed validation, but console.log and manual insert is accepted

I'm working on some signup code (MERN stack) and I'm getting "Document failed validation" from MongoDB when using the user.save. However, it is accepted if I use console.log(), copy/paste the result into an insert, and replace the single quotes with double quotes.
Here is my signup route so far:
router.post("/signup", async (req, res) => {
let { firstName,
lastName,
profileImage,
email,
subscribed_Comments,
subscribed_NewPost,
username,
password } = req.body;
if (username && email && password && firstName && lastName && subscribed_Comments !== undefined && subscribed_NewPost !== undefined) {
username = username.toLowerCase();
email = email.toLowerCase();
//Validate Password
if (/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[#$!%*?&])[A-Za-z\d#$!%*?&]{8,15}$/.test(password)) {
//Check if username or email already used
User.findOne({ "$or": [{ "username": username }, { "email": email }] })
.exec((err, user) => {
if (err) {
console.error(err);
res.status(500).send({ "error": "An unexpected error occurred. Please contact an administrator." });
} else if (user === null) {
//User did not exist ... create
bcrypt.genSalt(Number(SALT), function (err, salt) {
if (err) {
console.error(err);
res.status(500).send({ "error": "An unexpected error occurred. Please contact an administrator." });
}
bcrypt.hash(password, salt, function (err, hash) {
if (err) {
console.error(err);
res.status(500).send({ "error": "An unexpected error occurred. Please contact an administrator." });
}
//create user
let user = new User({
username: username,
email: email,
password: hash,
userRole: "User"
});
console.log(user)
user.save((err, user) => {
if (err) {
console.error(err);
res.status(500).send({ "error": "Failed to create a User. Please contact an administrator." });
} else {
console.log(`Successfully created user ${user.username}`);
//create account
let account = new Account({
"FirstName": firstName,
"LastName": lastName,
"SubscribedNewPost": subscribed_NewPost,
"SubscribedComments": subscribed_Comments,
"ProfileImage": profileImage || "",
"User_Account": user._id
});
account.save((err, account) => {
if (err) {
console.error(err);
user.delete();
res.status(500).send({ "error": "Failed to create an Account. Please contact an Administrator" });
} else {
console.log(`Successfully created account for ${account.firstName} ${account.lastName}`);
req.session.userid = user._id;
req.session.username = user.username;
req.session.userRole = user.userRole;
}
})
}
});
});
});
} else {
console.log(`Username: ${username} or Email: ${email} already in use.`);
res.status(500).send({ "error": "Username or Email is already in use" });
}
});
} else {
// Invalid Password
console.log("Invalid Password in Signup");
res.status(500).send({ "error": "Password did not meet expected criteria" });
}
} else {
res.status(500).send({ "error": "Invalid Request" });
}
})
Here is the Mongoose Schema
const mongoose = require("mongoose");
const userSchema = mongoose.Schema({
username: {
type: String,
unique: [true, "That username already registered."],
required: [true, "Username is required."]
},
email: {
type: String,
unique: [true, "That email is already registered."],
required: [true, "Email is required."],
validate: {
validator: function(v){
return /^\w+#[a-zA-Z_]+?\.[a-zA-Z]{2,3}$/i.test(v);
},
message: "Email was not in a valid format."
}
},
password: {
type: String,
required: [true, "Password is required."]
},
userRole: {
type: String,
enum: ["Anonymous", "User", "Moderator"],
required: [true, "User Role is required."]
}
}, {collection: "User"}, {versionKey: false})
module.exports = mongoose.model("User",userSchema);
Here is the MongoDB Validation
{
$jsonSchema: {
bsonType: 'object',
title: 'user',
required: [
'username',
'email',
'password',
'userRole'
],
properties: {
_id: {
bsonType: 'objectId',
uniqueItems: true
},
username: {
bsonType: 'string',
uniqueItems: true
},
email: {
bsonType: 'string',
uniqueItems: true,
pattern: '^\\w+#[a-zA-Z_]+?\\.[a-zA-Z]{2,3}$'
},
password: {
bsonType: 'string'
},
userRole: {
'enum': [
'Anonymous',
'User',
'Moderator'
]
}
},
additionalProperties: false
}
}
Here is the json that fails validation (from the console.log()). If I copy/paste this and replace the single quotes with double quotes then mongodb accepts it.
{
_id: 606a0a09d70db426acda05ee,
username: 'johndoe',
email: 'johndoe#gmail.com',
password: '$2b$10$vofYCj31690seAIKHqjkUuba8paeJy5ZC1Qt8ZHjZHmKVQurZ.hfi',
userRole: 'User'
}
Any thoughts on why the above would fail validation when it is following my defined schema? I've tried removing the validation from the Mongoose schema to make sure its not failing there and it wasn't. The only left I think it could be would be it doesn't format of the _id, but it wouldn't make sense that mongodb would reject the default _id created by mongoose.
Edit: Here is the stack trace
MongoError: Document failed validation
at Function.create (D:\Projects\portfolio-backend\node_modules\mongodb\lib\core\error.js:57:12)
at toError (D:\Projects\portfolio-backend\node_modules\mongodb\lib\utils.js:123:22)
at D:\Projects\portfolio-backend\node_modules\mongodb\lib\operations\common_functions.js:265:39
at handler (D:\Projects\portfolio-backend\node_modules\mongodb\lib\core\sdam\topology.js:942:24)
at D:\Projects\portfolio-backend\node_modules\mongodb\lib\cmap\connection_pool.js:350:13
at handleOperationResult (D:\Projects\portfolio-backend\node_modules\mongodb\lib\core\sdam\server.js:558:5)
at MessageStream.messageHandler (D:\Projects\portfolio-backend\node_modules\mongodb\lib\cmap\connection.js:277:5)
at MessageStream.emit (events.js:310:20)
at processIncomingData (D:\Projects\portfolio-backend\node_modules\mongodb\lib\cmap\message_stream.js:144:12)
at MessageStream._write (D:\Projects\portfolio-backend\node_modules\mongodb\lib\cmap\message_stream.js:42:5)
at doWrite (_stream_writable.js:442:12)
at writeOrBuffer (_stream_writable.js:426:5)
at MessageStream.Writable.write (_stream_writable.js:317:11)
at Socket.ondata (_stream_readable.js:695:22)
at Socket.emit (events.js:310:20)
at addChunk (_stream_readable.js:286:12) {
driver: true,
name: 'MongoError',
index: 0,
code: 121
}
The error is probably because of the _id. You are sending the string representation of the ObjectId, but the _id required type is ObjectId. Try to replace the _id: 606a0a09d70db426acda05ee with the _id:ObjectId('606a0a09d70db426acda05ee');
Also you can try to change this:
let user = new User({
username: username,
email: email,
password: hash,
});
console.log(user)
user.save((err, user) => {
With this:
User.create({
username: username,
email: email,
password: hash,
}).then((user)=>{
console.log('User successfully created: ', user);
}, (error)=>{
console.log('ERROR: ', error);
})
So, I figured it out. Apparently, Mongoose Schema adds the __v integer to the .save call despite my having versionKey: false in the schema. I tested it by setting the validation to warning instead of error and observed the __v appearing in the documents that got saved. To fix it, I added the following to the Validation JSON in mongo.
__v: {
bsonType: 'int'
}
TLDR: The "validation" that was missing was the __v property not being defined while 'additionalProperties' was set to false.

how to use populate method with react and node.js?

i have two collection in my db "users" and "forms"
each user has a table of forms
i used populate method to do this and it works
this is the model of user:
const Schema = mongoose.Schema
const UserSchema = new Schema({
firstName: {
type: 'string',
required: ' Firstname is Required'
},
lastName: {
type: 'string',
required: ' lastName is Required'
},
email: {
type: 'string',
required: ' email is Required'
},
phone: {
type: 'string',
},
entrprise: {
type: 'string'
},
password: {
type: 'string',
required: ' password is Required'
},
forms: [{
type: mongoose.Schema.Types.ObjectId,
ref: "Form"
}
]
})
const User = mongoose.model('User', UserSchema)
module.exports = User
this the model of form
const Schema = mongoose.Schema
const FormSchema = new Schema({
logo: {
type: String,
},
compagnyName: {
type: String,
},
title: {
type: String,
},
description: {
type: String,
},
userId: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User'
},
cardQuestion: [
{
questionTitle: String,
questionType: String,
questionCourte: String,
questionLongue: String,
choixMultiple: String,
caseaCocher: String,
telechargerFichier: String,
date: Date,
heure: String,
delete: false,
obligatoire: false,
}
]
})
const Form = mongoose.model('Form', FormSchema)
module.exports = Form
and this is how i use populate method
const getUser = async (req, res) => {
try {
const { userId } = req.params;
if (!userId) return res.status(400).json({ message: "ERROR ID!" });
const result = await User
.findById(userId).populate('forms')
.exec()
return res.status(200).json({ message: "Success", result });
} catch (err) {
res.status(500).json({ message: "INTERNAL ERROR SERVER!" });
console.log(err.message);
}
};
i added a user with forms using postman with the post method
when i try to add a form with react
const response = await axios.post(`${API_URL}/form/:userId`,
{
...form,
userId: localStorage.getItem('userId')
},
{
headers: {
authorization: localStorage.getItem('userId')
}
}
i get the form with the user id like this:
{
"_id": "6022916bf1d1060f84bc17d0",
"compagnyName": "axa",
"description": "recruitement",
"userId": "60214c5ec0491fcb2d8c29e8",
"__v": 0,
"cardQuestion": []
},
i find the new form in the forms collection but when i get the user ,the forms field doesn't update (still empty if i don't add the table of forms manually)
"result": {
"forms": [],
"_id": "60214c5ec0491fcb2d8c29e8",
"firstName": "moon",
"lastName": "lea",
"email": "moon15#gmail.com",
"password": "$2b$10$bnH0cEBQKktgaKHfBac3L.xUUNEYt9HaqKdLKxLOERrHPG4MVPPFS",
"phone": "087654266377",
"__v": 0
}
}
this is how i add a user
const register = async (req, res) => {
try {
const { firstName, lastName, email, phone, password, forms } = req.body
if (!firstName || !lastName || !email || !phone || !password)
return res.status(400).json({ message: 'ERROR!' })
//CHECKING EXISTING USER
const found = await User.findOne({ email })
console.log(found)
if (found) return res.status(400).json({ message: 'Email already exist' })
console.log(found)
//HASHING THE PASSWORD
const hashPassword = await bcrypt.hash(password, saltRounds)
console.log(hashPassword)
//CREATING THE NEW USER
const newUser = new User()
newUser.firstName = firstName
newUser.lastName = lastName
newUser.email = email
newUser.password = hashPassword
if (phone) newUser.phone = phone
if (forms) newUser.forms = forms
console.log('i')
//SAVING THE NEW USER
const result = await newUser.save()
console.log(result)
if (!result) return res.status(400).json({ message: 'failed to create user' })
can someone help?
maybe you have to change .populate('forms') in getUser to .populate('form')
i think it save it in singular shape

handle mutiple duplicate error mongoose nodejs

I have User Schema like this
const UserSchema = new Schema({
username: {
type: String,
unique: true,
required: true,
lowercase: true,
trim: true
},
email: {
type: String,
unique: true,
required: true
},
phoneNumber: {
type: Number,
unique: true,
required: true
},
});
When I post a user with duplicate name, phonenumber or email. I receive the error like this:
This is error message E11000 duplicate key error collection: project.users index: username_1 dup key: { : "john" }
So my question is that how I can know it is a duplicate name or duplicate phonenumber. I want to receive the error like this.
This phone number has been already used
This name has been already used
You can catch the error in your error handler. You can get the path name and value using regex. Simple example:
app.use((err, req, res, next) => {
if (err.code === 11000 || err.code === 11001) {
const pathRegex = err.message.match(/\.\$([a-z]+)/)
const path = pathRegex ? pathRegex[1] : '';
const keyRegex = err.message.match(/key:\s+{\s+:\s\"(.*)(?=\")/)
const key = keyRegex ? keyRegex[1] : '';
return res.status(409)
.json({ status: false, message: `${path} '${key}' already exists`})
}
}
You can also check mongoose-unique-validator that produces a mongoose 'ValidationError' that you can parse more easily.
Alternately you can use a path validation mechanism.
UserSchema.path('username').validate(function(value, done) {
this.model('User').count({ username: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'User Name already exists');
UserSchema.path('email').validate(function(value, done) {
this.model('User').count({ email: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'Email already exists');
UserSchema.path('phoneNumber').validate(function(value, done) {
this.model('User').count({ phoneNumber: value }, function(err, count) {
if (err) {
return done(err);
}
done(!count);
});
}, 'Phone already exists');

Error while trying to save document in MongoDB

I have this signup method to save a user.
exports.signup = function(req, res) {
// Initialize the variables
var user = new User(req.body);
var message = null;
user.provider = 'local';
// save the user
user.save(function(err) {
if (err) {
console.log(err);
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
res.json(user);
}
});
}
});
};
Her is my Schema.
var UserSchema = new Schema({
name: {
type: String,
required: true
},
email: {
type: String,
index: true,
match: [/.+\#.+\..+/, "Please fill valid e-mail address"]
},
username: {
type: String,
trim: true,
unique: "Username should be unique",
required: true
},
password: {
type: String,
validate: [
function (password) {
return password && password.length > 6;
},
"Password should be greater than six letters"
]
},
salt: {
type: String
},
provider: {
type: String,
required: "Provider is required"
},
providerId: String,
providerData: {},
created: {
type: Date,
default: Date.now()
}
});
When I make a post request in an empty collection, the first is saved, but after that i am getting this error.
MongoError: Projection cannot have a mix of inclusion and exclusion.
at Function.MongoError.create (/home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/error.js:31:11)
at queryCallback (/home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/cursor.js:212:36)
at /home/sinnedde/WebstormProjects/mean-chatapp/node_modules/mongodb-core/lib/connection/pool.js:455:18
at _combinedTickCallback (internal/process/next_tick.js:67:7)
at process._tickCallback (internal/process/next_tick.js:98:9)
POST /signup 500 1445.231 ms - 615
Please help.
First req.body
{
"name":"John",
"email": "johndoe#gmail.com",
"username": "john123",
"password": "password"
}
Second req.body
{
"name":"Jane",
"email": "janedoe#gmail.com",
"username": "jane123",
"password": "password"
}

Why Password and Salt automatically in MEAN STACK?

Here I am trying to verify mobile number in user module. I have created token and I sent to user but whenever user is trying to verify using that particular token 'Password' and 'salt' automatically got changed. How to avoid this? Some one help me out .. here I want to update only
user.Mobileverification = 'verfied';
user.Mobileverificationcode = undefined;
user.mobileVerificationExpires = undefined;
Above three variables got changed but I don't know why password and salt has changed?
I have given my routes below:
app.route('/auth/mobilereset/:token').get(users.mobileresetResetToken);
app.route('/auth/mobilereset/:token').post(users.mobilereset);
controller:
exports.mobileresetResetToken = function(req, res) {
User.findOne({
Mobileverificationcode :req.params.token,
mobileVerificationExpires: {
$gt: Date.now()
}
// resetPasswordToken: req.params.token,
// resetPasswordExpires: {
// $gt: Date.now()
// }
}, function(err, user) {
if (!user) {
res.send({
message: 'Invalid token'
});
} else {
console.log('working fine');
}
});
};
exports.mobilereset = function(req, res, next) {
async.waterfall([
function(done) {
User.findOne({
Mobileverificationcode: req.params.token,
mobileVerificationExpires: {
$gt: Date.now()
}
}, function(err, user) {
if (!err && user) {
user.Mobileverification = 'verfied';
user.Mobileverificationcode = undefined;
user.mobileVerificationExpires = undefined;
user.save(function(err) {
if (err) {
return res.status(400).send({
message: errorHandler.getErrorMessage(err)
});
} else {
req.login(user, function(err) {
if (err) {
res.status(400).send(err);
} else {
// Return authenticated user
res.json(user);
done(err, user);
}
});
}
});
} else {
return res.status(400).send({
message: 'reset token is invalid or has expired.'
});
}
});
},
], function(err) {
if (err) return next(err);
});
};
model:
var UserSchema = new Schema({
username: {
type: String,
unique: 'testing error message',
required: 'Please fill in a username',
trim: true
},
password: {
type: String,
default: '',
// validate: [validateLocalStrategyPassword, 'Password should be longer']
},
email: {
type: String,
trim: true,
default: '',
// validate: [validateLocalStrategyProperty, 'Please fill in your email'],
// match: [/.+\#.+\..+/, 'Please fill a valid email address']
},
Mobilenumber: {
type: String,
default: ''
},
roles: {
type: [{
type: String,
enum: ['user', 'admin']
}],
default: ['user']
},
salt: {
type: String
},
provider: {
type: String,
required: 'Provider is required'
},
providerData: {},
additionalProvidersData: {},
updated: {
type: Date
},
created: {
type: Date,
default: Date.now
},
/* For reset password */
Mobileverificationcode: {
type: String,
},
mobileVerificationExpires: {
type: Date
},
Mobileverification: {
type: String,
trim: true,
default: 'Not Verified',
},
resetPasswordToken: {
type: String
},
resetPasswordExpires: {
type: Date
}
});
I don't know if you removed this or not but in MEAN.js user model, you have to be careful with the following code block:
/**
* Hook a pre save method to hash the password
*/
UserSchema.pre('save', function (next) {
if (this.password && this.isModified('password')) {
this.salt = crypto.randomBytes(16).toString('base64');
this.password = this.hashPassword(this.password);
}
next();
});
Which will be called right before you save the user data. That's probably why password and salt keep changing... You are calling user.save in mobile.reset() and that code block above is still present somewhere.
Update:
A possible way of doing it is:
/**
* Hook a pre save method to hash the password
*/
UserSchema.pre('save', function (next) {
if(!this.isModified('Mobileverification') && !this.isModified('Mobileverificationcode') && !this.isModified('mobileVerificationExpires')) {
if (this.password && this.isModified('password')) {
this.salt = crypto.randomBytes(16).toString('base64');
this.password = this.hashPassword(this.password);
}
}
next();
});
However it might need a few adjustments, such as improving this pre-save hook according to your needs and testing password changing and mobile verification to see if nothing is broken.

Resources