Throw errors outside of promise - javascript

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);
})

Related

How do i Validate old password while updating user password?

What's the proper way to validate old user password while updating new password?
So far i have tried and always get error: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
What i did:
I tried using bcrypt to compare the old password from req.body with user existing password and then hash with bcrypt before saving. Comparing the password using bcrypt gave the error above. Not comparing the old password at all and just saving new password works properly.
My code:
exports.updatePassword = async (req, res) => {
try {
const { oldPassword, password } = req.body;
let updatedPassword = {
password: password,
};
const user = await User.findOneAndUpdate(
{ _id: req.params.userId },
{ $set: updatedPassword },
{ new: true, useFindAndModify: false }
);
// validate old password
bcrypt.compare(oldPassword, user.password, function (err, match) {
if (!match || err)
return res.status(400).send('Please enter correct old password');
});
//hash password and save user
bcrypt.genSalt(12, function (err, salt) {
bcrypt.hash(user.password, salt, (err, hash) => {
user.password = hash;
user.save();
return res.json({user});
});
});
} catch (err) {
console.log(err);
return res.status(400).send('Something went wrong. Try again');
}
};
The issue is that the updatePassword function is ending before you actually process everything. To avoid nested function calls and returns, use the async methods provided by bcrypt (also check their recomendation on using async vs sync).
Regarding the code itself, you are updating the user's password before checking if the password is valid. You should get the user from the db, check if the current password matches, and only then insert the new hashed password into the db.
exports.updatePassword = async (req, res) => {
const { oldPassword, password } = req.body;
try {
// get user
const user = await User.findById(req.params.userId);
if (!user) {
return res.status(400).send('User not found');
}
// validate old password
const isValidPassword = await bcrypt.compare(oldPassword, user.password);
if (!isValidPassword) {
return res.status(400).send('Please enter correct old password');
}
// hash new password
const hashedPassword = await bcrypt.hash(password, 12);
// update user's password
user.password = hashedPassword;
const updatedUser = await user.save();
return res.json({ user: updatedUser });
} catch (err) {
console.log(err);
return res.status(500).send('Something went wrong. Try again');
}
};

UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'password' of undefined

This is my login code. Unhandledpromises occour while comparing Users input password and stored password.
exports.login = (req, res) => {
try {
const {email, password} = req.body
console.log(req.body)
if(!email || !password) {
return res.status(400).render('login', {
message: 'Please Provide an email and password'
})
}
db.query('SELECT * FROM users WHERE email = ? ',{email}, async (error, results) => {
console.log(error)
console.log(results)
if( !results || !(await bcrypt.compare(password, results[0].password)) ) {
res.status(401).render('login', {
message: 'Email or Password is Incorrect'
})
}
})
} catch (error) {
console.log(error)
}
}
Console.log(error) is null..
Updated
I'd suggest you to promisify db.query so you can await it, the errors will then be properly caught in the catch block, and your code will be flattened.
Your error is caused by results[0] which is undefined when trying to access its password property, which means that results is an empty array.
The second argument of query should not be an Object but an Array, so replace { email } with [email]:
const {
promisify
} = require('util');
const query = promisify(db.query.bind(db));
exports.login = async (req, res) => {
try {
const {
email,
password
} = req.body;
if (!email || !password) {
return res.status(400).render('login', {
message: 'Please Provide an email and password'
});
}
const results = await query('SELECT * FROM users WHERE email = ? ', [email]); // problem occur due to usage of curly brackets.
if (!results || !(await bcrypt.compare(password, results[0].password))) {
res.status(401).render('login', {
message: 'Email or Password is Incorrect'
});
}
} catch (error) {
console.log(error);
}
}

BCRYPT issue pulling from MySQL

Novice in my programming endeavors, but I am stuck and cannot find any help. I am able to register users into my db and hash the password. I am trying to be able to login with that user. I am able to check for required field in email and password, but when I enter a user in the db with the correct password the page hangs.
'''
exports.login = (req,res) =>{
try {
const {email, password} = req.body;
if (!email || !password){
return res.status(400).render('login', {
message: 'Email and password required.'
})
}
db.query ('SELECT * FROM users WHERE email = ?', [email], async (error, results)=> {
if(error)
console.log(error)
if(!results){
res.status(401).render('login', {
message: 'Invalid email or password.'
})
}else{
await bcrypt.compare(req.body.password, results.password)
if(bool == false){
res.status(401).render('login', {
message: 'Invalid password.'
})
}else{
res.status(401).render('index', {
message: 'User Logged in'
});
}
}
})
} catch (error) {
console.log(error);
}
}'''
Try this function : (bcryptCompare.js)
const checkBcrypt = async function(pass, hash) {
const bcrypt = require('bcrypt');
return new Promise(resolve => {
bcrypt.compare(pass, hash, function(bcryptErr, bcryptRes) {
if (bcryptErr) {
console.log('ERROR -> BCRYPT');
return resolve(false);
} else {
if (!bcryptRes) {
return resolve(false);
} else {
return resolve(true);
}
}
});
});
}
module.exports = checkBcrypt;
You can use it like this:
const checkBcrypt = require('./bcryptCompare');
const checkPass = await checkBcrypt(req.body.password, results.password);
if(checkPass){
//
} else {
//invalid password
}

How to catch wrong username or password on Expressjs?

I have a POST route that takes an username and password and returns a token on successful login. If either username or password are blank, it returns an error. However, if I provide a wrong username or password, I'm still getting a token generated from the provided information. How can I check the information returned from the database to return the same wrong username or password error? This is my route:
router.post('/login', (req, res) => {
if (req.body.username == '' || req.body.password == '') {
res.status(401).send({ error: "Wrong username or password" });
} else {
queries.login(req.body.username, req.body.password).then((user) => {
if (res.error) {
res.status(401).send({ error: 'Wrong username or password' });
}
res.json(auth.getToken(user.id, user.username));
});
}
});
And this is my query:
async login(username, password) {
let getUser = await knex('users').where('username', username);
let user = getUser[0];
try {
if (await argon.verify(user.password_hash, password)) {
return user;
}
} catch (e) {
return e;
}
}
In your login query you need to throw an error for the error case, like:
async login(username, password) {
try {
let getUser = await knex('users').where('username', username);
let user = getUser[0];
if (await argon.verify(user.password_hash, password)) {
return user;
}
throw Error('User not verified');
} catch (e) {
throw Error(e.message);
}
}
Then in your route you can handle the error case by using .catch():
router.post('/login', (req, res) => {
if (req.body.username == '' || req.body.password == '') {
res.status(401).send({ error: "Wrong username or password" });
} else {
queries.login(req.body.username, req.body.password)
.then((user) => {
res.json(auth.getToken(user.id, user.username));
});
.catch((error) => {
res.status(401).send({ error: 'Wrong username or password' });
});
}
});

"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