Login Authentication with bcrypt and SQLite3 - javascript

i am currently building a React application with Nodejs/Express Backend and try to implement a Login Authentication.
i register Users with Name, email, password and hash the password with bcrypt:
router.post('/register', async (req, res) => {
// Hashing
const salt = await bcrypt.genSalt(10)
const hashedPassword = await bcrypt.hash(req.body.regpassword, salt)
// Validate
const {error} = Joi.validate(req.body, schema)
var data = {
regname: req.body.regname,
regemail: req.body.regemail,
regpassword : hashedPassword
}
var sql ='INSERT INTO Users (regname, regemail, regpassword) VALUES (?,?,?)'
var params =[data.regname, data.regemail, data.regpassword]
db.run(sql, params, function (err, result) {
if (error){
res.status(400).send(error.details[0].message);
return;
}
res.json({
"answer": "Success",
})
res.status(200)
});
})
this works fine. But my /login route doesnt work:
router.post('/login', (req, res, next) => {
let sql = `SELECT * FROM Users WHERE regname = "${req.body.regname}" AND regpassword = "${req.body.regpassword}"`;
var x;
db.all(sql, (err, rows) => {
if (err) {
next(err);
return;
}
if (!rows) {
res.status(400);
res.send('Invalid username or password');
return
}
rows.forEach( async (row) => {
if (row.regname === req.body.regname && await bcrypt.compare(req.body.regpassword, row.regpassword) ) {
x = 1;
}
else {
x = 2;
db.close();
}
})
if (x === 1) {
res.json({
"answer":"Success",
})
}
else {
res.json(
{"answer":"Denied",
})
}
})
})

The salt needs to be stored in the database as well.
The /login route must then retrieve the regpassword and the salt from the database based on the req.body.regname. It then needs to run a await bcrypt.hash(req.body.regpassword, salt) exactly identical to the /register route and then compare the result of that hashing operation with the regpassword from the database. If the two hashes match then the user provided the correct password and you can display some confirmation / issue some session token / ...

i share my solution here, if someone needs it:
router.post('/login', (req, res) => {
const regname = req.body.regname;
const regpassword = req.body.regpassword;
const findUserByName = (regname, cb) => {
return db.get(`SELECT * FROM Users WHERE regname = ?`,[regname], (err, row) => {
cb(err, row)
});
}
findUserByName(regname, (err, user)=>{
if (err) return res.status(500).send('Server error!');
if (!user) return res.status(404).send('User not found!');
const result = bcrypt.compareSync(regpassword, user.regpassword);
if(!result) return res.status(401).send('Password not valid!');
res.status(200)
res.json({
"answer":"Success",
})
});
});

Related

Login Page, want to check for existing users

For my registration page, I want to check for existing users before adding a new user to avoid duplicate usernames. For some reason, after I check if an existing username exists in the database, it does not run any of the code afterwards.
app.post("/register", (req, res) => {
const username = req.body.username;
const password = req.body.password;
var newuser = false;
db.query("SELECT * FROM users WHERE username = ?;", [username], (err, result) => {
if (err) {
res.send({err: err})
}
if (result.length == 0) {
newuser = true;
console.log(newuser + "one")
}
})
if (newuser) {
console.log(newuser + "two")
bcrypt.hash(password, saltRounds, (err, hash) => {
if (err)
console.log(err)
db.query(
"INSERT INTO users (username, password) VALUES (?,?)",
[username, hash],
(err, result) => {
if (err)
console.log(err);
}
)
})
}
})
In this example, the only output I get is "trueone" ... meaning that it evaluated result.length to 0 and set newuser = true.
It is because you are not waiting to the result of the first select. You can use async function or move the if(new user) condition into the select callback.

Node.js constant is not waiting for response from async function

