Strange nodejs behaviour when logging in a user - mysql

The problem is that it shows that it is successfully logged in (201) without the redirect code, but with it, it shows a 302 error and the email_address is undefined.
What could be the problem here? I still can't come to a conclusion.
The problem may be in the order of the code I guess?
const login = async (req, res, next) => {
const { email_address, password, user_email, user_password}: { email_address: string, password: string, user_email: string, user_password: string } = req.body;
try {
const userWithDetails = 'SELECT * FROM users WHERE email_address = user_email AND password = user_password'; //w form info
if (userWithDetails) {
req.session.loggedin = true; //true
req.session.email_address = email_address; //undefined
console.log(req.session.email_address)
// return res.redirect('./index.html')
}
res.status(201).send('Succesfully signed in');
// res.status(403).send('Password is not correct');
} catch(error) {
res.status(404).send(`User with email ${email_address} not found!`);
}
await next;
};
NEW CODE ***
const login = async (req, res, next) => {
const { email_address, password}: { email_address: string, password: string} = req.body;
const userWithDetails = 'SELECT * FROM users WHERE email_address = ?';
return con.query(userWithDetails, email_address, (err, results) => {
if (err) {
console.error(err);
}
const user = results.find(emailObj => emailObj.email_address === email_address);
if (results && results.length && user.email_address) {
req.session.loggedin = true;
req.session.email_address = email_address;
const matchPassword: boolean = bcrypt.compareSync(password, user.password);
if (matchPassword) {
const token = jwt.sign({ user }, 'aaaa', { expiresIn: '1h'});
res.status(200).send({message: 'Logged in', token: token});
} else {
res.status(403).send('Password is not correct');
}
} else {
res.status(404).send(`User with email ${email_address} not found!`);
}
});
await next;
}

You don't execute your sql query at any point.
You just say :
query = 'select blabla'
if(query){...}
Of course this will always be true. You want to run the query on your database.
Also in your query you don't properly use the variables, see string formatting :
let my_var = `SELECT xxx from xxx where username = '${username}'`
Also please sanitize the parameters to prevent SQL Injection...

Related

My Express MySQL server get crash when user input wrong data for login

When the user try to login with wrong email or password my app throw error user not found if the email is not in dB a other error then password is wrong but after few second the server get crashed with the error
"SERVER CONNECTION LOST"
I was Thing the problem come in my error handling but after many try and search i'm now block
export const getUser = (req, res) => {
const token = req.cookies.accessToken;
if (!token) return res.status(401).json("Please logging first !");
jwt.verify(token, "secretkey", (err, data) =\> {
if (err) {
return res.status(403).json("Please login first !!");
}
const q = "SELECT * FROM `Users` WHERE ID= ?";
db.query(q, [req.params.userId], (err, data) => {
if (err) return res.status(500).json("Something when wrong !!");
const { user_PassCode, ...info } = data[0];
if(data) return res.json(info);
});
});
};
//...........LOGIN START HERE...............
export const logIn = (req, res) => {
const q = "SELECT * FROM Users WHERE user_Email = ? "
db.query(q, [req.body.user_Email], (err, data) => {
if (err) res.status(500).json(err)
if(data.length === 0) res.status(404).json('user not found!')
// chech if password match to userPasscode
const checkPassword = bcrypt.compareSync(
req.body.user_PassCode,
data[0].user_PassCode
)
if(!checkPassword) return res.status(400).json('wrong password or username try again!')
const token = jwt.sign({ id: data[0].ID }, "secretkey")
const {user_PassCode, ...others} = data[0]
// if not error
res.cookie("accessToken", token, {
httpOnly: true
}).status(200).json(others)
})
}

NodeJS with MySQL - Return blank Array

