Authentication Error (nodejs, mongodb, jwt) - node.js

I have this code for the login, and it doesn't authenticate the user, it gives me back the error response I wrote. I tried changing it so the user would stop giving back "null", but nothing is functioning. This is the code:
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username
});
!user && res.status(401).json("Wrong Credentials");
const hashedPassword = CryptoJS.AES.decrypt(
user.password,
process.env.PASS_SEC
);
const OriginalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
OriginalPassword !== req.body.password &&
res.status(401).json("Wrong Password!");
const accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin,
}, process.env.JWT_SEC,
{expiresIn:"3d"}
);
const { password, ...others } = user._doc;
res.status(200).json(...others, accessToken);
} catch (err) {
res.status(500).json(err);
}
});
what it gives back is the "Wrong Credentials" string I put; I console logged the req.body.username and it gives me back my username, but the const user just keeps giving back null.
I hope I can find some answer, I'm losing hope already

const { username, password } = req.body;
try {
const getUser=user.findOne({username:username})
if (!getUser) {
return res.json({err:"User Doesn't Exists"})
} else {
const hashedPassword = CryptoJS.AES.decrypt(
getUser.password,
process.env.PASS_SEC
);
const OriginalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
if (user.password === OriginalPassword) {
const accessToken = jwt.sign({
id: user._id,
isAdmin: user.isAdmin,
}, process.env.JWT_SEC, {
expiresIn: "3d"
});
const { password, ...others } = user._doc;
res.status(200).json(...others, accessToken,{ message: "Logged in
Successfully"});
} else {
return res.json({ err: "Password Doesn't Match" })
}
}
} catch(err) {
return res.json(err)
}

Related

Cannot set headers to pass token in node js

node js
This is my register method to register a user. I am trying to pass token in headers when a user is registered which will be used in the front end to access the token and store it in the local storage.
module.exports.register = async function (req, res, next) {
try {
const { username, email, password } = req.body;
const profileImage = req.file.path;
const usernameCheck = await User.findOne({ username });
if (usernameCheck)
return res.json({ msg: "Username already used", status: false });
const emailCheck = await User.findOne({ email });
if (emailCheck)
return res.json({ msg: "Email already exists", status: false });
const hashedPassword = await bcrypt.hash(password, 10);
const user = await User.create({
_id: new mongoose.Types.ObjectId(),
username,
email,
profileImage,
password: hashedPassword,
});
delete user.password;
//create jwt token
const token = jwt.sign(
{
username: user.username,
email: user.email,
userId: user._id,
},
process.env.JWT_KEY,
{
expiresIn: "1h",
}
);
res.header("x-auth-token", token); //This is not setting the token in headers
return res.json({
message: "User Created Successfully",
status: true,
user,
});
} catch (ex) {
next(ex);
}
};
react js
This is my front-end react code to register a user. I want to login the user with the jwt token stored in localStorage once the user is registered.
const handleSubmit = async (values) => {
try {
const { username, email, profileImage, password } = values;
const formData = new FormData();
formData.append("username", username);
formData.append("email", email);
formData.append("profileImage", profileImage);
formData.append("password", password);
const response = await register(formData);
console.log(response);
if (response.status === false) return;
else {
loginWithJwt(response.headers["x-auth-token"]);// log the user in using jwt token
console.log(response.headers["x-auth-token"]);
navigate("/chatroom");
}
} catch (ex) {
console.log(ex.message);
}
};

ExpressJS Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client

initially successful for the hit request, but the second time failed. is there something wrong in my code? thank you
My error:
Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client
My Code =
router.post("/login", async (req, res) => {
try {
const user = await User.findOne({ username: req.body.username });
!user && res.status(401).json("Wrong credentials!");
const hashedPassword = CryptoJS.AES.decrypt(
user.password,
process.env.PASS_SEC
);
const OriginalPassword = hashedPassword.toString(CryptoJS.enc.Utf8);
OriginalPassword !== req.body.password &&
res.status(401).json("Wrong credentials!");
const accessToken = jwt.sign(
{
id: user._id,
isAdmin: user.isAdmin,
},
process.env.JWT_SEC,
{ expiresIn: "3d" }
);
const { password, ...others } = user._doc;
res.status(200).json({...others, accessToken});
} catch (err) {
res.status(500).json(err);
}
})
First spread the values into your object then update the values
const { ...others, password } = user._doc

Mongoose return "new ObjectId("//id")" instead of just the id

I am trying to do my login function (I am using bcrypt and jsonwebtoken) the problem is that console.log (user._id) returns me "new ObjectId (" 6148f043ebbaa0ab41ac8499 ")" instead of just "6148f043ebbaa0ab41ac8499" , which would be easier for the creation of the token.
module.exports.login = async (req, res) => {
const { email, password } = req.body;
// Compare the req.body.password to the hashed password in DB
const user = await UserModel.findOne({ email: email });
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
const user = await UserModel.findOne({ email: email });
console.log(user._id);
// Assign a token
const token = jwt.sign({ userId: user._id }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user: user._id });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(500).json({ message: "error!!!" });
}
};
How to fix this please?
That is a normal behaviour. Since you got an ObjectId, you can convert it to a string by calling the toHexString() method on it. I have also modified the code to check for an undefined user, and removed the extra call to find a user since you already did in the previous line. Please see updated code:
module.exports.login = async (req, res) => {
const { email, password } = req.body;
const user = await UserModel.findOne({ email: email });
if (!user) {
return res.status(400).json({ message: "Unauthorised"});
}
// Compare the req.body.password to the hashed password in DB
const match = await bcrypt.compare(password, user.password);
if (match) {
try {
// Convert user id (ObjectId) to a string
const userId = user._id.toHexString();
// Now user id is a string
console.log(userId);
// Assign a token
const token = jwt.sign({ userId }, process.env.LOGIN_TOKEN, {
expiresIn: "1h",
});
console.log(token);
res.cookie("jwt", token, { httpOnly: true});
res.status(200).json({ user });
} catch (err) {
res.status(500).json(err);
}
} else {
res.status(400).json({ message: "Unauthorised" });
}
};
use the following
const id = _id.toHexString();

