My Login System in Node.js is not working - javascript

I'm using Node.js and MySQL for a login system. My problem is, login system is not working when I use async-await and promises. It's not giving errors but page keeps reloading and user cannot enter the system. What can the problem be? Is there a problem in my await declarations. I'm kind of new at fullstack programming. Thanks for your help.
At the top of the login controller, there is a async arrow function and below its code block, there are multiple awaits which are defined for some database calls and bcrypting password.
Should I return the database connection as .promise()
or not a promise? Sometimes code is working and user can enter, but sometimes cannot.
This Code Block For User Login
exports.login = async (req, res) => {
try {
const { email, password } = req.body;
// if (!email || !password) {
// return res.status(400).render("giriş_yap", {
// msg: "Lütfen e-mail veya şifrenizi giriniz",
// msg_type: "error",
// });
// }
const allDB = await login_db.query(`select * from users where email='${email}'`);
const user = allDB[0] ;
//console.log(user[0]) ;
if (user.length <= 0) {
return res.status(401).render("giriş_yap", {
msg: "Sistemde kaydınız bulunamadı",
msg_type: "error",
});
} else {
const passwordMatch = (await bcrypt.compare(password, user[0].PASS))
if (!passwordMatch) {
return res.status(401).render("giriş_yap", {
msg: "Emailiniz veya Şifreniz hatalı",
msg_type: "error",
});
} else {
const id = user[0].ID;
const token =jwt.sign({ id: 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
),
httpOnly: true,
};
res.cookie("joes", token, cookieOptions);
res.status(200).redirect("/anasayfa");
}
}
} catch (error) {
console.log(error);
}
};
This Code Block for mysql database connection
const mySql = require("mysql2");
const config = require("../config_db");
const login_db = mySql.createConnection(config.db_login);
login_db.connect(function (err) {
if (err) {
console.log(err);
}
console.log("Kullanici bilgileri veritabanina başariyla baglandiniz.");
});
module.exports = login_db.promise();

Are you sure you didn't forget in your front-end this code ?
e.preventDefault()
If not, please share the front-end code also.
Also in your code, be careful with your console.log in the catch block on your login function. It can throw to infinite api call.

Related

How to handle 401 error status code error in Node.js/Express?

I am working on login functionality in my project, now, flow looks like this (from front-end to back-end):
async login() {
await login({
password: this.userPassword,
login: this.userLogin,
twoFactor: this.twoFactor
}).then((res) => {
if (res.error) {
//
} else {
console.log(res)
}
})
}
And here is starts problems, as you can see if something goes wrong, I return status code 401 and some error message. When I login with correct data, there is no problem with getting token, but when I provide wrong data I have external pending login endpoint in development tools in browser and then, after some time, Error: Request failed with status code 401 in front end terminal. Without this status(401) with just JSON it works fine, but when I try to add 401 code, application crashes.
const userService = require('./../services/userService')
const crypto = require('./../services/cryptoService')
const jwt = require('./../services/jwtService')
const twoFactorService = require('node-2fa')
module.exports = {
login: async (req, res) => {
let { login, password, twoFactor } = req.body
password = crypto.encrypt(password, process.env.APP_KEY)
const result = await userService.getUserToLogin(login, password)
if (!result) {
res.status(401).json({
error: 'Unauthorized'
})
} else {
const faCode = result.twofatoken
const result2F = twoFactorService.verifyToken(faCode, twoFactor);
if ( !result2F || result2F.delta !== 0 ) {
res.status(401).json({
error: 'Unauthorized'
})
} else {
const userId = crypto.encrypt(result.id, process.env.CRYPTO_KEY)
const token = await jwt.sign({
uxd: userId,
});
res.json(token);
}
}
}
}
Actually, I have no idea on what to do with that and how to handle this error.
Ok, here is the answer. Actually, you just need to handle this error in your router:
router.post('/login', async (req, res) => {
try {
const data = await api.post('/login', req.body)
res.json(data.data)
} catch (e) {
// Probably you have here just console.log(e), but this way, you can handle it
res.status(e.response.status).json(e.response.data)
}
})

