MongoDB returning an onject that doesnt exist - javascript

I was working on a project with MongoDB and passport, when i ran into this error, event though p1 isn't used, it still reruns an object im guessing, because it just says that the field p1 is taken, when it isn't. The same is happening with p2. Does anyone know why ?
passport.use(
"local.signup",
new LocalStrtegy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true,
},
async function (req, email, password, done) {
req.checkBody("email", "E-mail is empty").notEmpty();
req
.checkBody("password", "Your password is too short!")
.isLength({ min: 4 });
var errors = await req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash("error", messages));
}
const p1 = User.find({ p1: req.body.p1 });
const p2 = User.find({ p2: req.body.p2 });
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, {
message:
"This E-Mail alredy in use! If you believe that this is an error, please an admin on. (ERR 002 MCEE)",
});
} else if (p1) {
return done(null, false, {
message:
"This username is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCEM)",
});
} else if (p2) {
return done(null, false, {
message:
"This Tag is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCED)",
});
}
console.log(mc + " " + dcign + " " + user);
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(req.body.password);
newUser.p1 = req.body.p1;
newUser.p2 = req.body.p2;
newUser.Banned = false;
console.log(req.body);
newUser.save(function (err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}
)
);

Calling User.find returns a Promise which you are not awaiting. So when you are checking for existence of p1 and p2, it returns a truthy value as both values are Promise objects.
To fix the issue use await in front of both User.find like this
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
After that both values will be array as you are using find method so just check for length property or better use findOne instead of find method.
const p1 = await User.findOne({ p1: req.body.p1 });
const p2 = await User.findOne({ p2: req.body.p2 });

MongoDB .find returns an array. In your case p1 is an empty array. if(p1) will always return true. You should check for its length.
You should use await for your query calls.
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
I'm pasting sample code below -
passport.use(
"local.signup",
new LocalStrtegy(
{
usernameField: "email",
passwordField: "password",
passReqToCallback: true,
},
async function (req, email, password, done) {
req.checkBody("email", "E-mail is empty").notEmpty();
req
.checkBody("password", "Your password is too short!")
.isLength({ min: 4 });
var errors = await req.validationErrors();
if (errors) {
var messages = [];
errors.forEach(function (error) {
messages.push(error.msg);
});
return done(null, false, req.flash("error", messages));
}
const p1 = await User.find({ p1: req.body.p1 });
const p2 = await User.find({ p2: req.body.p2 });
User.findOne({ email: email }, function (err, user) {
if (err) {
return done(err);
}
if (user) {
return done(null, false, {
message:
"This E-Mail alredy in use! If you believe that this is an error, please an admin on. (ERR 002 MCEE)",
});
} else if (p1.length) { // Check for Length
return done(null, false, {
message:
"This username is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCEM)",
});
} else if (p2.length) { // Check for Length
return done(null, false, {
message:
"This Tag is alredy in use! If you believe that this is an error, please contact an admin. (ERR 002 MCED)",
});
}
console.log(mc + " " + dcign + " " + user);
var newUser = new User();
newUser.email = email;
newUser.password = newUser.encryptPassword(req.body.password);
newUser.p1 = req.body.p1;
newUser.p2 = req.body.p2;
newUser.Banned = false;
console.log(req.body);
newUser.save(function (err, result) {
if (err) {
return done(err);
}
return done(null, newUser);
});
});
}
)
);

Related

Can I pass my own data (object) back from passport.use('local') to passport.authenticate?

