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")
}
Related
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.
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) => {
I'm currently trying to build a firebase cloud function (using express) where:
- I check if a use exists in database
- Sends a message to the telegram API depending on whether it exists or not
The issue is, when I try to run the function, Firebase logs is able to get the console.log telling me if the user exists, but will not send to telegram. The error log says:
[2020-02-15T10:41:34.568Z] #firebase/database: FIREBASE WARNING:
Exception was thrown by user callback. Error: Can't set headers after
they are sent.
at validateHeader (_http_outgoing.js:491:11)
at ServerResponse.setHeader (_http_outgoing.js:498:3)
at ServerResponse.header (/srv/node_modules/express/lib/response.js:771:10)
at ServerResponse.send (/srv/node_modules/express/lib/response.js:170:12)
at ServerResponse.json (/srv/node_modules/express/lib/response.js:267:15)
at ServerResponse.send (/srv/node_modules/express/lib/response.js:158:21)
at firebase.database.ref.child.once.snapshot (/srv/index.js:59:40)
at onceCallback (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:4933:51)
at /srv/node_modules/#firebase/database/dist/index.node.cjs.js:4549:22
at exceptionGuard (/srv/node_modules/#firebase/database/dist/index.node.cjs.js:698:9)
Could anyone please help? Thank you!
app.post("/", async (req, res) => {
const isTelegramMessage =
req.body &&
req.body.message &&
req.body.message.chat &&
req.body.message.chat.id &&
req.body.message.from &&
req.body.message.from.first_name &&
req.body.update_id;
const user_id = req.body.message.from.id
firebase.initializeApp(firebaseConfig);
let myUser;
const chat_id = req.body.message.chat.id;
const {
first_name
} = req.body.message.from;
// Check User Exists
firebase
.database()
.ref("/telegramUsers")
.child(req.body.message.from.id)
.once("value", snapshot => {
if (snapshot.exists()) {
myUser = true;
console.log("exists!", myUser);
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Welcome Back ${first_name}`
});
} else {
myUser = false;
console.log("does not exist!");
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Hello ${first_name}`
});
}
});
return res.status(200).send({
status: "not a telegram message"
});
});
As others have commented, you're returning and writing a response to the caller twice. Since send starts writing the body of the HTTP response, you can't call status (or even send) after you've already called it on res before.
In code that'd look something like this:
app.post("/", async (req, res) => {
const isTelegramMessage =
req.body &&
req.body.message &&
req.body.message.chat &&
req.body.message.chat.id &&
req.body.message.from &&
req.body.message.from.first_name &&
req.body.update_id;
const user_id = req.body.message.from.id
firebase.initializeApp(firebaseConfig);
let myUser;
const chat_id = req.body.message.chat.id;
const {
first_name
} = req.body.message.from;
// Check User Exists
if (isTelegramMessage) {
return firebase
.database()
.ref("/telegramUsers")
.child(req.body.message.from.id)
.once("value")
.then(snapshot => {
if (snapshot.exists()) {
myUser = true;
console.log("exists!", myUser);
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Welcome Back ${first_name}`
});
} else {
myUser = false;
console.log("does not exist!");
return res.status(200).send({
method: "sendMessage",
chat_id,
text: `Hello ${first_name}`
});
}
});
} else {
return res.status(200).send({
status: "not a telegram message"
});
}
});
The changes:
Now only checks if the user exists if isTelegramMessage is true.
Now returns the result from the database read operation.
Use once().then(), so that the return res.status().send()... bubbles up. This ensures that Cloud Functions will not terminate the function before both the database load and the sending of the response are done.
Though the second and third bullets are not strictly needed for HTTPS triggered Cloud Functions (as those terminate when you send a response), I still recommend to use them, to make it easier to port/copy-paste the code to Cloud Functions types that are triggered by other events.
I have a basic login system that does the following:
Checks if the username exists (goes OK)
Inserts the username
After the insert, I select the id of the inserted username. This goes correct on the first login, but when I logout and login with a new username, the insert is too slow, causing my select to throw an exception, because the insert is not done yet. How can I fix this?
My code:
Database.js:
getPlayerByName (username) {
return this.knex("penguins").first("*").where("username", username)
}
getPlayerExistsByName (username) {
return this.knex("penguins").where("username", username).select("username")
}
insertPlayer (username) {
return this.knex("penguins").insert({username: username}).then(() => {
Logger.info(`Inserted ${username}`)
}).catch((err) => {
Logger.error(err)
})
}
Login.js:
function handlePlayer (username, res) {
database.getPlayerExistsByName(username).then(result => {
if (result.length != 1) { // Username does not exist
database.insertPlayer(username) // Insert username
database.getPlayerByName(username).then(penguin => {
return write(`&id=${penguin.id}&m=${penguin.mod}${Errors.OK}`, res) // I need the id here
})
} else {
return write(Errors.NAME_UNAVAILABLE, res)
}
})
}
Try to use async funcion in the callback of then and use await in insert and get user to do this 2 methods synchronous
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)
}
}