nodejs mongoose - how to check items in the database before login

In my project, I've different roles (seller/user/admin)and i want to check the role and redirect to specific page if they are seller for example.
I struggle on how i can check the role in Mongo DB before the login. My login page is basic email-password and submit button.
for my signup all is good, it's use the correct model and post it in the DB.
here are some pieces of my code:
(client model)
userSchema.statics.login = async function (email, password, role) {
const user = await this.findOne({ email });
if (user) {
const auth = await bcrypt.compare(password, user.password);
if (auth) {
return user;
}
throw Error("incorrect password");
}
throw Error("incorrect email");
};
const ClientModel = mongoose.model("client", userSchema, "users");
login controller:
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
res.redirect('/success');
} catch (err) {
console.log(err.message);
}
};
thanks in advance for your help, if you need more info please feel free to ask
Following #EAzevedo 's advice.
i just change my Controller
module.exports.clientSignIn = async (req, res) => {
const { email, password } = req.body;
try {
const user = await LoginModel.login(email, password);
const token = createToken(user._id);
res.cookie("jwt", token, { httpOnly: true, maxAge });
if (user.role == "client") {
res.redirect("/success");
} else if (user.role == "technicien") {
res.redirect("/success-technicien");
} else if (user.role == "superuser") {
res.redirect("/success-admin");
};
} catch (err) {
const errors = signInErrors(err);
res.status(200).json({ errors });
}
};
when you get the user , you should have field for the role ,
then check which role logged in and redirect him to where he needs to be

Using async/await correctly in the context of token validation

I'm building an Express.js backend for a website validating users using Google Sign-in.
And, I'm trying to create a RESTful API call which:
Takes in an IDtoken.
Validates that token using Google's OAuth2 Library.
Checks to see if a user is in a MongoDB Collection and then:
Either sends back the access token if true, or returns nothing if false.
This is what I have so far...
The post request:
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', (req, res) => {
const idToken = req.body.tokenID;
const accessToken = req.body.accessToken;
const client = new OAuth2Client(keys.google.clientID);
const session = verify(idToken, accessToken, client).catch(console.error);
console.log('Session: ', session)
return res.send(session);
});
verify() :
//verify token
async function verify(idToken, accessToken, client) {
const ticket = await client.verifyIdToken({
idToken: idToken,
audience: keys.google.clientID,
});
const payload = ticket.getPayload();
const email = payload['email'];
if (findUser(email)) {
return {
email: email,
accessToken: accessToken
};
} else {
return null;
}
}
findUser() :
//find user
function findUser(email) {
User.find({email: email}, (error, user) => {
if(error) {
console.log(error);
return false;
} else if (user.length === 0) {
console.log('this user is not in the database');
return false;
} else {
console.log('this user is in the database');
return true;
}
});
}
For an output regardless of whether the user is in the DB or not I always get:
Session: Promise { <pending> }
this user is in the database
or
Session: Promise { <pending> }
this user is not in the database
I know that since verify() is an async function I need to get it to resolve its promise before assigning it to a variable, but I'm at a loss for how to do it correctly, and unfortunately, I can't make verify() into a synchronous function as then it does not create the ticket object correctly before calling getPayload() from Google's Library.
Any ideas for how to make all of this happen in the correct order?
Thank you.
Could you please try:
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', async (req, res) => {
const idToken = req.body.tokenID;
const accessToken = req.body.accessToken;
const client = new OAuth2Client(keys.google.clientID);
const session = await verify(idToken, accessToken, client).catch(console.error);
console.log('Session: ', session)
return res.send(session);
});
Update -
Find User:
//find user
async function findUser(email) {
const user = await User.find({email: email}, (error, user) => {
if(error) {
console.log(error);
return false;
} else if (user.length === 0) {
console.log('this user is not in the database');
return false;
} else {
console.log('this user is in the database');
return true;
}
});
return user;
}

How do I correctly await the return of an async function in javascript?