I want to pass my own data from passport.use to passport.authenticate.
I thought that the info parameter in
passport.authenticate('local', function(err, user, info)
could be used for that.
So is there a way to doing this ?
My auth route
passport.authenticate('local-register', (err, user, info) => {
if (err) {
return next(err); // 500 status
}
console.log(info);
if (info) {
console.log('rendering info ' + info);
return res.render('auth/register', { info });
} else {
if (!user) {
return res.status(409).render('auth/register');
}
req.login(user, err => {
if (err) {
console.log(err);
return next(err);
}
return res.redirect('auth/profile');
});
}
})(req, res, next);
});
My config file
module.exports = passport => {
passport.use(
'local-register',
new LocalStrategy(
{
...
},
(req, email, password, done) => {
// Check if form is filled out correctly
let errors = [];
//check for same email
SCUser.findOne({ 'local.email': req.body.email }, (err, local) => {
if (local) errors.push({ text: 'Email already in use.' });
//check for same passwords
...
//check for password length
...
//abort if errors are found
if (errors.length > 0) {
const info = {
errors: errors,
...,
};
console.log(`returning info ${info}`);
return done(null, info);
}
//form is filled in correctly create a user
else {
...
}
...
Random things I've tried so far:
Adding , form_validate behind info & changing the required variables to form_validate doesn't pass it through to the auth file.
There are probably better ways to handle form validation, haven't looked that up yet, if you have any suggestions please tell me, but I still kind of want to know if it would be possible to pass custom objects through passports methods.
Take a look at the example from the documentation:
passport.use(new LocalStrategy(
function(username, password, done) {
User.findOne({ username: username }, function (err, user) {
if (err) { return done(err); }
if (!user) {
return done(null, false, { message: 'Incorrect username.' });
}
if (!user.validPassword(password)) {
return done(null, false, { message: 'Incorrect password.' });
}
return done(null, user);
});
}
));
If you determine that the username/pass are not correct, you can call done with the last parameter being any object that you wish
return done(null, false, { message: "Incorrect password", otherData: "my other data"});
And, if it is a success, you can pass the user, but there is no reason you can't add more data to the user, or pass something completely different.
return done(null, {username: "user123", otherData: myData, customString: "myString"});

Nodejs, bcrypt async, mongoose login

I have the following code for the user model
const userSchema = mongoose.Schema({
_id: mongoose.Schema.Types.ObjectId,
username: {
type: String,
required: true,
trim: true,
minlength: 3,
},
email: {
type: String,
required: true
},
password: {
type: String,
required: true
}
})
Then for hashing the password i have this code
UserSchema.pre('save', function (next) {
if (this.isModified('password')){
bcrypt.genSalt(10, (err, salt)=>{
bcrypt.hash(this.password, salt, (err, hash)=>{
this.password = hash;
next();
})
});
}
next();
})
Then for checking the hash against the input value from user i have the following code
userSchema.statics.comparePassword = function(password){
let user = this;
return bcrypt.compareAsync(password, user.password)
}
So when it comes to the usage of all of these pieces of code i have the following
async loginUser(req, res) {
try{
const {email, password} = req.body
const user = await User.findOne({
email: req.body.email
})
if(!user){
return res.status(403).send({
error: "Incorrect details email"
})
}
const isPassValid = await user.comparePassword(password)
}catch(err){
res.status(403).send({
error: "The bid deal happened"
})
}
}
So I've tried searching on google and on this forum to find the answers but everything seems to be outdated or not working particularly for my situation. This code always sends "The bid deal happened", i've tried to debug it from all sides but its still unsuccessful.
The question is how to make it work? so that i can compare the passwords in the right way
P.S I've tried changing from compareAsync to compareSync, shows no effect
You encrypt the password when the value is changed, but not when you insert a new mongo document, you can check this with document.isNew.
I have updated your save method to the follow.
UsersSchema.pre('save', function (next) {
let user = this;
if (this.isModified('password') || this.isNew) {
bcrypt.genSalt(10, (err, salt) => {
if (err) {
return next(err);
}
bcrypt.hash(user.password, salt, (err, hash) => {
if (err) {
return next(err);
}
user.password = hash;
next();
});
});
} else {
next();
}
});
Also, Schema.statics is used to serve static methods. The this context will not return the user, thus making this.password undefined. To populate the instances of your schema with methods, you have to append them to the Schema.methods object.
I have used bcrypt.compare in the past, I dont know if bcrypt.compareAsync is a valid method because the first one is already async. And if it was async, it wouldnt directly return a value. Compare requires a callback.
UsersSchema.methods.comparePassword = function (password, callback) {
bcrypt.compare(password, this.password, (err, isMatch) => callback(err, isMatch));
};
To compare the password, u can do something like the following:
const { email, password } = req.body
User.findOne({
email: email,
}, (err, user) => {
if (err) throw err;
if (user) {
user.comparePassword(password, (err, match) => {
if (match && !err) {
// match
}
});
}
});

Throw errors outside of promise

I have a function to log in a user which should return JSON.
const username = req.body.username;
const password = req.body.password;
if (!username) {
throw new Error('Missing username');
}
if (!password) {
throw new Error('Missing password');
}
User.findOne({ username, password }).then(user => {
res.json({ user });
}).catch(err => {
res.json({ err });
});
but then the errors for missing username or missing password are not returned in JSON.
I could change it to
const username = req.body.username;
const password = req.body.password;
if (!username) {
res.json({ err: 'Missing username' });
}
if (!password) {
res.json({ err: 'Missing password' });
}
User.findOne({ username, password }).then(user => {
res.json({ user });
}).catch(err => {
res.json({ err });
});
but it seems a little redundant.
Is the correct way to do it to encapsulate it in a promise?
In your first solution, the thrown errors won't be handled, because you throw them outside of promise chain and without try/catch block. In your second solution you can get cannot send headers after they sent error, because the response can be sent twice (username is missing and password is missing).
So the one of the possible solutions here, is to create a promise chain (using Promise.resolve()) and validate parameters here:
function validateParams() {
const username = req.body.username;
const password = req.body.password;
if (!username) {
throw new Error('Missing username');
}
if (!password) {
throw new Error('Missing password');
}
return { username, password };
}
Promise
.resolve()
.then(validateParams)
.then(filter => User.findOne(filter))
.then(user => res.json(user))
.catch(err => res.json(err));
The obvious way would indeed be to encapsulate them in a promise to start your promise chain (with the User.findOne being inside the first then-block) - that way your current error handler catches them just fine.
I'm taking the example from #alexmac and use es6 async feature:
function validateParams() {
const username = req.body.username;
const password = req.body.password;
if (!username) {
throw new Error('Missing username');
}
if (!password) {
throw new Error('Missing password');
}
return { username, password };
}
async function resolver() {
try {
await resolve()
let filter = validateParams()
let user = await User.findOne(filter)
await res.json(user)
} catch (e) {
await res.json(e)
}
}
and that would look more elegant by using an if instead of a throw:
async function(req, res) {
const password = req.body.password
const username = req.body.username
let c = !password ? 'missing password' :
!username ? 'missing username' : null
if (!c) {
c = await User.findOne({ username, password })
}
await res.json(c)
}
you can wrap your functions in a promise and handle it efficiently
function getRes(){
return new Promise(function(resolve, reject){
const username = req.body.username;
const password = req.body.password;
if (!username) {
reject(new Error('Missing username'));
}
if (!password) {
reject(new Error('Missing password'));
}
resolve(User.findOne({ username, password }));
});
}
getRes().then(function(result){
res.json(result);
}).catch(function(err){
res.json(err);
})

How do I properly chain Promises when both creating a new user and hashing their password?

I am trying to make a POST request that creates a new account. It first checks if there is already an account using that email. If there isn't, it then creates a new account and stores it in a user collection. It also does two things, it hashes the person's password and generates a token for that user when the account is created. Here is my code for my first attempt:
const bcrypt = require('bcryptjs');
const authentication = require('../controllers/authentication');
const express = require('express');
const passport = require('passport');
const passportService =require('../services/passport');
const config = require('../config/config');
const User = require('../model/user');
const router = express.Router();
const mongodb = require('mongodb');
const mongoose = require('mongoose');
mongoose.Promise = Promise;
router.post('/register', (req, res, next) => {
// I guess I will have to use this, and set this to true or false within findOne
let doesExist_ = undefined;
function generateUserToken(user){
return jwt.encode({sub: user._id, iat: timeStamp}, config.secretKey);
}
if (!req.body.email || !req.body.password) {
return res.status(442).send({error: 'Make sure that you entered your email and password'});
}
/// this doesn't return a boolean
User.findOne({email: req.body.email})
.then((err, user) => {
if (user !== null || user !== undefined){
doesExist_ = true;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
.then(() => {
console.log('in second Then, doesExist_ is ' + doesExist_);
if (!doesExist_){
let password = req.body.password;
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash){
if (err) throw err;
password = hash;
})
}).then(() => {
User.create({
username: req.body.username,
password: password,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
})
});
}
else {
return res.status(422).send({error: "Email is already in use"});
}
})
.catch((err) => {
console.log(err);
});
});
However, it isn't working, every time I try to create a new User, it fails to do so. Postman takes three minutes to process the request and when I check Robomongo afterwards, the user I just created is not in the collection.
I also wrote a second version of this making use of promises. I thought maybe the problem was that I needed to make my code wait for bcrypt to hash the password, then I could create and save the entry to the database:
const bcrypt = require('bcryptjs');
const authentication = require('../controllers/authentication');
const express = require('express');
const passport = require('passport');
const passportService =require('../services/passport');
const config = require('../config/config');
const User = require('../model/user');
const router = express.Router();
const mongodb = require('mongodb');
const mongoose = require('mongoose');
mongoose.Promise = Promise;
router.post('/register', (req, res, next) => {
// I guess I will have to use this, and set this to true or false within findOne
let doesExist_ = undefined;
function generateUserToken(user){
return jwt.encode({sub: user._id, iat: timeStamp}, config.secretKey);
}
if (!req.body.email || !req.body.password) {
return res.status(442).send({error: 'Make sure that you entered your email and password'});
}
/// this doesn't return a boolean
User.findOne({email: req.body.email})
.then((user) => {
/// this should be null if it doesn't exist
if (user !== null){
doesExist_ = true;
console.log('in first Then, user is...' + user);
console.log('in first Then, doesExist_ is...' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
.then(() => {
console.log('in second Then, doesExist_ is ' + doesExist_);
if (!doesExist_){
let password = req.body.password;
let hashedPassword;
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash){
if (err) throw err;
hashedPassword = hash;
console.log('hashedPassword is now...' + hashedPassword + ' and the hash is...' + hash);
})
});
let hashPromise = () => {
return new Promise ((resolve, reject) => {
if (hashedPassword !== undefined && hashedPassword !== null){
console.log('Within hashPromise, hashedPassword is...' + hashedPassword);
resolve();
}
else {
console.log('Within hashPromise, hashedPassword is...' + hashedPassword);
reject('hashedPassword is still undefined');
}
});
};
hashPromise().then(() => {
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
})
});
}
else {
return res.status(422).send({error: "Email is already in use"});
}
})
.catch((err) => {
console.log(err);
});
However, I am still running into the same problems with this version. It is running for a long ass time in Postman, and when the server times out, I still don't have the entry added to the collection. Am I chaining the promises correctly in this approach?
When I run this version I get this console log:
in first Then, doesExist_ is false
in second Then, doesExist_ is false
Within hashPromise, hashedPassword is...undefined
(node:8684) UnhandledPromiseRejectionWarning: Unhandled promise rejection (rejection id: 2): hashedPassword is still
undefined
hashedPassword is now...$2a$10$2VQ6AlaEgnrCvt8udshnZe/auswqawe8eZ1f5IXESmKmQVEI7nYNa and the hash is...$2a$10$2VQ6Al
aEgnrCvt8udshnZe/auswqawe8eZ1f5IXESmKmQVEI7nYNa
Before Bcrypt finishes hashing the password, I get the console.log from my Promise. Is the console.log an indicator that the Promise is not working? The Promise is supposed to be resolved once the variable hashedPassword is no longer undefined after bcrypt finishes hashing it. But it doesn't appear to be running as I intended.
Any tips as to how I can fix either versions? Am I doing too much in a single request given that I am also generating a token as well?
Any help would be greatly appreciated.
The standard bcrypt library does not return a promise - if you want to work with promises, you could use bcrypt-promise.
If you want to use the standard bcrypt lib, you'll have to work in the callback:
User.findOne({email: req.body.email})
.then((user) => {
if (user !== null){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, function(err, hash) {
// this is the function bcrypt calls when it's done
if (err) throw err;
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
});
});
});
}
});
(reduced example to show nesting - you'll still need to handle your errors and stuff)
alternatively - because callbacks get nested deep really quickly, you can of course create an extra function and give that to bcrypt as a callback:
var afterHash = function(err, hash){
if (err) throw err;
User.create({
username: req.body.username,
password: hashedPassword,
name: req.body.name,
email: req.body.email,
profilePic: req.body.profilePic,
/// userId: userid
}).then((user) => {
res.json({token: generateUserToken(user)});
});
};
User.findOne({email: req.body.email})
.then((user) => {
if (user !== null){
bcrypt.genSalt(10, function(err, salt){
bcrypt.hash(password, salt, afterHash);
});
}
});
I am currently learning how to use mongoose and I did not get to learning about hashes yet but I glimpsed at the docs and I think you are not using the methods correctly ...
first of all mongo docs say that findone (queries) don't return a promise but have a then() function
I would try something like :
User.findOne({email: req.body.email}).exec()
.then((user) => {
/// this should be null if it doesn't exist
if (user !== null){
doesExist_ = true;
console.log('in first Then, user is...' + user);
console.log('in first Then, doesExist_ is...' + doesExist_);
}
else {
doesExist_ = false;
console.log('in first Then, doesExist_ is ' + doesExist_);
}
})
or:
User.findOne({email: req.body.email})
.then((user) => {
doesExist_ = true;
console.log(user);
console.log('in first Then, doesExist_ is...' + doesExist_);
},(err) =>
{
doesExist_ = false;
console.log(err);
console.log('in first Then, doesExist_ is ' + doesExist_);
}
)
second of all bcrypt's genSalt() and hash() return a promise If the callbacks are omitted ...
Hope I was of help ...

"Error: Illegal arguments: string, undefined" and stop server in node JS

I'm trying to build logging application in node JS. in here password authentication app do not work properly. when i enter username and password it occur following error and stop server.
this is the error.
Here is the code for authentication part
passport.use(new LocalStrategy(
function(username, password, done) {
User.getUserByUsername(username, function(err, user){
if(err) throw err;
if (!user) {
return done(null, false, {message: 'Unknown user'});
}
User.comparePassword(password, user.password, function(err, isMatch){
if(err) throw err;
if (isMatch) {
return done(null, user);
} else {
return done(null, false, {message: 'Invalid password'});
}
});
});
}));
This code work for Unknown user.
but it is not working for comparing username and password. i cannot see any bug in here. i want a help for solve this.
In the name of the universe programmer
in my case i forgot to select the password
because in database the password was ((select: false))
this code for app
const user = await User.findOne({email}).select("+password")
i forgot to append the ((.select("+password")))to the findOne
and I received this error ;
Error: Illegal arguments: string, undefined
and this code for database
const User = new mongoose.Schema({
username:{
type:String,
required: [true,"نام کاربری ضروری است"]
},
email:{
type:String,
required: [true,"رایانامه ضروری است"],
unique: true,
match:[
/^\w+([\.-]?\w+)*#\w+([\.-]?\w+)*(\.\w{1,3})$/,
"لطفا یک رایانامه صحیح وارد کنید"
]
},
password:{
type:String,
required:[true,"رمز ضروری است"],
minlegth: 5,
select: false
}
})
I found the problem in here. it is not things regarding the code.
The thing is i had registered two users with same user name and different password. then when i tried to login with the user name and one password it occurred this error and stop the server.
Because there is embarrassing situation with find password to username that user entered. because there are two password with same username.
In my case, I was using arrow function
userSchema.methods.comparePassword = async (enterdPassword) => {
return await bcrypt.compare(enterdPassword, this.password);
};
which I converted to normal function
userSchema.methods.comparePassword = async function (enterdPassword) {
return await bcrypt.compare(enterdPassword, this.password);
};
that solved the problem
In my case, I'm using social signin/signup. When the user is signing up using a social login option, the value of the password stored is "NULL".
So I just added this little check :
comparePassword: function(password, user){
if (!user.password)
return false;
return bcrypt.compareSync(password, user.password);
}
At
"models/user.js"
Inside comparePassword
module.exports.comparePassword = (candidatePassword, hash, callback) => {...)
Add this code:
bcrypt.hash(candidatePassword, 10, (err, hash) => {
if(err) {
throw err;
}
bcrypt.compare(candidatePassword, hash, (err, isMatch) => {
if(err) {
throw err;
}
callback(null, isMatch);
});
});
Here We are grabbing username and password from the sign in page AND
finding our user by the username from the database and then
Matching its encrypted password with an entered password by the user
passport.use(new LocalStrategy(
(username,password,done)=> {
db.users.findOne({username: username},(err, user)=> {
if(err) return done(err);
if(!user) {
return done(null,false,{message: 'Incorrect Username'});
}
bcrypt.compare(password, user.password,(err,isMatch)=> {
if(err) return done(err);
if(isMatch) {
return done(null, user);
} else {
return done(null, false,{message: 'Incorrect Password'});
}
});
});
}
));
You need to apply await to your salt and password assignments too.
Like this,
const salt = await bcrypt.genSaltSync(10);
const password = await req.body.password;
You can write a code like this: After this.findOne({ select: [] ........}) ... I hope this is helpful
async validateUserPassword(loginDto: AuthLoginDto): Promise<User> {
const { mobile, email, password } = loginDto;
const user = await this.findOne({
select: ['id', 'email', 'mobile', 'password', 'salt', 'status', 'logged_at'],
where: [
{ mobile: mobile },
{ email: email }
]
});
if (user && await user.validatePassword(password)) {
const logged_at = {
logged_at: new Date()
}
await this.update({ id: user.id }, logged_at)
return user;
} else {
return null;
}
}
async validatePassword(password: string): Promise<boolean> {
const hash = await bcrypt.hash(password, this.salt);
return hash === this.password;
}
In my own case , I just want to check if the old password matches the password in Db but got the error , here is my code below:
changePassword = asyncHandler (async (req: IGetUserAuthInfoRequest, res: Response) => {
const user = await User.findById(req.user._id)
const {oldPassword, password} = req.body
if(!user) {
res.status(400)
throw new Error("User not found, please signup")
}
// Validate
if(!oldPassword || !password) {
res.status(400)
throw new Error("Please add old and new password")
}
// Check if old password matches password in DB
const passwordIsCorrect = await bcrypt.compare(oldPassword, user.password)
// Save new password
if(user && passwordIsCorrect) {
user.password = password
await user.save()
res.status(200).send("Password change successful")
} else {
res.status(400)
throw new Error("Old password is incorrect")
}
});

Categories

Resources