I'm trying to implement login method where I'm searing the user by his/her email then checking for the password verification.
So, if the email is invalid then throwing an invalid email response and with password, I'm doing the same, but the bycrpt.compare gives me a false when I compare the candidatePassword and the user stored Password. I have checked the password from the database(MongoDB) too, it is correct also tried printing the password and the hashedPassword to the console to see I'm getting the data and it does prints. So, came to a conclusion that there is some error with the bcrypt.compare method.
Can you guys please help me if I'm hashing it wrong or my method for checking the password is wrong?
Any help is appreciated.
User_model:
userSchema.methods.correctPassword = async (candidatePassword, userPassword) => {
console.log(candidatePassword, "user=>", userPassword)
return await bcrypt.compare(candidatePassword, userPassword);
};
export const user = mongoose.model('user', userSchema);
Login-File:
export const loginUser = async (req, res, next) => {
try {
const { email, passcode } = req.body;
// 1) Check if email and password is empty
if (!email || !passcode)
return res.status(400).send('Please provide email and password!');
// 2) Check if user exists && password is correct
const user = await UserModule.findOne({ email }).select('+passcode');
console.log("user=>", passcode, "\n", "hashed=>", user.passcode, "\n", await user.correctPassword(passcode, user.passcode))
if (!user || !(await user.correctPassword(passcode, user.passcode)))
return res.send("Email or Password is invalid.")
res.send(message: "Logged in Successfully");
}
catch (err) {
res.send(err.message)
}
}
Output
user=> vandor1passcode
hashed=> $2b$12$LdpTufKRc2qXiWh2YOfNUO9f4QnNI/jfT4Hq9/.GJ2O7cTWjFugoy
false
So, I found an answer to this problem.
Basically, my hashed password was getting hashed properly with a salt of size 12, and when I was comparing it to my existing stored password it was comparing with size salt of 10. That was the problem why I was getting false in result even though my password was correct.
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.
I'm now trying to build the user authentication logic of a website. Now I should check, when a user tries to log in, does he have a registered email/password already. But I cannot just simply get the field's value from the DB, as a string.
In the database, I have 6 fields (isSuperuser, fullName, institution, password, email, approved). For the authentication, the unfinished method is here:
router.post("/login", async (req, res, next)=> {
const email = req.body.email;
const data = await client.query(`SELECT * FROM user_table WHERE email= $1;`, [email])
const arr = data.rows
//const arr = data.rows;
.then(user=> {
if(arr==null || arr== undefined || arr.length==0){
return res.status(401).json({
message: "Login authentication failed!"
})
}
})
})
My problem is, that, when I check, if the email, provided at login, already exists, I should simply compare it (req.body.email) with the value(s) of the email field in the database. But I did not find any solution, how to just get a string, as a result for the query. The const arr gives an array, from which I cannot get the value of the email key. So far, this code might works (though ugly as hell), but when it comes to password compare, I will bleed.
If I try
client.query("SELECT password FROM user_table WHERE email= $1;", [email])
it only gives back an empty array.
What do I do wrong? Any solution, please?
Iam not sure about PostgreSql but I know sql so give it a try;
router.post("/login", async (req, res, next) => {
const { email } = req.body;
const data = await client.query(`SELECT * FROM user_table WHERE email= ${email}`).then((result) => {
return result
})
if (!data) { // OR if data is an array then use "data.length == 0"
return res.status(401).json({
success: false,
message: "Login authentication failed!"
})
}
})
I am aware that it is related to; setting the header a second time doesn't work, but my app is crashing after sending the first response without even sending another request.
When the app crashes the console indicates to check the catch block of the code.
My major concern is why is the execution entering the catch block when there is no error in the execution, because I only want to check if the user exists, and from what I understand the result should be same irrespective of the number of times we check. If I try to add a user that already exists this is correctly working and I am getting the output:
if (user.rows.length !== 0) {
res.status(400).json("User already exists");
}
then why is my catch block being called after sending the json? I have tried changing the response status code and changing the to res.status(401).send("User already exists"); also gives the same error, but once I only change the status and send no message the server works fine with res.status(401), why is my catch block being invoked after sending the response with json/send??
What is a better approach I can incorporate here?
This is the code:
try {
// extract data from request body
const { user_name, user_email, user_password } = req.body;
// check if user already exists
const user = await pool.query("SELECT * FROM users WHERE user_email = $1", [user_email]);
// console.log(typeof user); returns object
// console.log(user.rows);
if (user.rows.length !== 0) {
res.status(400).json("User already exists");
}
// bcrypt the user password
const saltRound = 10; // Try randomising the salt rounds
const salt = await bcrypt.genSalt(saltRound);
const bcryptPassword = await bcrypt.hash(user_password, salt); // encrypted password
// insert user into database
const newUser = await pool.query("Insert INTO users (user_name, user_email, user_password) VALUES ($1, $2, $3) RETURNING *", [user_name, user_email, bcryptPassword]);
res.json(newUser.rows[0]);
} catch (error) {
console.error(error.message);
res.status(500).send("Server Error");
}
Error in console:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent
to the client
You are getting this error because you didn't check the else block. after checking the if statement it goes down and try to execute the rest of the code. your catch block isn't executed cause if it is you'll get 500. Try executing the code like below-
if (user.rows.length !== 0) {
res.status(400).json("User already exists");
}else{
// bcrypt the user password
const saltRound = 10; // Try randomising the salt rounds
const salt = await bcrypt.genSalt(saltRound);
const bcryptPassword = await bcrypt.hash(user_password, salt); // encrypted password
// insert user into database
const newUser = await pool.query("Insert INTO users (user_name, user_email, user_password) VALUES ($1, $2, $3) RETURNING *", [user_name, user_email, bcryptPassword]);
res.json(newUser.rows[0]);
}
You can't execute both responses at once. Hope this will help you handle your problem.
As per your code you are handling the if block with wrong approach. In NodeJs as it is async in nature, It will never terminated as you expected. add return in the if block.
if (user.rows.length !== 0) {
return res.status(400).json("User already exists");
}
It will return the response and break the execution of further code. As per your code it is executing the new user creation code and try to create the new user which is already present where the sql throws the error and it goes to the catch block. And agin try to send the new error response which gives you the ERR_HTTP_HEADERS_SEND.
As per the best practices always handle the error cases 1st so it will not create the if-else ladder.
I have a set of server side functions that 1.
Generates a random salt
Uses password and salt to create a hash
Invokes the second function to create hashed password that is returned
var sha512 = function(password, salt){
var hash = crypto.createHmac('sha512', salt); /** Hashing algorithm sha512 */
hash.update(password);
var value = hash.digest('hex');
return {
salt:salt,
passwordHash:value
};
};
//--------------Function to hash password
function saltHashPassword(userpassword) {
var salt = genRandomString(16); /** Gives us salt of length 16 */
var passwordData = sha512(userpassword, salt);
console.log('UserPassword = '+userpassword);
console.log('Passwordhash = '+passwordData.passwordHash);
console.log('\nSalt = '+passwordData.salt);
return passwordData;
}
//----------------------
// Function to generate salt
var genRandomString = function(length){
return crypto.randomBytes(Math.ceil(length/2))
.toString('hex') /** convert to hexadecimal format */
.slice(0,length); /** return required number of characters */
};
Whenever I get a username and password in the sign up page, I can call saltHashPassword(passwordinstring) and get a hash and a salt. I then save this to the user document in the mongoose schema.
How do I decrypt when the user is trying to login?
This is a method I'm currently writing in the User schema, user.js. I'm stuck with how get the salt stored for the email provided.
// this authenticates the user against the database
UserSchema.statics.authenticate = function(email, password, callback){
User.findOne({email: email})
.exec(function(error, user){
if(error){
return callback(error);
}else if(!user){
var err = new Error("User not found");
err.status = 401;
return callback(err);
}
// if we reach this part of the code, then there is a valid username
// I could call sha512(password, salt) and get the hash and then
// match the two calculated hashed passwords. But how do
// I query the salt within a mongo query?
// every user has email, hashed_password, salt fields in its document
});
};
Looks like you are trying to reinvent the wheel. You could use bcrypt which wouldn't need you to store salt separately.
But since you already started with this:
// this authenticates the user against the database
UserSchema.statics.authenticate = function(email, password, callback){
User.findOne({email: email})
.exec(function(error, user){
if(error){
return callback(error);
}
if(!user){
var err = new Error("User not found");
err.status = 401;
return callback(err);
}
// if we reach this part of the code, then there is a valid username
// I could call sha512(password, salt) and get the hash and then
// match the two calculated hashed passwords. But how do
// I query the salt within a mongo query?
// every user has email, hashed_password, salt fields in its document
var salt = user.salt; // Because you said that User has salt, email, hashed_password
// Do your password matching....
});
};
Mongoose's find and findOne methods return either the error or the document. You already have error and user in your callback. You can access the properties by user.email, user.salt, etc.
You don't need any separate queries, since the User contains the salt.