Mongoose findOne always return undefined - node.js

When ever i tried to post this API via postman using email and password.
I get User Not Found. I cross checked email and password 100+ times.
In command prompt i am getting UnhandledPromiseRejectionWarning: Error [ERR_HTTP_HEADERS_SENT]: Cannot set headers after they are sent to the client.
What i am doing wrong here. Please tell me, Thank you in advance.
exports.loginUser = async (req, res) => {
try{
const user = await userTable.findOne({ email:req.body.email });
if (!user){
res.send({
status: 404,
message: "User Not Found"
});
}
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password){
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, {expiresIn:"1d"});
res.send({
status: 200,
data: user,
jwt: accessToken
});
}else{
res.send({
status: 404,
message: "Wrong password"
});
}
}catch(err){
res.status(500).send({
status: 0,
message: "catch issue" + err.message
});
};
};

I'm pretty sure this is happening because your handler continues after sending a response, therefore attempting to modify a response after it's already been sent.
Keep in mind, it could be how you are using the loginUser function. If you wouldn't mind updating your question showing us how you are using it, that would be super helpful!
There are a couple things that come to mind; 1. You may need to add a return statement somewhere, like on line 10/11 for example 2. You could also wrap the code after the first if statement inside an else statement to see if that changes things.
At the end of the day, I'm pretty sure your code is continuing after sending a response, which is why you see that error.
With return
exports.loginUser = async (req, res) => {
try {
let dataToSend = {};
const user = await userTable.findOne({ email: req.body.email });
if (!user) {
res.send({
status: 404,
message: "User Not Found",
});
// Try adding this
return;
}
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password) {
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, { expiresIn: "1d" });
res.send({
status: 200,
data: user,
jwt: accessToken,
});
} else {
res.send({
status: 404,
message: "Wrong password",
});
}
} catch (err) {
res.status(500).send({
status: 0,
message: "catch issue" + err.message,
});
}
};
Wrap in else
This is very ugly and should only be used to test in my opinion!
exports.loginUser = async (req, res) => {
try {
const user = await userTable.findOne({ email: req.body.email });
if (!user) {
res.send({
status: 404,
message: "User Not Found",
});
} else {
const hashpass = cryptr.decrypt(user.password);
if (hashpass == req.body.password) {
const accessToken = jwt.sign({ id: user._id, email: user.email }, process.env.JWT_PASS, { expiresIn: "1d" });
res.send({
status: 200,
data: user,
jwt: accessToken,
});
} else {
res.send({
status: 404,
message: "Wrong password",
});
}
}
} catch (err) {
res.status(500).send({
status: 0,
message: "catch issue" + err.message,
});
}
};

Related

express status code not updating, getting json message update

I am not getting status message as 500 eventhough I set. always getting status message as 200. how to set the status as 500?
here is my code : "express": "4.17.2",
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.json({ message: 'User already exist', success: false }).status(500);
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.json({ message: 'user created successfully', success: true });
} catch (error) {
res.sendStatus(500).json({ message: 'Error creating user', success: false });
}
});
react axios:
when i use:
return res.status(500).json({ message: 'User already exist', success: false }); getting something went wrong
export const registerUser = createAsyncThunk('post/user', async (user: RegisterFormProps) => {
try {
const response = await axios.post(environment.BASE_URL + '/user/register', user);
console.log('suc', response.data.success);
if (response.data.success) {
toast.success(response.data.message);
} else {
toast.error(response.data.message);
}
} catch (error) {
const err = error as AxiosError;
console.log('err', err);
toast.error('something went wrong');
}
});
You should be using res.status instead of res.sendStatus in your code.
res.status(statusCode) just sets the status on the response.
whereas res.sendStatus(statusCode) sends the response after setting the status.
for example:
res.sendStatus(500); // equivalent to res.status(500).send('Internal Server Error')
on the client side try using error.response.data in your catch block
Try this one:
router.post('/register', async (req: Request, res: Response) => {
const { password, email } = req.body;
try {
const isUserExist = await UserModel.findOne({ email: email });
if (isUserExist) {
//status not set.
return res.status(500).json({ message: 'User already exist', success: false });
}
const hashPassword = bcrypt.hashSync(password, 10);
req.body.password = hashPassword;
const newUser = new UserModel(req.body);
await newUser.save();
res.status(201).json({ message: 'user created successfully', success: true });
} catch (error) {
res.status(500).json({ message: 'Error creating user', success: false });
}
});