I have initialized an array "userTasklist". I have pushed the object in this array in .map function. After .map, I have console this Array but array is blank.
Than I have console the object in .map function and all the value print successfully but in Array there are no value. Don't know why.
exports.allUserList = (req, res) => {
let userID = req.params.userid;
const ghusersQuery = "SELECT user_id, name, employee_code FROM users WHERE under_gh = ?";
conn.query(ghusersQuery, [userID], (err, userdata) => {
if (err) {
res.send({ message: err })
} else {
if (userdata && userdata.length > 0) {
let userTasklist = [];
userdata.map((datauser) => {
var objtask = {};
const userDataQuery = "SELECT * FROM tasklist WHERE user_id = ?";
conn.query(userDataQuery, [datauser.user_id], (errnew, taskdata) => {
if (taskdata && taskdata.length > 0) {
objtask = {
userid: datauser.user_id,
tasklist: taskdata
}
userTasklist.push(objtask);
}
})
})
console.log(userTasklist)
res.send({ message: "user list fetched", userdata: userdata, tasklistdata: userTasklist })
} else {
res.send({ message: "Data not found!" })
}
}
})
}
Simplified solution using mysql21 for handling queries as Promises.
exports.allUserList = async (req, res) => {
const { userid } = req.params
const users = await connection.query('SELECT user_id, name, employee_code FROM users WHERE under_gh = ?', [userid])
if (!users.length)
return res.send({ message: "Data not found!" })
// better fetch all relevant tasks in bulk
const tasks = await connection.query('SELECT * FROM tasklist WHERE user_id IN (?)', [users.map(r => r.user_id)])
res.send({ message: "user list fetched", users, tasks })
}

Bycrpt cannot compare and always sends null values

i m using Bycrpty library for security. so i read bycrpt Official document.
i sent postman in signup routes. it work or not
it was success full! like that.
so i have to compare the passwords When logging in,
but compare is always failed. it's my code..
const jwt = require('jsonwebtoken');
// const { Op } = require("sequelize");
const { user } = require("../../models");
const bcrypt = require("bcrypt");
const salt = bcrypt.genSaltSync(10) ;
signUpController: async (req, res) => {
const { username, email, password} = req.body;
if( !(username && email && password) ){
res.status(405).send({
"message" : "invalid request"
});
}
else{
const userInfo = await user.findOne({
where: {
email: email,
username : username
}
});
if(userInfo === null){
const newUser = await user.create({
username: username,
email : email,
password: bcrypt.hashSync(password, salt),
});
let response = {
username: newUser.username,
email: newUser.email,
username: newUser.username,
password : newUser.password
}
res.status(201).json( response );
}
else{
res.status(409).send({
"message" : "email already exist"
});
}
}
},
login : async(req,res)=>{
const { email, password } = req.body;
const userInfo = await user.findOne({
where: {
email: email,
password : password
}
});
// console.log("req: ", req)
if(!userInfo) {
await res.status(400).send({data : null, message : 'not authorized'})
}
else {
const data = {...userInfo.dataValues}
console.log('password:', checkMail.password)
bcrypt.compareSync(password, userInfo.password) ;
delete data.password
const accessToken = jwt.sign(data, process.env.ACCESS_SECRET, {expiresIn : '3h'}) // create jwt
const refreshToken = jwt.sign(data, process.env.REFRESH_SECRET, {expiresIn : '1h'}) // save in cookie .
res.cookie("refreshToken", refreshToken)
res.status(200).send({data:{"accessToken": accessToken}, message:'ok'})
}
}
What should I do to be successful? I need advice and tips.
I'm slightly confused as your using async/await for some things like the database library however not for bcrypt which also has promises and instead you're using their sync versions. As a first advice I wouldn't use the sync versions of the code as they block the eventLoop.
There is another problem with your logic - which is highlighted below.
const jwt = require('jsonwebtoken');
// const { Op } = require("sequelize");
const { user } = require("../../models");
const bcrypt = require("bcrypt");
const salt = bcrypt.genSaltSync(10) ;
signUpController: async (req, res) => {
const { username, email, password} = req.body;
if( !(username && email && password) ){
res.status(405).send({
"message" : "invalid request"
});
}
else{
const userInfo = await user.findOne({
where: {
email: email,
username : username
}
});
// using email/username as unique fields to find a user and check if they already have an account
if(userInfo === null){
const newUser = await user.create({
username: username,
email : email,
password: bcrypt.hashSync(password, salt),
// saving the hashed password rather than the plaintext password
});
let response = {
username: newUser.username,
email: newUser.email,
username: newUser.username,
password : newUser.password
}
// do not under any circumstance send the password back to the user.
res.status(201).json( response );
}
else{
res.status(409).send({
"message" : "email already exist"
});
}
}
},
login : async(req,res)=>{
const { email, password } = req.body;
// you're trying to find a user that exists based on their email and plaintext password, but the password you've saved is the HASHED version not the plaintext version so this result will always be empty... No such user exists
const userInfo = await user.findOne({
where: {
email: email,
password : password
}
});
// console.log("req: ", req)
if(!userInfo) {
// hence this error is present ALL THE TIME
await res.status(400).send({data : null, message : 'not authorized'})
}
else {
const data = {...userInfo.dataValues}
console.log('password:', checkMail.password)
bcrypt.compareSync(password, userInfo.password) ;
// you wouldn't need this step as you've found the user based on the password
delete data.password
const accessToken = jwt.sign(data, process.env.ACCESS_SECRET, {expiresIn : '3h'}) // create jwt
const refreshToken = jwt.sign(data, process.env.REFRESH_SECRET, {expiresIn : '1h'}) // save in cookie .
res.cookie("refreshToken", refreshToken)
res.status(200).send({data:{"accessToken": accessToken}, message:'ok'})
}
}
This seems to me rather than misunderstanding how password hashing works you don't understand the data in your database.
I'd suggest to get a visual database explorer for whatever database you're trying to use. There are many free and opensource ones out there!

