Check logged in user - javascript

I am trying to write a custom middleware to log a user into my application.
For this I am using the below function:
async function login(username, password) {
try {
const pwdHash = await bcrypt.hash(password, saltRounds)
const res = await knex("users").where({
username: username,
password: pwdHash
}).first()
console.log("res: " + res) //here I am getting the error
if (res.password == pwdHash) {
return true
} else {
return false
}
} catch (e) {
console.log(e)
}
}
However, I am getting the following error message back:
TypeError: Cannot read property '0' of undefined
at Object.login (C:\Users\user\project\service\user.js:56:34)
at <anonymous>
I know that I cannot access the res object immediately, but shouldntawaitget an object back. However, I am gettingundefined` back.
Any suggestions what I am doing wrong?
Appreciate your replies!

I found this, https://github.com/mysqljs/mysql/issues/910#issuecomment-55536265.
what I think is happening here is that your query is returning an empty array and the .first() method is doing something with the [0] item which is undefined in that case, so that's why you are getting that error Cannot read property '0' of undefined.
You can improve your code doing something like this:
async function login(username, password) {
try {
const pwdHash = await bcrypt.hash(password, saltRounds)
const res = await knex("users").where({
username: username,
password: pwdHash
});
// res should be an empty array here if there is no match
// Just check if the query gives you some result
// so, if the array has some item inside then it means
// that you found a valid user with that `username` and `password`
if (res.length > 0) {
return true
} else {
return false
}
} catch (e) {
console.log(e)
}
}

Related

Trying to send over routine data with api

I am building a fitness tracker through a class, it gives me built in tests to use as well. I am having an issue with passing this one in specific. I shortened the test specs for convenience.
Expected[{"activities": [{"activityId": 3,
Received {"publicRoutines": [{"activities": [{"activityId": 3,
1. Gets a list of public routines for a particular user.
2. Gets a list of all routines for the logged in user
I understand that the publicRoutines are sent in the res.send() but without the curly brackets, it sends over a failed test that is in my catch. Is there a way to send over both of these functions in my code to match the expected result?
usersRouter.get(`/:username/routines`,async(req,res) =>{
const username = req.params.username
try{
if(username){
const userRoutines = await getAllRoutinesByUser({username});
const publicRoutines = await getPublicRoutinesByUser({username})
console.log(publicRoutines, userRoutines)
res.send({publicRoutines, userRoutines})
}else{
return null;
}
}catch(error){
throw Error('Failed to get', error)
}
})
Yes, you can modify your code to send the expected result format by combining the two objects into a single object:
usersRouter.get(`/:username/routines`, async (req, res) => {
const username = req.params.username;
try {
if (username) {
const userRoutines = await getAllRoutinesByUser({ username });
const publicRoutines = await getPublicRoutinesByUser({ username });
console.log(publicRoutines, userRoutines);
res.send({ activities: [...publicRoutines.activities, ...userRoutines.activities] });
} else {
return null;
}
} catch (error) {
throw Error("Failed to get", error);
}
});
This way, you are combining the arrays of activities from both publicRoutines and userRoutines and returning it in the format that the test is expecting.

Node js I can't return a query using mysql

When I try to return a query, we get an undefined object, I don't know why, I need to return a value to the main function but always is undefined, I need to return that information to validate a user in the database, when I do the same function in main was working, but when I try to separate the functions can't work.
class login{
validateUser(user, pass){
const connection = require ('./db');
let datos = connection.query('SELECT * FROM login WHERE usuario = ?', [user], async(error, results)=>{
if (results.length == 0 || pass != results[0].password){
return null;
}
else{
return results;
}
});
}
}
module.exports = login
I need to return values in this function to validate login:
app.post('/auth', async (req, res)=>{
const user = req.body.user;
const pass = req.body.pass;
const log = new login();
const response = log.validateUser(user, pass);
console.log(response);
if(!response){
res.render('login',{
alert:true,
alertTitle: "Error",
alertMessage: "Usuario y/o Contraseña incorrecta",
alertIcon : "error",
showConfirmButton: true,
timer: null,
ruta: 'login'
});
}
else{
req.session.loggedin=true;
req.session.name = response[0].usuario;
res.render('login',{
alert:true,
alertTitle: "Conexión exitosa",
alertMessage: "!Login exitoso¡",
alertIcon : "success",
showConfirmButton: false,
timer: 2000,
ruta: ''
});
}
});
I think the problem is in these lines
1. const response = log.validateUser(user, pass);
2. console.log(response);
This code is running Asynchronously so line 2 is running before the completion of line 1 that is why you are getting the response as undefined.
I suggest you use Async await for this if your database client supports it or make your custom Promise such that you code do
//note the await keyword before log
1. const response = await log.validateUser(user, pass);
2. console.log(response);
This will make sure that line 2 or any code below only executes once you have fetched and returned your data properly
P.S: Above fix won't work until unless your fix the implementation of validateUser to support Async Await via Promise

Keep getting invalid credentials when login node.js + Express.js

I am fairly new to node.js and trying to get my login credentials from the database, I can successfully get login credentials from the database but when I call it from my server.js file, I keep getting Invalid credentials even though they are the right credentials. I will appreciate it if fresh eyes could look into this and point out what I am doing wrong. Using Postgres DB
app.post('/api/login', async (req, res) => {
try {
const loginCredentials = {
email: req.body.email,
password: req.body.password
}
if (loginCredentials.email === '' && loginCredentials.password === '') {
return res.status(500).send('Email and Password required')
}
if (loginCredentials.email === '' || loginCredentials.password === '') {
return res.status(500).send('Email and Password required')
}
if (loginCredentials.email && loginCredentials.password ) {
// getting credentials from db.login
const result = await db.login(loginCredentials.email, loginCredentials.password)
console.log('* ' + result)
if (result) {
res.status(200).send("Success")
} else {
res.status(401).send("Invalid Credentials")
}
}
} catch (e) {
throw e
}
and here is my db.login method
async function login(email, password) {
console.debug("login db")
const query = `SELECT *
FROM users
WHERE email = $1`
const client = await pool.connect()
try {
await client.query(query, [email], async function (err, result) {
if (err) {
throw err
}
console.debug('hash: ' + result.rows[0].hash)
console.debug('pass: ' + password)
if (result.rows.length > 0) {
const user = result.rows[0]
//role = user.role
const validPassword = bcrypt.compare(password, user.hash)
if (!validPassword){
console.log('Invalid Credentials')
return 'Invalid Credentials'
}else {
console.debug('success')
return user
}
} else {
return 'No user in the DB'
}
})
} catch (e) {
throw e
}
}
You are not awaiting the compare method's result, so validPassword will always be an unresolved Promise, not the result.
Adding await should fix it:
const validPassword = await bcrypt.compare(password, user.hash)
EDIT: I see you check with if (!validPassword), which is never "truthy" if validPassword is a Promise. You might want to check what version of bcrypt you are using - does compare return a Promise, or is this maybe an old version that uses a callback signature only. In that case you should either update your bcrypt dependency, or use the callback method instead: compare(password, user.hash, function(err, validPassword) { /* check validPassword here */ }).
You might also want to check your flow, you return the user from the client.query callback, but you are also awaiting it, but not storing or returning its return value. I have no experience with the Postgres API, but it looks like you are mixing Promises (with async/await) and callbacks. In my experience it's always one or the other - you either use the callback flow, or returned Promise flow. If client.query returns a Promise, it will probably resolve with the results you are now handling with the callback.
Try rewriting it this way:
async function login(email, password) {
console.debug("login db")
const query = `SELECT *
FROM users
WHERE email = $1`
const client = await pool.connect()
try {
const result = await client.query(query, [email])
console.debug('hash: ' + result.rows[0].hash)
console.debug('pass: ' + password)
if (result.rows.length > 0) {
const user = result.rows[0]
//role = user.role
const validPassword = await bcrypt.compare(password, user.hash)
if (!validPassword){
console.log('Invalid Credentials')
return 'Invalid Credentials'
}else {
console.debug('success')
return user
}
} else {
return 'No user in the DB'
}
} catch (e) {
throw e
}
}
Another edit: Notice that the code above will now actually return something, either the user row or a string. Previously, your db.login method returned a Promise that resolved without a value, always failing your following if (result) check because result is undefined. If you change your db.login code to the above, the if (result) will now always be true because it is either a string or the user object. You might want to throw 'Invalid credentials' and throw 'No user in the DB' instead, and wrap the db.login(..) call in try / catch:
try {
const result = await db.login(loginCredentials.email, loginCredentials.password)
res.status(200).send("Success")
}
catch(err) {
res.status(401).send("Invalid Credentials")
}

Separating Mongoose code from Express Router

So basically, I'm trying to separate my code that handles data (mongoose) from my express Router code, since I might want to use it elsewhere too.
The first thing I did was, I got rid of the res.json() calls, since I don't want the code to only work returning a http response. I want it to return data, so I can then return that data from my router as a http response, but still use it as regular data elsewhere.
Here is a function I wrote to get data from mongoose.
module.exports.user_login = data => {
console.log(data);
ModelUser.findOne({email: data.email}).then(user => {
if(!user){
console.log({email: 'E-mail address not found'});
return {
status: response_code.HTTP_404,
response: {email: 'E-mail address not found'}
}
}
bcrypt.compare(data.password, user.password).then(isMatch => {
if(!isMatch){
console.log({password: 'Invalid password'});
return {
status: response_code.HTTP_400,
response: {password: 'Invalid password'}
}
}
const payload = {
id: user.id,
email: user.email
};
jwt.sign(
payload,
config.PASSPORT_SECRET,
{
expiresIn: "1h"
},
(err, token) => {
console.log({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
return {
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
}
}
);
});
});
};
When this code gets executed in my route like so:
router.post("/login", (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
console.log("ret", dm_user.user_login(req.body));
});
The log says the return value of user_login() is undefined, even though right before the return statement in user_login() I am logging the exact same values and they are getting logged.
Before I changed it to a log, I tried to store the return value in a variable, but obviously that remained undefined as well, and I got the error: 'Cannot read propery 'status' of undefined' when trying to use the value.
I am definitely missing something..
Well you have an small callback hell here. It might be a good idea to go with async / await and splitting up your code into smaller chunks instead of putting everyhing in 1 file.
I rewrote your user_login function:
const { generateToken } = require("./token.js");
module.exports.user_login = async data => {
let user = await ModelUser.findOne({ email: data.email });
if (!user) {
console.log({ email: "E-mail address not found" });
return {
status: response_code.HTTP_404,
response: { email: "E-mail address not found" }
};
}
let isMatch = await bcrypt.compare(data.password, user.password);
if (!isMatch) {
console.log({ password: "Invalid password" });
return {
status: response_code.HTTP_400,
response: { password: "Invalid password" }
};
}
const payload = {
id: user.id,
email: user.email
};
let response = await generateToken(
payload,
config.PASSPORT_SECRET,
response_code
);
return response;
};
I have moved your token signing method into another file and promisfied it:
module.exports.generateToken = (payload, secret, response_code) => {
return new Promise((res, rej) => {
jwt.sign(
payload,
secret,
{
expiresIn: "1h"
},
(err, token) => {
if (err) {
rej(err);
}
res({
status: response_code.HTTP_200,
response: {
success: true,
token: token
}
});
}
);
});
};
Now you need to change your router function into an async:
router.post("/login", async (req, res) => {
const { errors, isValid } = validateLogin(req.body);
if(!isValid) return res.status(400).json(errors);
let result = await dm_user.user_login(req.body);
console.log(result);
});
In addition: You get undefined because you return your value to an callback function
I also would seperate your routes from your controllers instead of writing your code inside an anonymous function
Please notice that whenever you are trying to return any value you are always present in the callback function and that is definitely not going to return any value to its intended place.
There are a couple of things you can improve about your code :
1.Donot use jwt inside your code where you are making database calls, instead move it where your routes are defined or make a separate file.
2.If you are intending to re-use the code, I would suggest you either use async-await as shown in the answer above by Ifaruki or you can use something like async.js. But the above shown approach is better.
Also always use 'error' field when you are making db calls like this:
ModelUser.findOne({email: data.email}).then((error,user) => {

toObject is not a function error while converting mongoose object

I have a route '/login' which has controller to verify user is valid. For now, i am just verifying password entered from route params equals to password i stored in my DB.
I have few methods to get job done for me.
findUser() - verifies user entered mandatory fields or not and resolves Mongoose object when data is found
ValidatePassword() - It is chained to findUser() and converts mongoose object to JS object
In validatePassword method, i want to delete unwanted fields like password before sending data to client. As a result i am converting mongoose object to javascript object and performing delete operation.
Problem: Whenever i am converting in to JS object, i am getting 'toObject() not defined' error.
Attaching snippets for above controller methods.
Please suggest any changes!
let findUser = (req, res) => {
return new Promise((resolve, reject) => {
if (req.body.email) {
userModel.find({ email: req.body.email }, (error, userDetails) => {
if (error) {
let response = apiResponse.generate(
true,
"Unable to reach out to server",
500,
null
);
reject(response);
} else if (checkLib.isEmpty(userDetails)) {
let response = apiResponse.generate(
true,
"Unable to reach out to server",
500,
null
);
reject(response);
} else {
resolve(userDetails);
}
});
} else {
let response = apiResponse.generate(
true,
"Please provide emailID and password",
404,
null
);
reject(response);
}
});
};
This method retrieves userDetails which i am chaining in validatePassword()
let validatePassword = (retrievedUserDetails) => {
return new Promise((resolve, reject) => {
console.log("passw:" + retrievedUserDetails);
if (req.body.password) {
console.log(retrievedUserDetails.password);
console.log(req.body.password);
if (retrievedUserDetails[0].password != req.body.password) {
let response = apiResponse.generate(
true,
"Password or email is invalid",
404,
null
);
reject(response);
} else {
let retrievedUserDetailsObj = retrievedUserDetails.toObject();
delete retrievedUserDetailsObj.password;
let response = apiResponse.generate(
false,
"Signed in successfully",
200,
retrievedUserDetails
);
resolve(response);
}
} else {
let response = apiResponse.generate(
true,
"Please provide password",
404,
null
);
reject(response);
}
});
}
Chaining:
findUser(req, res)
.then(validatePassword)
.then((resolve) => {
let response = apiResponse.generate(
false,
"user is signed in successfully",
200,
resolve
);
res.send(response);
})
.catch((err) => {
console.log(err);
res.send(err);
});
};
It seems you're using the .find method of the model and not the .findOne method. The first one will always return an array of documents that match the query while the second one will return the first object that matches. What you're basically trying to do is [{something}].toObject() and that is indeed undefined.
Use findOne, instead of find because .toObject works on a single object, not an array.
If you want to run .toObject on the array, just loop the array and run .toObject on single object of the array in the loop.

Categories