How to display errors with Axios in React

Good day,
i am running to a slight problem with Axios, i want to check if the status code is ok, if not then i send the error to the client. How can i do that ?
This is my attempt to do it, but i had no success:
Route code:
router.post("/register", async (req, res) => {
try {
//check if fields are empty
const { name, email, password, rePassword } = req.body;
if (!name || !email || !password || !rePassword) {
return res.status(406).send("fields are empty");
}
//passsword length should be at leats 6 characters
if (password.length < 6) {
return res
.status(406)
.send("Password should at least be 6 characters long");
}
//checking if passwords match
if (password !== rePassword) {
return res.status(406).send("Passwords dont match");
}
//check if user exists
const emailExist = await User.findOne({ email: req.body.email });
if (emailExist) {
return res.status(406).send("User already exists");
} else {
//create salt
const salt = await genSalt(10);
//hashing the password
const hashedPassword = await hash(req.body.password, salt);
const user = new User({
name: req.body.name,
email: req.body.email,
password: hashedPassword,
isAdmin: req.body.isAdmin,
});
const newUser = await user.save();
res.status(200).send(newUser);
}
} catch (err) {
console.log(err);
}
});
Page code:
const handleSubmit = async e => {
e.preventDefault();
axios
.post("http://localhost:5000/users/register", {
name,
email,
password,
rePassword,
})
.then(response => {
console.log(response.data);
})
.catch(err => {
console.log(err);
});
}
The response will give you the following options
console.log(response.data);
console.log(response.status);
console.log(response.statusText);
console.log(response.headers);
console.log(response.config);
This will allow you to check for valid stausText/codes, or display errors accordingly.