using bcrypt for login in nodejs

I'm having a hard time with integrating bcrypt to try to make my login system safe.
I basically get the username, password the user inputs and try to compare it from the hashed password in my db. here's what I have.
const inputUsername = req.body.inputUsername;
const inputPassword = req.body.inputPassword;
var userLogin = "select * from login where USERNAME = ?"
ibmdb.open(ibmdbconnMaster, function(err, conn) {
if (err) return console.log(err);
conn.query(userLogin, [inputUsername], function(err, rows) {
if (err) {
console.log(err)
}
if (rows.length > 0) {
var pass = ""
for (var i = 0; i < rows.length; i++) {
pass = rows[i]['PASSWORD'];
console.log(pass)
bcrypt.compare(inputPassword, hash, function(err, result) {
if (pass == result) {
console.log("this works")
userAuth = true;
res.redirect('/index')
}
})
}
console.log("does not work")
} else {
userAuth = "false";
res.render('login.ejs')
alert('Incorrect username or password. Please try again')
}
conn.close(function() {
console.log('closed the function /login');
});
})
})
what happens right now is I get the error ReferenceError: hash is not defined
not sure how to fix this. thanks in advance
Where have you defined hash? I don't see it in your code.
Here's an example of auth routes that I've used with bcrypt/node/express:
const Users = require("../users/users-model.js");
router.post("/register", (req, res) => {
// Pull the user's credentials from the body of the request.
const user = req.body;
// Hash the user's password, and set the hashed password as the
// user's password in the request.
const hash = bcrypt.hashSync(user.password, 10);
user.password = hash;
Users.add(user)
.then((newUser) => {
const token = generateToken(newUser);
res
.status(201)
.json({ created_user: newUser, token: token, user_id: newUser.id });
})
.catch((err) => {
res.status(500).json({
message: "There was an error adding a user to the database",
err,
});
});
});
router.post("/login", (req, res) => {
const { username, password } = req.body;
Users.findBy({ username })
.first()
.then((user) => {
if (user && bcrypt.compareSync(password, user.password)) {
const token = generateToken(user);
res
.status(200)
.json({
username: user.username,
first_name: user.first_name,
last_name: user.last_name,
email: user.email,
token: token,
user_id: user.id,
});
} else {
res.status(401).json({ message: "Invalid Credentials" });
}
})
.catch((err) => {
res.status(500).json(err);
});
});
function generateToken(user) {
const payload = {
userid: user.id,
username: user.username,
};
const options = {
expiresIn: "1h",
};
const token = jwt.sign(payload, secrets.jwtSecret, options);
return token;
}
module.exports = router;

Update only some attributes of user in MySQL using Nodejs

