Express: Variable not getting rewritten - javascript

I have this JS code that I've been looking at for the past 2 hours and cannot find the reason why the variable "message" doesn't get rewritten to "User already exists.". The thing is that "Inside first if" runs, but the variable message doesn't get rewritten to "User already exists."
async function postRegister(req, res) {
const familyName = req.body.familyName;
const email = req.body.email;
const password = req.body.password;
const repeatPassword = req.body.repeatPassword;
let message = 'msg';
// Check if user already exists
await db
.promise()
.query(
'SELECT * FROM users WHERE email = ?',
[email],
function (err, results) {
if (results.length > 1) {
console.log('Inside first if');
message = 'User already exists.';
return;
} else {
if (password !== repeatPassword) {
console.log('passwords do not match');
} else {
const newUser = new User(familyName, password, email);
newUser.save();
res.redirect('/login');
}
}
}
);
console.log(message);
res.render('authentication/register', { message: message });
}

Try this. Do not find all the users. Find only one since there will not be any duplicate entry.
try {
const user = await User.findOne({ email: email})
let message = ''
if(user) {
message = 'User already exists'
// better return the response here
}
if (password !== repeatPassword) {
message 'passwords do not match'
}
if(message) {
// Some error message is present, perform your action here
}
// Create your user
res.redirect('/login')
} catch(err) {
console.log(err)
}

Related

Login system wrong username error handling with MySql / NodeJs