Endpoint or route inaccurate? (axios call works in code but not on postman)

After following a tutorial for a MERN login system using a REST api, I've been trying to write my own patch method. However it's not working correctly so I've been trying to understand the already written routes and methods from the tutorial. When I login/register, axios will post to the specific endpoint and will log in fine but when I try to test it on Postman it's not working. Here's an example:
const submit = async (e) => {
e.preventDefault();
try{
const loginUser = {email, password};
const loginResponse = await axios.post("http://localhost:5000/users/login", loginUser);
setUserData({
token: loginResponse.data.token,
user: loginResponse.data.user
});
localStorage.setItem("auth-token", loginResponse.data.token);
history.push("/dashboard");
} catch(err) {
err.response.data.msg && setError(err.response.data.msg)
}
};
with the corresponding route:
router.post("/login", async (req, res) => {
try {
const { email, password } = req.body;
// validate
if (!email || !password)
return res.status(400).json({ msg: "Not all fields have been entered." });
const user = await User.findOne({ email: email });
if (!user)
return res
.status(400)
.json({ msg: "No account with this email has been registered." });
const isMatch = await bcrypt.compare(password, user.password);
if (!isMatch) return res.status(400).json({ msg: "Invalid credentials." });
const token = jwt.sign({ id: user._id }, process.env.JWT_SECRET);
console.log("token",token);
res.json({
token,
user: {
id: user._id,
displayName: user.displayName,
favPokemon: user.favPokemon,
},
});
} catch (err) {
res.status(500).json({ error: err.message });
}
});
and this will work fine. However with the following Postman request,
then it responds with the error message created. What am I missing?
You are sending email and password fields as 'Params' (Query Params) instead as 'Body', as seen in the screenshot
POST http://localhost:5000/users/login
Body: application/json (raw)
{
"email": "test#test.com",
"password": "test123"
}

I am not getting any response when my credentials are wrong in catch while calling login API with axios

I am not getting any response while calling login api from nodejs.
I am handling the catch in frontend as well.
How to get Invalid Credentials message from backend API if credentials doesn't matched.
my Backend login API is -
// api to login user
router.post('/login', function (req, res) {
const valid = validator.isEmail(req.body.email)
if (valid == false) {
var message = { "Success": 0, "Message": "Please enter a valid email." };
res.send(message)
}
userObj.findOne({
email: req.body.email
}).then(user => {
if (!user) {
var message = { "Success": 0, "Message": "User does not exists." };
res.send(message)
} else {
// console.log(bcrypt.compare(req.body.password, user.password))
// var message = { "Success": 1, "User": user };
// res.send(message)
bcrypt.compare(req.body.password, user.password)
.then(isMatch => {
if (isMatch) {
const payload = {
name: user.name,
id: user._id,
email: user.email
}
jwt.sign(payload, 'secret', {
expiresIn: 3600
}, (err, token) => {
if (err) console.error('There is some error in token', err);
else {
res.json({
Success: 1,
token: `${token}`
})
}
})
}
else {
res.json({
Success: 0,
Message: 'Invalid Credentials'
})
}
})
}
})
});
my frontend login action code is -
// Login - get user token
export const loginUser = user => dispatch => {
return axios
.post("http://18.207.190.61:4000/login", {
email: user.email,
password: user.password
})
.then(res => {
// Save to localStorage
// Set token to localStorage
localStorage.setItem("usertoken", res.data.token);
// Set token to Auth header
setAuthToken(res.data.token);
// Decode token to get user data
const decoded = jwt_decode(res.data.token);
// Set current user
localStorage.setItem("username", decoded.name);
dispatch(setCurrentUser(decoded));
return res.data;
})
.catch(err => {
return err;
});
};
finally my login component code is -
this.props.loginUser(user).then((res, err) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});
How can I get message Message: 'Invalid Credentials' from backend API in front end to print.
Please return response with status codes '200' for success and '401' for invalid credentials and try again. Axios recognises the status codes and tells if there is an error.
if(success)
res.status(200).json({
Success: 1,
token: '${token}'
})
else
res.status(401).json({
Success: 0,
Message: 'Invalid Credentials'
})
Try this once.
1.Remove the catch block in your login action code
2. change your login component code to
this.props.loginUser(user).then((res ) => {
if (res.Success == "0") {
this.setState({
loading: false,
message: res.Message
});
}
});