I have a put route which can be used to update the user. Everything works fine unless the user will only provide only some params instead of all. How I can fix this? Are there some "simple" solutions for this problem? Because if the user only update his email everything else will be inserted empty..
const id: number = req.params.id;
const password: string = req.body.password;
const email: string = req.body.email;
const lastname: string = req.body.lastname;
const firstname: string = req.body.firstname;
const phoneNumber: string = req.body.phoneNumber;
const permissionID: number = req.body.permissionID;
const imageUrl: string = String(imagePath);
const passwordHash = bcrypt.hashSync(password, 10);
const insertData: [string, string, string, string, string, string, number, number] = [email, passwordHash, phoneNumber, firstname, lastname, imageUrl, permissionID, id];
const query = `UPDATE Users SET email = ?, password = ?, phone_number = ?, first_name = ?, last_name = ?, image_url = ?, permission_id = ? WHERE user_id = ?;`;
connection.query(query, insertData, (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json( { "Message": "Successfull user was updated" } );
} else {
res.status(500);
res.json( { "Database Error ": err.message } );
}
});
Okay I wrote something I hope this post will help someone. First of course it's possible to save the complete user data model in the client and to resend the complete data to the server. But why should I do this? I don't think this is effecient. If the user just want to change his lastname why I should send the whole payload...Anyway this is the way I solve it.
First I define my possible data I will receive if the user will update some attributes.
enum Validate {
password = 'password',
email = 'email',
firstname = 'first_name',
lastname = 'last_name',
phoneNumber = 'phone_number',
permissionID = 'permission_id'
}
So my function will check the received params and will return the insertData and query. As I'm using password hashing it will check as well if the user wants to update his password.
function updateParams(body: {}, options: [Validate], callBack: (insertData: string[], query: string) => void) {
const insertData: string[] = [];
let query = "";
for (const index in options) {
if (!(body[`${options[index]}`] === '' || body[`${options[index]}`] === undefined || body[`${options[index]}`] === null)) {
query += `${options[index]} = ?, `;
// If user will update password hash it
`${options[index]}` === 'password' ? insertData.push(bcrypt.hashSync(body[`${options[index]}`], 10)) : insertData.push(body[`${options[index]}`]);
}
}
callBack(insertData, query.slice(0, -2));
}
For the next step I'm using promises because there are some if/else statements. The user has the possibilities to just update his picture for example.
const updateUser = (req, res, insertData, query) => {
const promise = new Promise((resolve, reject) => {
let endQuery = '';
if (req.file) {
image.uploadImageToStorage(req.file)
.then((imagePath) => {
if (Object.keys(req.body).length === 0) {
endQuery = `UPDATE Users SET image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
} else {
endQuery = `UPDATE Users SET ${query}, image_url = ? WHERE user_id = ?;`;
insertData.push(String(imagePath));
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
}).catch((error) => {
reject(error.message );
});
} else {
endQuery = `UPDATE Users SET ${query} WHERE user_id = ?;`;
insertData.push(req.params.id);
resolve([endQuery, insertData]);
}
});
return promise;
};
Now I can just use my route.
app.put('/api/v1/users/:id', image.multerMiddleware.single('image'), (req, res) => {
if (((Object.keys(req.body).length !== 0) || req.file) && !isNaN(req.params.id)) {
updateParams(req.body, [Validate.password, Validate.email, Validate.lastname, Validate.firstname, Validate.phoneNumber, Validate.permissionID], (insertData, query) => {
updateUser(req, res, insertData, query)
.then((result) => {
connection.query(result[0], result[1], (err: MysqlError | null) => {
if (!err) {
res.status(200);
res.json({ "Message": "Successfull user was updated" });
} else {
res.status(500);
res.json({ "Database Error ": err.message });
}
});
}).catch((error) => {
res.status(500);
res.json({ "Error ": error.message });
});
});
} else {
res.status(400);
res.json({ "Error": "Please provide the correct paramaters" });
}
});
So now
The user can update only some params
The user can update some params and his picture
The user can update only his picture
It work's fine now.
What I do for when someone is editing a user (or other type of data) is that I retrieve the entire data for the user and show it on the editing form. Then when they make the updates, I send all the data up. This way when I do the SQL update, it will re-save the unchanged data as well as the changed data.
Your other option is a series of conditionals which add to the update statement based off what fields are sent in to update.
You either set only those values that were provided, or, if you really insist on updating all columns (why not the PK while you're at it) you qould query them first.