I'm currently working on a Google Sign-in Auth app with a React frontend and an Express backend and I'm currently stuck in the part of the process where I'm validating tokens on the backend. The docs for this process show this code to validate the token:
const {OAuth2Client} = require('google-auth-library');
...
const client = new OAuth2Client(CLIENT_ID);
async function verify() {
const ticket = await client.verifyIdToken({
idToken: token,
audience: CLIENT_ID, // Specify the CLIENT_ID of the app that accesses the backend
// Or, if multiple clients access the backend:
//[CLIENT_ID_1, CLIENT_ID_2, CLIENT_ID_3]
});
const payload = ticket.getPayload();
const userid = payload['sub'];
// If request specified a G Suite domain:
//const domain = payload['hd'];
}
verify().catch(console.error);
I've implemented this code in my own project here:
//verify token
async function verify(token, client) {
const ticket = await client.verifyIdToken({
idToken: token,
audience: keys.google.clientID,
});
const payload = ticket.getPayload();
const userid = payload['sub'];
const domain = payload['hd'];
const email = payload['email']
console.log('User ID: ' + userid);
console.log('Domian: ' + domain);
console.log('Email: ' + email);
var message = '';
var cookie = {};
await User.find({email: email}, (error, user) => {
if(error) {
message = error;
} else if (user.length === 0) {
message = 'this user is not in the database';
} else {
message = 'this user is in the database';
const session = new Session({
email: email,
session_token: token
});
cookie = {
email: email,
session_token: token
};
session.save((error, session) => {
if (error) {
console.log(error);
} else {
console.log('session saved');
}
});
console.log(message);
}
});
return Promise.resolve(cookie);
}
//recieve token id from frontend, verify it, and send session back in response
router.post('/google', (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
let cookie = verify(body, client).catch(console.error);
console.log('Cookie:' + cookie);
return res.send(cookie);
});
Currently when this runs everything inside the async function executes, but the return statement only returns the empty promise object. I think I'm making a mistake using async and await incorrectly, but I don't know how to correctly get the function to wait for all the work verifying the token and then update the DB before returning.
Not sure if this will help, but when I call the route my console gives me this output:
(I took out my personal info from the output fields, but assume these lines actually have gmail account info)
...
Cookie:[object Promise]
User ID: <GOOGLE ID>
Domian: <DOMAIN>
Email: <USER EMAIL>
this user is in the database
session saved
Thanks for reading!
Since "verify" function is an async function, you should add "await" before calling it. For catching errors you can simply place it in a try/catch:
router.post('/google', async (req, res) => {
const body = req.body.tokenID;
const client = new OAuth2Client(keys.google.clientID);
try {
let cookie = await verify(body, client);
console.log('Cookie:' + cookie);
return res.send(cookie);
} catch(error) {
// handling error
console.log(error);
return res.send("error")
}
});
`
You're mixing async/await with callback based calls. I don't know the internals of the library you're using, but the pattern should look more like this:
var cookie = {};
try{
const user = await User.find({email: email});
if (user.length === 0) {
console.log('this user is not in the database');
}
else {
console.log('this user is in the database');
const session = new Session({
email: email,
session_token: token
});
try{
await session.save();
console.log('session saved');
} catch(err){
console.log(err);
}
return {
email: email,
session_token: token
};
} catch(error){
console.log(error);
}

Using async in app startup script not returning any results

I am trying to run the following script in my Node app to check if any users exist and if not, create first admin user. Yet the script simply do nothing, return nothing even while using Try/Catch so can someone please tell me what I am missing / doing wrong here? or how I can possibly catch the error (if any)? Thanks
import pmongo from 'promised-mongo';
import crypto from 'crypto';
const salt = 'DuCDuUR8yvttLU7Cc4';
const MONGODB_URI = 'mongodb://localhost:27017/mydb';
const db = pmongo(MONGODB_URI, {
authMechanism: 'ScramSHA1'
}, ['users']);
async function firstRunCheckAndCreateSuperAdmin(cb) {
const username = 'admin2#test2.com';
try {
const user = await db.users.findOne({ role: 'admin'});
console.log(user);
if(!user) return cb('No user found');
} catch(e) {
cb('Unexpected error occurred');
}
if(!user) {
console.log('No admin detected.');
const adminPassword = crypto.pbkdf2Sync ( 'password', salt, 10000, 512, 'sha512' ).toString ( 'hex' );
await db.users.update({username: username}, {$set: {username: username, password: adminPassword, role: 'admin'}}, {upsert: true});
}
db.close();
process.exit();
}
firstRunCheckAndCreateSuperAdmin(function(err, resultA){
if(err) console.log(err);
});
You are not returning any callback when there is no admin user in the following code snippet
if (!user) {
console.log('No admin detected.');
const adminPassword = crypto.pbkdf2Sync ( 'password', salt, 10000, 512, 'sha512' ).toString ( 'hex' );
await db.users.update({username: username}, {$set: {username: username, password: adminPassword, role: 'admin'}}, {upsert: true});
// call cb(user) here
}
Please see comment.
import pmongo from 'promised-mongo';
import crypto from 'crypto';
const salt = 'DuCDuUR8yvttLU7Cc4';
const MONGODB_URI = 'mongodb://localhost:27017/mydb';
const db = pmongo(MONGODB_URI, {
authMechanism: 'ScramSHA1'
}, ['users']);
async function firstRunCheckAndCreateSuperAdmin(cb) {
const username = 'admin2#test2.com';
try {
const user = await db.users.findOne({
role: 'admin'
});
console.log(user);
//(1) If user is undefined, then launch cb with an error message;
if (!user) return cb('No user found');
} catch (e) {
//(2) If something is wrong, then launch cb with an error message;
cb('Unexpected error occurred');
}
//This part of the code will only be reached if user is defined.
//This is a dead code as if user is undefined, it would have exited at (1)
if (!user) {
console.log('No admin detected.');
const adminPassword = crypto.pbkdf2Sync('password', salt, 10000, 512, 'sha512').toString('hex');
await db.users.update({
username: username
}, {
$set: {
username: username,
password: adminPassword,
role: 'admin'
}
}, {
upsert: true
});
}
//So if user exists, it will close db and exit without calling cb.
db.close();
process.exit();
}
firstRunCheckAndCreateSuperAdmin(function(err, resultA) {
if (err) console.log(err);
});
Note:
If you are using async/await, then you don't need to use callback.
If you are using callback, then you don't need to have a return statement.
If the intention of the function is suppose to have a return value, make sure all code path returns a value.
I have tried to rewrite your code to make it smaller and to remove all node-style callback types of async code from it. I replaced update with insertOne since you only have one user to insert (not multiple to update). Also I have added 500 ms timeout when calling firstRunCheckAndCreateSuperAdmin in case it "hangs". It should log something at the end :)
import pmongo from 'promised-mongo'
import crypto from 'crypto'
import {
promisify
} from 'util'
const pbkdf2 = promisify(crypto.pbkdf2)
const salt = 'DuCDuUR8yvttLU7Cc4'
const MONGODB_URI = 'mongodb://localhost:27017/mydb'
const db = pmongo(MONGODB_URI, {
authMechanism: 'ScramSHA1'
}, ['users']);
const username = 'admin2#test2.com'
async function firstRunCheckAndCreateSuperAdmin() {
let user = await db.users.findOne({
role: 'admin'
});
if (!user) { // no user lets create one
user = await db.users.insertOne({
username: username,
password: (await pbkdf2('password', salt, 10000, 512, 'sha512')).toString('HEX'),
role: 'admin'
});
}
return user
}
const timeout = delay => message => new Promise((_, reject) => setTimeout(reject, delay, new Error(message)))
Promise
.race([firstRunCheckAndCreateSuperAdmin(), timeout(500)('Rejected due to timeout')])
.then(user => console.log(`Got user ${JSON.stringify(user)}`))
.catch(error => console.error(error))

Categories

Resources