I wrote this code in node.js to verify username and password from a json of users. problem is it keeps looping through all the users although a valid user is already found. How do I break the loop when a valid user is found?
users.forEach(function (user) {
if(req.body.username==user.username){
if(req.body.password==user.password){
console.log('validate success');
res.status(200).send({message:'user validation successful'});
}
}
else{
console.log('validate failed');
res.status(404).send({message:'user validation failed'});
}
});
What you want is to find the user matching the username and if you find one, check the password. This way, you can also provide the right error message to tell the user what's wrong (username or password)
const user = users.find(function (user) {
return user.username === req.body.username
})
if (!user) {
return res.status(404).send({ message: 'User not found' })
}
if (user.password !== req.body.password) {
return res.status(404).send({ message: 'Wrong pasword' })
}
// everything is alright
return res.status(200).send({message:'user validation successful'})
You can use arrow function to make the find shorter
const user = users.find(u => (u.username === req.body.username))
You are using the wrong function. You use forEach if you want to loop over all the entries. If you need to find a subset, you use filter, and if you need only one result, you use find : https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/find
So your code, with some other improvements, will become :
const loggedUser = users.find((user) => {
return req.body.username === user.username && req.body.password === user.password
})
I know I could have shorten this even more, but it is clearer as it is right now IMO.
Related
I am using PostgreSQL for the first time with an express server and am running into an error. On my register user route I am trying to check if the username or email already exists, because they need to be unique. What keeps happening is, say I pass in a username that is already in the database then the first return will run and return that the username is already in use. But what is happening is it is returning the username is already in use and it still running the rest of the code so it trying to return multiple json responses.
module.exports.register = async (req, res, next) => {
try {
const { username, email, password } = req.body;
postgres
.query("SELECT * FROM users WHERE username = $1", [username])
.then((data) => {
if (data.rows.length > 0) {
return res.status(409).json({
msg: "Username is already in use",
status: false,
});
}
})
.catch((err) => {
console.log(err);
});
postgres
.query("SELECT * FROM users WHERE email = $1", [email])
.then((data) => {
if (data.rows.length > 0) {
return res.status(409).json({
msg: "Email is already in use",
status: false,
});
}
})
.catch((err) => {
console.log(err);
});
const hashedPassword = await bcrypt.hash(password, 10);
postgres.query(
"INSERT INTO users (username, email, password) VALUES ($1,$2,$3) RETURNING *",
[username, email, hashedPassword],
(err, data) => {
if (err) {
console.log(err.stack);
} else {
return res.json({ user: data.rows, status: true });
}
}
);
} catch (err) {
next(err);
}
};
I can't figure out why the rest of the code is running even though I am returning res.json. If anybody has any suggestions/solutions I would really appreciate it!
The return in front of the res.status(409) is returning you out of the then of the postgres.query function instead of the full register function. As a result it jumps out of the then and runs the rest of the code from there, so it's still hashing the password and attempting an insert into the users table (which hopefully fails on a unique index).
In order to fix this you can either 1) Define a variable before the function, change it if something was found and then do a return outside of the then statement if the variable was changed 2) perform all the rest of the code in the then statement (since you're returning out of that it will not be run) or 3) use awaits instead and throw/next+return/res.json+return an the HTTP 409 error.
Option 3 will take the most effort but you should definitely learn to use this route as soon as possible as it makes writing async code a lot easier (plus you'll avoid getting a bunch of nasty nested then statement). You could try out using option 1 and 2 just to get a feel for how the flow of the express code works.
I tried to get a document using document.findOne() but it's value is showing undefined .
Here is my code
`app.post("/studentlogin",(req,res)=>
{
let password;
console.log("login page");
bcrypt.hash(req.body.password,saltRounds,(err,hash)=>
{
const user= Student.findOne({srno:req.body.srno});
console.log(user.srno);
if(req.body.srno==user.srno && hash==user.password)
{
session=req.username;
session.userid=req.body.srno;
res.redirect("/");
}
else{
console.log("invalid user");
res.redirect("/studentlogin");
}
});
})`
I'm implementing session authentication using express-session. Here when I log the user it's showing schema and bunch of other stuff which I don't know(The error is too long) . user.srno is also showing as undefined. How can I fix it?
I tried using call-back function which gave me required document correctly. But I want the query to return the correct document and store it in user.
Using callback function
`app.post("/studentlogin",(req,res)=>
{
let password;
console.log("login page");
bcrypt.hash(req.body.password,saltRounds,(err,hash)=>
{
Student.findOne({srno:req.body.srno},(err,result)=>
{
console.log(result);
});
//console.log(user.srno);
if(req.body.srno==user.srno && hash==user.password)
{
session=req.username;
session.userid=req.body.srno;
res.redirect("/");
}
else{
console.log("invalid user");
res.redirect("/studentlogin");
}
});
})`
You need to wait the result from your query on the database before doing the next task like comparing your password, and looks like you just try to log in, you re not going to register a new one, so it's better to use the compare method in Bcrypt like this :
app.post("/studentlogin", async (req , res) => {
const {srno, password} = req.body // destructuring your request is better for visibility
try {
const user = await Student.findOne({srno: srno});//await the result before next step
console.log(user.srno) //you can check
if(!user) {
console.log("invalid user");
// your logic to tell not student found /wrong username or password, and/or redirect
}
const isMatch = await bcrypt.compare(password, user.password) //await the result and this method for comparing the request password and the user's password found
if(!isMatch) {
//your code to tell Wrong username or password
res.redirect("/studentlogin");
} else {
// your code to access to the login.
res.redirect("/");
}
} catch (err) {
console.log(err)
}
The error you are facing is because you are not using async await in your code. use async in your function definition then use await where you are searching the database.
const someFunction = async(req,res)=>{
// your code
const user=await Student.findOne({srno:req.body.srno});
// your code
}
Your code should look like this.
app.post("/studentlogin", async(req,res)=> {
// your code
const user=await Student.findOne({srno:req.body.srno});
// your code
}
console.log(user) to verify.
Hope it helps.
there are two ways to check login, both of them are using query once
let where = {
username
};
//check username first
let result = await UserModel.findOne(where)
if (result != null) {
if (result.password === password) {
return done(null, result)
} else {
return done(null, false, 'wrong password')
}
} else {
return done(null, false, 'user does not exist')
}
and
let where = {
username,
password
};
//check username and password
let result = await UserModel.findOne(where)
if (result != null) {
return done(null, result)
} else {
return done(null, false, 'wrong password or username')
}
Which one is better and why? Thank you.
Ask yourself this question.
let query = {
matchValue: "someValue",
someValue: { $gt: 1 },
someOtherValue: { $gte: 1 }
}
const results = await model.findOne(query)
if (results) // do something
vs
let query = {
matchValue: "someValue"
}
const results = await model.findOne(query);
if (results.someValue > 1 && results.someValue >== 1){
... do something.
}
If checks can be done by mongodb (more performant), why do it in javascript?
Other than performance, both of your logics work the same way, and returns the same results.
Whatever the result may be (invalid username OR password), the response to frontend should always be "Invalid username / password"
Well, according to me you should first ask for username and if that matches the database then we should ask for password. If you ask a user for username, you should validate that user as soon as possible. Otherwise, users may make a typo in their contact info and then spend considerable time using your service only to find there is no account matching their info the next time they attempt login. These accounts are often orphaned and unrecoverable without manual intervention. Worse still, the contact info may belong to someone else, handing full control of the account to a third party.
I am relatively new to NodeJS and have run into an issue. Incoming request from Angluar UI has two parameters (loginId and password) and on Node, I am reading the data from a local json file, iterating through the 'users' array and based on the matching params, I am sending the particular object in the server response. However, my logic is failing where there are multiple objects in the 'users' array and I have to send a server response for the not-matched one as well. I know that I cannot send multiple responses but that is where i need the help. Any help will be highly appreciated.
Here is the code:
if (jsonData.users.length > 0) {
jsonData.users.forEach(user => {
if ((reqBody.loginId === user.email || reqBody.loginId === user.mobile) && reqBody.password === user.password) {
res.send({ 'userData': user });
} else {
res.json({ 'userData': 'No records found' });
}
})
res.send ends the response.
So using it a second time it will not send anything.
https://www.nodejsconnect.com/blog/articles/sending-multiple-http-responses-expressjs
EDIT:
I'm not completely sure what you want, but I think you want to do something like this.
-> return the user for whom the userId and password matches or return 'no records found' ( or even better send an 401 - unauthorized - status code)
const user = jsonData.users.find(user => (reqBody.loginId === user.email || reqBody.loginId === user.mobile) && reqBody.password === user.password));
if (user) {
res.send({ 'userData': user });
} else {
res.json({ 'userData': 'No records found' });
// or res.sendStatus(401); // unauth
}
Try this... If you wanna break the loop after a match.
if (jsonData.users.length > 0) {
let isMatch = false;
jsonData.users.forEach(user => {
if ((reqBody.loginId === user.email || reqBody.loginId === user.mobile) && reqBody.password === user.password) {
isMatch = true;
return res.send({ 'userData': user });
}
})
if(!isMatch)
res.json({ 'userData': 'No records found' });
}
I am trying to determine if a username exists before creating the user. I am using the following code. I need an elegant way to determine if the zero row is returned ...username doesn't exist. for example I know the returned row value would be zero if not found. How can I get access to the row value in the code. Can someone assist...thanks...BTW I am using neo4j3.0 Nodejs with express and Passport
neo4jSession
.run(MATCH (user {email: newUser.email}) RETURN user);
.then (function(result) {
if ((not found) {
.run(CREATE (user: {email:newUser.email, password:newUser.password} ) ASSERT email is UNIQUE RETURN user);
neo4jSession.close();
}) //end of if not found
else (found)
{
// email address already exist
console.log("email address already exist");
neo4jSession.close();
}
}); //end .then
.catch(function(error) {
console.log(error);
});
The Neo4j Driver for JavaScript record module can check if a value from record exists by index or field key using the has method. When evaluating or validating the existence of any field within a record (e.g. determining if a User node contains an existing email address property), using the has instead of the get method can allow for shorter Cypher statements and condensed javascript code; which (IMO) can lead to elegance you are seeking.
Using your original example, you can use a simple Cypher statement to search if a User node contains an email property by passing in a user's input. Utilizing the Neo4j Driver for JavaScript, you can return a result stream with a single record.
Cypher Statement:
MATCH ( u:User { email: $email } )
RETURN u, u.email
If an email address exists as a User node property in the Neo4j database, a stream of records with one field named "u.email" be will returned . The record represents one user found by the statement above. You can access the field value by key using the record module's has method.
Access Record by Field Key:
result.records[0].has('u.email')
The following example is one of many ways you could implement both the simple Cypher statement and has method:
async (_, { email, password }) => {
const session = await driver.session()
const closeSession = await session.close()
const endSession = await driver.close()
let query = 'MATCH (u:User{email: $email}) RETURN u, u.email'
return session
.run(query, { email })
.then(async result => {
closeSession()
let emailExists = result.records[0].has('u.email')
let newUser = result.records[0].get('u').properties
if (
(Array.isArray(result.records) && !result.records.length) ||
(Object.keys(result).length === 0 && result.constructor === Object)
) {
if (!emailExists) {
let query =
'MERGE (u:User { email: $email }) ON CREATE SET u.password = $password RETURN u'
return session
.run(query, { email, password })
.then(result => {
closeSession()
return newUser
})
} else if (Array.isArray(result.records) && result.records.length) {
const emailExists = result.records[0].has('u.email')
if (emailExists) {
closeSession()
throw new Error(emailExists + ' already exists.')
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
} else {
closeSession()
endSession()
throw new Error('Internal Server Error')
}
})
.catch(function(err) {
closeSession()
endSession()
if (err) throw err
})
}
Note: This example validates whether a record exists first by evaluating the result with conditional statements, then the e-mail property is checked. A few errors have been handled as well.
Query (use counter):
MATCH (user {email: newUser.email})
RETURN count(user)=1 as user_exists
In javascript:
if ( result.records[0].get('user_exists') !== true ) {
// create new user
}
And, of course, add a unique constraint to the email address for the user.
Though the answer from #stdob is an accepted answer on further research I found out that in case the record do exist, you can't retrieve any data on that query... so:
MATCH (user {email: newUser.email})
RETURN user.name AS Name, count(user)=1 as user_exists
wont yield any data if user exist. The following works:
MATCH (user {email: newUser.email}) RETURN user;
if (!result[0]) {
//no records found
}
else {get user properties}
Thanks to:
https://github.com/mfong/node-neo4j-passport-template/blob/master/models/user.js