I have the below controller which I use to manage the login system on my app. Is working in all cases except the one when I insert the wrong username.
I have inserted the below conditional statement to handle the error:
if (error === null) {
res.status(401).render('login', {
message: 'Email or Password is incorrect'
})
}
But when I insert it, I receive the following message on the terminal:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
const jwt = require('jsonwebtoken');
const mysql = require('mysql');
const bcrypt = require('bcryptjs');
const { promisify } = require('util');
var db_config = {
host: process.env.DB_HOST,
user: process.env.DB_USER,
password: process.env.DB_PASS,
database: process.env.DB_NAME
};
var connection;
function handleDisconnect() {
connection = mysql.createConnection(db_config); // Recreate the connection, since
// the old one cannot be reused.
connection.connect(function (err) { // The server is either down
if (err) { // or restarting (takes a while sometimes).
console.log('error when connecting to db:', err);
setTimeout(handleDisconnect, 2000); // We introduce a delay before attempting to reconnect,
} // to avoid a hot loop, and to allow our node script to
}); // process asynchronous requests in the meantime.
// If you're also serving http, display a 503 error.
connection.on('error', function (err) {
console.log('db error', err);
if (err.code === 'PROTOCOL_CONNECTION_LOST') { // Connection to the MySQL server is usually
handleDisconnect(); // lost due to either server restart, or a
} else { // connnection idle timeout (the wait_timeout
throw err; // server variable configures this)
}
});
}
handleDisconnect();
// code in case the user leave the login space empty
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'
})
}
connection.query('SELECT * FROM login WHERE email=?', [email], async (error, results) => {
// conditional statement to handle the wrong username error
if (error === null) {
res.status(401).render('login', {
message: 'Email or Password is incorrect'
})
}
//conditional if statement to compare password in database and password inserted by the client
if (!results || !(await bcrypt.compare(password, results[0].password))) {
res.status(401).render('login', {
message: 'Email or Password is incorrect'
}) //conditional statement to fetch the id of the client and signign in with sign() function
} else {
const id = results[0].id;
const token = jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
// console.log('the token is:' + token);
const cookieOptions = {
expires: new Date(
Date.now() + process.env.JWT_COOKIE_EXPIRES * 24 * 60 * 60 * 1000 // 24 hours converted in milliseconds to set the expiration cookies to 24 hours
),
httpOnly: true
}//setting of cookies on the browser and redirecting to the user interface page
res.cookie('jwt', token, cookieOptions);
res.status(200).redirect('/ui');
}
});
} catch (error) {
console.log("this is the error:", error)
}
}
exports.register = (req, res) => {
// Destructor
const { name, email, password, passwordConfirm } = req.body;
//query that order to MySQL to get the user email only once
connection.query('SELECT email FROM login WHERE email = ?', [email], async (error, results) => {
if (error) {
console.log(error);
}
if (results.length > 0) {
return res.render('register', {
message: 'That email is already in use'
})
} else if (password !== passwordConfirm) {
return res.render('register', {
message: 'Password do not match'
});
}
let hashedPassword = await bcrypt.hash(password, 8);
// console.log(hashedPassword);
connection.query('INSERT INTO login SET ?', { name: name, email: email, password: hashedPassword }, (error, results) => {
if (error) {
console.log(error);
} else {
// console.log(results);
return res.render('register', {
message: 'User registered'
});
}
})
});
}
exports.isLoggedIn = async (req, res, next) => {
// console.log(req.cookies);
if (req.cookies.jwt) {
try {
//1)verify the token
decoded = await promisify(jwt.verify)(req.cookies.jwt,
process.env.JWT_SECRET
);
//2) Check if the user still exists
connection.query('SELECT * FROM login WHERE id = ?', [decoded.id], (error, result) => {
// console.log(result);
if (!result) {
return next();
}
req.user = result[0];
return next();
});
} catch (error) {
console.log(error);
return next();
}
} else {
next();
}
}
exports.logout = async (req, res) => {
res.clearCookie('jwt');
res.status(200).redirect('/');
}
Thanks in advance for suggestions or correction to the right path.
Do not forget to return the response or the function will continue.
return res.status(401).render('login', {
message: 'Email or Password is incorrect'
});
You need to halt the execution of the function by putting the return keyword or the code will run through the other statements.
if (error === null) {
return res.status(401).render('login', {
message: 'Email or Password is incorrect'
})
}
Or
if (error === null) {
res.status(401).render('login', {
message: 'Email or Password is incorrect'
})
return
}
I got it, or at least is working. I deleted the first if statement and modify the remain one as below. instead of !results I edited it to results == "" . So if results is empty it will render the login with te alert message.
if (results == "" || !(await bcrypt.compare(password, results[0].password))) {
res.status(401).render('login', {
message: 'Email or Password is incorrect'
}) //conditio

Forgot password functionality using NodeJs/Knex/Nodemailer and it is not working properly

Note: this is my first time posting, if you have feedback please let me know
Goal: I am building some endpoints that let a user reset their password if they forgot it. Flow would look like this:
User doesn't know password so they click on forgot password.
User types in email and clicks send
User receives email with link to reset password. Clicks on link and is redirected to type in their new password.
They click 'save' and they are redirected to login to sign in with their new password
I am using Insomnia to hit the endpoints for testing.
Things that are working:
When providing an email to reset password, Nodemailer does send out an email.
When updating the password it does show 'password updated' and gives a 200 status.
Bugs:
After trying to log in with that new password, it is not saving to the database. Only the old password will allow you to log back in.
Things I have tried:
I tried changing my user.model to use my findByEmail function and ran into some weird bugs, which then led me down a rabbit hold of issues.
I tried console logging quite a few things to see if I could trace the path.
I tried changing the user.update function but was not able to get it to work.
Here is my code:
Any guidance would be appreciated. If you need to look at any other files please let me know.
Forgot.password.js
const router = require('express').Router();
const crypto = require('crypto')
const User = require('../models/users.model')
const nodemailer = require('nodemailer')
router.post('/forgotpassword', (req, res) => {
let {
email
} = req.body
console.log(req.body)
// if (req.body.email === '') {
// res.status(400).json({ message: 'Email is required'})
// } console.error(req.body.email)
User.findBy({
email
})
.first()
.then(user => {
if (user === null) {
res.status(403).json({
message: 'Email not in db'
})
} else {
const token = crypto.randomBytes(20).toString('hex')
User.update({
resetPasswordToken: token,
resetPasswordExpires: Date.now() + 3600000,
})
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: `${process.env.EMAIL_USER}`,
pass: `${process.env.EMAIL_PASS}`
}
})
const mailOptions = {
from: `${process.env.EMAIL_USER}`,
to: `${user.email}`,
subject: '[Promoquo] Reset Password Link',
text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
'Please click on the following link, or paste this into your browser to complete the process within one hour of receiving it:\n\n' +
`http://localhost:5000/reset/${token}\n\n` +
'If you did not request this, please ignore this email and your password will remain unchanged.\n',
}
transporter.sendMail(mailOptions, (err, res) => {
if (err) {
console.log('ERROR coming from forgot.password js and it sucks', err)
} else {
console.log('here is the res', res)
res.status(200).json({
message: 'recovery email sent hell yes'
})
}
})
}
res.status(200).json({
message: 'Reset password email has been sent WOOHOO 🎉'
})
})
.catch(error => {
res.status(500).json({
message: 'ERROR on last catch forgotpassword.js, likely no user exists',
error
})
console.log(error)
})
})
module.exports = router
Update.password.js
const router = require('express').Router();
const passport = require('passport')
const bcrypt = require('bcrypt')
const User = require('../models/users.model')
const BCRYPT_SALT_ROUNDS = 12
router.put('/updatePasswordViaEmail', (req, res) => {
User.find({
where: {
username: req.body.username,
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: Date.now() + 3600000,
}
})
.then(user => {
if (user == null) {
console.error('password reset link has expired')
res.status(403).json({ message: 'Password reset link is invalid or has expired' })
} else if (user != null) {
console.log('user exists in db')
bcrypt.hash(req.body.password, BCRYPT_SALT_ROUNDS)
.then(hashedPassword => {
User.update({
password: hashedPassword,
resetPasswordToken: null,
resetPasswordExpires: null,
})
})
.then(() => {
console.log('log for THEN updating password')
res.status(200).json({ message: 'password updated' })
})
} else {
console.error('no user exists in db to update')
res.status(401).json({ message: 'no user exists in db to update'})
}
})
})
module.exports = router
Users.model.js
const db = require('../dbConfig')
module.exports = {
add,
find,
findBy,
findById,
findByEmail,
findByType,
update
};
function find() {
return db('users').select('id', 'username', 'email', 'password');
}
function findBy(filter) {
return db('users').where(filter);
}
async function add(user) {
const [id] = await db('users').insert(user);
return findById(id);
}
function findById(id) {
return db('users').where({ id }).first();
}
function findByEmail(email) {
return db('users').where({ email }).first();
}
function findByType(type) {
return db('users').where({ type }).first();
}
function update(changes, id) {
return db('users').where({ id }).update(changes)
}
20200913211559_users.js (this is the table)
exports.up = function(knex) {
return knex.schema.createTable('users', tbl => {
tbl.increments();
tbl.string('firstname', 30).notNullable();
tbl.string('lastname', 30).notNullable();
tbl.string('username', 30).notNullable()
tbl.string('email', 50).notNullable()
tbl.string('password', 128).notNullable();
tbl.string('type').notNullable();
tbl.boolean('confirmed').defaultTo('false');
tbl.string('resetPasswordToken');
tbl.date('resetPasswordExpires');
})
};
exports.down = function(knex) {
return knex.schema.dropTableIfExists('users')
};
Your User.update() lines aren't running (you either need to return their promises into the chains of promises, or hook into their callbacks). async/await is your friend here to avoid "callback hell."
const user = await User.find({
where: {
username: req.body.username,
resetPasswordToken: req.body.resetPasswordToken,
resetPasswordExpires: Date.now() + 3600000,
}
})
if (!user) { /* ... */ }
const token = crypto.randomBytes(20).toString('hex')
await User.update({ // await here!
resetPasswordToken: token,
resetPasswordExpires: Date.now() + 3600000,
})

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
}

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

Categories

Resources