Why does transaction fails with already rolled back when user inputs in wrong email

I'm authorizing emails in the database but when I input wrong email it throws Transaction cannot be rolled back because it has been finished with state: commit
export const signin = async (req, res) => {
const { email, password } = req.body;
const transaction = await db.sequelize.transaction();
try {
const user = await db.User.findOne({ where: { email } }, { transaction });
await transaction.commit();
const passBool = Auth.comparePassword(password, user.password);
if (user && passBool) {
return res.json({
success: 1,
user,
message: 'signed in'
});
}
res.json({ success: 0, message: 'wrong username or password' });
} catch (ex) {
await transaction.rollback();
res.json({ success: 0, message: 'wrong username or password' });
}
};
I'm not sure exactly why rolling back doesn't work in your example, but you can try:
A transaction should be passed in the options object of the query:
const user = await db.User.findOne({ where: { email }, transaction });
You can try using managed transaction, to avoid manual handling of commit/rollback:
export const signin = async (req, res) => {
const { email, password } = req.body;
db.sequelize.transaction(async transaction => {
const user = await db.User.findOne({ where: { email }, transaction });
const passBool = Auth.comparePassword(password, user.password);
if (user && passBool) {
return res.json({
success: 1,
user,
message: 'signed in'
});
}
res.json({ success: 0, message: 'wrong username or password' });
}).catch(err => {
res.json({ success: 0, message: 'wrong username or password' });
});
};
solved this, it would fail if i had inserted in wrong request body params

How to update token when the user logs in in express js?

I want to update token in user collection when the user logs in. So far, I have tried this.
router.post("/login", (req, res, next) => {
User.find({ email: req.body.email })
.exec()
.then(user => {
if (user.length < 1) {
return res.status(401).json({
message: "Auth failed"
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed"
});
}
if (result) {
const token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({token : token2 })
.exec()
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
})
.catch(err => {
console.log(err);
res.status(500).json({
error: err
});
});
});
here new token is getting generated but it is not being saved in the user collection. I want to update new token in the collection.
Can anyone know where I am missing?
Try Something like:
router.post("/login", async (req, res) => {
try{
const user = await User.find({ email: req.body.email });
if (user.length < 1) {
return res.status(401).json({
message: "Auth failed"
});
}
bcrypt.compare(req.body.password, user[0].password, (err, result) => {
if (err) {
return res.status(401).json({
message: "Auth failed"
});
}
if (result) {
const token2 = jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}
);
User.update({_id:user[0]._id},{$set:{token : token2 }},{new: true});
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
res.status(401).json({
message: "Auth failed"
});
});
}
catch(err){
res.status(500).json({
error: err
});
}
});
Try below code:
jwt.sign(
{
email: user[0].email,iat: Math.floor(Date.now() / 1000) - 30
},
"123",
{
expiresIn: "1h"
}, function(err, token2) {
User.update({token : token2 }).exec() //Change the query to update
return res.status(200).json({
message: "Auth successful",
token: token2
});
}
);
Let me know if it helps.
Firstly understand why do you need to refresh the token. Once you create the token you can store that token as a session variable in a web, internal storage of your mobile etc...
Instead of refresh of token you can create another token.
In another way you can set timeout for the token. Token will be invalid after that time period.

Resources