I am new to async/await and I'm trying to set a 'user'-constant as the return value of a MySQL query in Node.js. However, the constant does not wait for the return value of the function. How do I use async and await to set 'user' to the return value of the SQL query?
// Should return a user object
const getUserByUsername = async (username, db) => {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const user = await db.query(QUERY,
async (err, result) => {
if (!err) {
console.log("name in SQL function: " + result[0].username);
return await result[0];
} else {
console.log(err);
}
}
);
return user;
};
// Does stuff with the user object
const authenticateUser = async (username, password, done) => {
const user = await getUserByUsername(username, db);
console.log("name in main function: " + user.username);
// Trying to do stuff with the user object...
}
What I get in the terminal:
name in main function: undefined
UnhandledPromiseRejectionWarning: Error: data and hash arguments required
at Object.compare
at /app/node_modules/bcrypt/promises.js:29:12
at new Promise (<anonymous>)
at Object.module.exports.promise
etc.....
name in SQL function: john
When you use db.query with a callback, it does not return a promise
Try the following code instead
const getUserByUsername = async (username, db) => {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const result = await db.query(QUERY);
console.log("name in SQL function: " + result[0].username);
return result[0];
};
Please try the below code.
const getUserByUsername = async (username, db) => {
try {
const QUERY = `SELECT * FROM ${table_name} WHERE username = '${username}'`;
const user = await db.query(QUERY, async (err, result) => {
if (err) {
throw err;
}
if (result && result.length) {
return result[0];
}
throw new Error(`User with username ${username} not found`);
});
console.log(`name in SQL function: ${user.username}`);
return user;
} catch (error) {
console.log(error);
throw error;
}
};
After jfriend00's comment, I decided to make my function a callback function. I found this answer explaining how to do it: https://stackoverflow.com/a/31875808/6817961. This worked!
My code now:
// Function returns a user object
const getUserByUsername = (username, callback) => {
const QUERY = `SELECT * FROM ${db_name} WHERE username = '${username}'`;
db.query(QUERY, (err, result) => {
if (!err) {
if (result.length > 0) {
return callback(null, result[0]);
} else {
err = "No user with that username";
return callback(err, null);
}
} else {
return callback(err, null);
}
});
};
// Then this function does stuff with the callback
const authenticateUser = (username, password, done) => {
getUserByUsername(username, async (err, result) => {
if (err) {
console.log(err);
return done(null, false, { message: err });
} else {
const user = result;
// result will now return the user object!

How do I retrieve an encrypted password from my database with bcrypt?

I have an application with a register and login form. I've managed to get the encrypted password into the database, but i can't seem to get it to work when I want to compare it when logging in. How would I implement bcrypt in my login post method?
Here is my register post where the password is stored successfully:
router.post('/register', (req, res) => {
bcrypt.hash(req.body.password, 10).then((hash) => {
let userData = req.body;
let user = new User(userData);
user.password = hash;
user.save((error, registeredUser) => {
if (error) {
console.log(error);
} else {
let payload = {subject: registeredUser._id};
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({token});
}
});
});
});
And here is my login post:
router.post('/login', (req, res) => {
let userData = req.body;
User.findOne({email: userData.email}, (error, user) => {
if (error) {
console.log(error);
} else {
if(!user) {
res.status(401).send('Invalid Email');
} else
if (user.password !== userData.password) {
res.status(401).send('Invalid Password');
} else {
let payload = {subject: user._id};
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({token});
}
}
});
});
Few things to note here.
Password are usually encrypted using one-way hashing function, which means you shouldn't be expecting to decrypt the saved password back to original text.
In a one way hash function, same hash (encryption output) is created for same input, every time. Eg: If you can encrypt the word "mysimplepassword", the output is going to be the same "xjjklqjlj34309dskjle4" (just a sample) every time.
The method of checking password in such scenarios is:
(a) Store the encrypted password (hash) when its first provided (usually during sign up)
(b) During login, receive the password as input and encrypt it using same encryption method, to obtain the hash
(c) Compare the hash
If you are using bcrypt, you can use bcrypt.compare() to perform these operations
I figured it out and am now comparing the hashed passwords successfully. Here is the new login post:
router.post('/login', (req, res) => {
let userData = req.body;
User.findOne({email: userData.email}, (error, user) => {
if (error) {
console.log(error);
} else {
if(!user) {
res.status(401).send('Invalid Email');
} else {
bcrypt.compare(req.body.password, user.password, function (err, result) {
if (result == false) {
res.status(401).send('Invalid Password');
} else {
let payload = {subject: user._id};
let token = jwt.sign(payload, 'secretKey');
res.status(200).send({token});
}
});
}}
});
});

cannot compare password with bcrypt compare

Im trying to build a node api for change password,
User must type the currentPassword and the new password
when bcrypt.compare the new currentPassword with the stored on db, i got always false, whatever it's wrong or correct
const changePass = async (req, res, next) => {
//email and password
const CurrentPassword = req.body.currPassword
let password1 = ''+req.body.password1
let password2 = ''+req.body.password2
const hashedPassword = await bcrypt.hash(password1, 10);
let id = "" + req.body.id
User.findById( id )
.then(user => {
bcrypt.compare(CurrentPassword, user.password, (err, data) => {
if (err) throw err
if (data) {
User.findByIdAndUpdate(id, {password : hashedPassword }, {new: false}, (err) => {
if (err) throw err
})
} else {
return res.status(401).json({ msg: "Invalid" })
}
})
})
}
If you want to learn bcrypt I recommend you to visit bcrypt NPM because it will save you too much time later,
in your case I made some modification on your code in order to check for the current password OLD and then compare between the newPassword1 and the confirmation passwordConfirmation
feel free to use console.log('') when you have doubts about anything it will give you a good vision about your code status
const changePassword = async (req, res, next) => {
let id = req.body.nid;
if(id){
console.log('Im here')
const old = req.body.old;
const newP = req.body.newP;
const newP2 = req.body.newP2;
User.findById(id,(err,user)=>{
if(user){
console.log(user)
const hash = user.password;
bcrypt.compare(old,hash,function (err,res){
if(res){
if(newP === newP2){
bcrypt.hash(newP,10, (err,hash)=>{
user.password = hash;
user.save( (err,user) =>{
if(err) return console.error(err);
console.log(user.userName +' your password has been changed');
});
});
};
};
});
}
})
}
}

Getting result from MySQL

My backend is consist of Api and DB. When I want to get response from DB I have had delayed output by 1 query.
API (I think api is ok. Start read DB first)
app.post('/api/query', (req, res) => {
console.log(`\n Query input : ${JSON.stringify(req.body)}`);
let queryInput = (Object.values(req.body).join(' '));
if(!dbApi.checkArray(queryInput)){ //If array is not made from clear strings
res.json(dbApi.queryFromUser(queryInput));
}
else{
res.json(dbApi.queryOutput);
}
});
app.listen(dbConfig.server.port, () =>
console.log(`Server running on port ${dbConfig.server.port}`));
DB
queryOutput = [];
const receivingQuery =(queryInput) => {
db.query(queryInput, (err, result) =>{
if(err) throw err+' : '+queryInput;
queryOutput = result;
console.log("\nQuery output "+ JSON.stringify(queryOutput)); //Output (result) is ok
});
return queryOutput //Here is Output from previous query (sends to API)
}
module.exports = {
queryOutput: queryOutput,
queryFromUser: receivingQuery,
}
I tryied callback method and I rewrite it couple of times. But I dont have enough skill to solve it.
If You want to return result of query so simply do following things:
add query method to db module:
function query(sql, args = []) {
return new Promise(function(resolve, reject) {
db.query(sql, args, (err, result) => {
if (err) return reject(err);
resolve(result);
});
});
}
// extra feature, getting user by id
async function getUserById(id) {
const result = await query('SELECT * FROM users WHER id = ? LIMIT 1', [id]);
if (Array.isArray(result) && result[0]) return result[0];
return null;
}
module.exports = {
query,
getUserById, // export user by id
queryOutput,
queryFromUser: receivingQuery,
}
use it (with async and await):
app.post('/api/query', async (req, res) => {
try {
console.log('Query input:', req.body);
const queryInput = Object.values(req.body).join(' ');
const result = await dbApi.query(queryInput);
res.json(result);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.get('/api/users/:id', async (req, res) => {
try {
const user = await dbApi.getUserById(req.params.id);
if (!user) return res.status(404).json({message: 'User not found'});
res.status(200).json(user);
}
catch (error) {
console.error(error);
res.status(500).json({message: 'Please try again soon'});
}
});
app.listen(dbConfig.server.port, () =>
console.log('Server running on port', dbConfig.server.port));

Categories

Resources