ExpressJS: Can't set headers after they are sent

I have an API in ExpressJS. Within that API I have a login endpoint, when posting to that endpoint however I keep getting the exception that headers cannot be set after they have been sent.
I understand this is normally a callback that is being called twice or not properly returning from something that has set headers, causing the app to attempt to set them again, however in my /login endpoint I am not doing this.
I cannot understand why this happening, I would love some input as to why as I am close to pulling my hair out reading the same replies and answers. I hope it is something obvious I am missing.
import User from '../../models/user';
import { Router } from 'express';
import jwt from 'jsonwebtoken';
export default () => {
const route = Router();
route.post('/create', async (req, res, next) => {
if (!req.body.email || !req.body.password) {
return res
.status(400)
.json({ message: 'username or password is missing' });
}
const { email, password } = req.body;
const count = await User.count({ email });
if (count > 0) {
return res.status(409).json({ message: 'email must be unique' });
}
const newUser = await new User({ email, password });
const doc = await newUser.save();
return res.status(201).json({ type: 'account', attributes: doc });
});
route.post('/login', async (req, res, next) => {
if (req.body.email && req.body.password) {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user) {
user.comparePassword(password, isMatch => {
if (isMatch) {
const token = jwt.sign(
{ sub: user.id, roles: [], email: user.email },
process.env.SECRET_KEY,
{ expiresIn: '12h' },
);
return res
.status(200)
.json({ type: 'account', attributes: { token } });
}
});
}
}
res.sendStatus(401);
});
return route;
};
import User from '../../models/user';
import { Router } from 'express';
import jwt from 'jsonwebtoken';
export default () => {
const route = Router();
route.post('/create', async (req, res, next) => {
if (!req.body.email || !req.body.password) {
return res
.status(400)
.json({ message: 'username or password is missing' });
}
const { email, password } = req.body;
const count = await User.count({ email });
if (count > 0) {
return res.status(409).json({ message: 'email must be unique' });
}
const newUser = await new User({ email, password });
const doc = await newUser.save();
return res.status(201).json({ type: 'account', attributes: doc });
});
route.post('/login', async (req, res, next) => {
if (req.body.email && req.body.password) {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user) {
return user.comparePassword(password, isMatch => {
if (isMatch) {
const token = jwt.sign(
{ sub: user.id, roles: [], email: user.email },
process.env.SECRET_KEY,
{ expiresIn: '12h' },
);
return res
.status(200)
.json({ type: 'account', attributes: { token } });
} else {
return res.status(400)
.json({ message: 'username or password is invalid' });
}
});
}
}
res.sendStatus(401);
});
return route;
};
Have a look at the updated code return was missing at
return user.comparePassword(password, isMatch => {
Hope it'll fix your issue.
The problem in here. Your callback in comparePassword return only inside that callback. So the code still run to res.sendStatus(401) and after the callback is done it will run res.status(200).json...
user.comparePassword(password, isMatch => {
if (isMatch) {
const token = jwt.sign(
{ sub: user.id, roles: [], email: user.email },
process.env.SECRET_KEY,
{ expiresIn: '12h' },
);
return res
.status(200)
.json({ type: 'account', attributes: { token } });
}
});
Try to promisify comparePassword method in the user model:
userSchema.methods.comparePassword = function (password) {
return new Promise( function(resolve, reject) {
resolve(password === this.password);
});
}
Now you can use await syntax to get the promise result:
route.post('/login', async (req, res, next) => {
if (req.body.email && req.body.password) {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (user) {
const isMatch = await user.comparePassword(password);
if (isMatch) {
const token = jwt.sign(
{ sub: user.id, roles: [], email: user.email },
process.env.SECRET_KEY,
{ expiresIn: '12h' },
);
return res
.status(200)
.json({ type: 'account', attributes: { token } });
}
}
}
res.sendStatus(401);
});

Resources