I have tried to set up the forgot password backend as follows but it seems not to work.
exports.forgotPassword = (req, res) => {
const { email } = req.body.email;
User.findOne({ email }, (err, user) => {
if (err || !user) {
return res.status(401).json({
error: 'User with that email does not exist'
});
}
const token = jwt.sign({ _id: user._id }, process.env.JWT_RESET_PASSWORD, { expiresIn: '10m' });
// email
const emailData = {
from: process.env.EMAIL_FROM,
to: email,
subject: `Password reset link`,
html: `
<p>Please use the following link to reset your password:</p>
<p>${process.env.CLIENT_URL}/auth/password/reset/${token}</p>
<hr />
<p>This email may contain sensetive information</p>
`
};
// populating the db > user > resetPasswordLink
return user.updateOne({ resetPasswordLink: token }, (err, success) => {
if (err) {
return res.json({ error: errorHandler(err) });
} else {
sgMail.send(emailData).then(sent => {
return res.json({
message: `Email has been sent to ${email}. Follow the instructions to reset your password. Link expires in 10min.`
});
});
}
});
});
};
Test on postman showing sending without success and error
up on canceling the continues sending a request in postman, there is no error in postman console. However, my terminal console has this funny response
I will appreciate any help.
Thank you.
Since express doesn't know when to go from a function to another function, you need to call the next() argument, passed to these functions (in this case: forgotPasswordValidator and forgotPassword). You can find more on that here: http://expressjs.com/en/guide/using-middleware.html
The router:
const validationRules = () => {
return [
check('email')
.notEmpty().withMessage('Please add an email address')
.isEmail().withMessage('Must be a valid email address')
]
}
router.put("/forgot-password", validationRules(), forgotPasswordValidator, forgotPassword);
forgotPasswordValidator middleware function:
const { validationResult } = require('express-validator');
exports.forgotPasswordValidator = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
} else next()
};
The forgotPassword function seems fine, but if you have any more problems feel free to write a comment.
Related
I am not receiving any errors but when I test my endpoint it's taking longer and no response until I terminate the request. I am sending a user an email with a token that will be used to validate if he exists in the database so that he can change his/her password but I have not succeeded for the last two days. I am frustrated now, I have never done this before.
The Middleware that sends the reset password link
export class sendGridEmail {
static async sendResetPasswordEmail(email, token) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `You are receiving this email because you (or someone else) has requested the reset of a password. Follow this link ${process.env.BASE_URL}/api/resetpassword/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
}
The Component that sends the reset password link
export const sendResetPasswordLink = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "account with this email was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token);
res.status(200);
res.json({
message: `password reset link hase been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Route that tries to save the password. Am I getting it wrong by verifying the token in the params using jwt and then checking if the user exists or am I missing out something ?
export const resetPassword = asynchandler(async (req, res) => {
const { resetToken } = req.params;
const private_key = process.env.PRIVATE_KEY;
const payload = jwt.verify(resetToken, private_key);
const user = await userModel.findById(payload.id);
console.log(payload.id);
if (!user) {
res.status(404);
res.jsonp({ message: "token has expired" });
}else if(user){
user.password= req.body.password
await user.save();
await resetToken.delete();
await sendMessage.sendPasswordResetSuccess(user.number);
res.status(200);
res.json({message:"password changed succesfully"});
}else{
res.status(500)
res.json({message:"no token was procide"})
}
});
The routes
app.post('/api/resetlink', sendResetPasswordLink);
app.put("/api/resetpassword/:resetToken", resetPassword);
I am using SendGrid to send the user the reset password link that goes with two parameters (The user._id and token). I have another component that saves the user's changed the password but all I get is an error user. save is not a function
Email helper Code.
import sendGrid from "#sendgrid/mail";
export class sendGridEmail {
static async sendResetPasswordEmail(email, token, id) {
sendGrid.setApiKey(process.env.SENDGRID_API_KEY);
const msg = {
to: `${email}`,
from: `${process.env.VERIFIED_SENDER}`, // Change to your verified sender
subject: "RESET YOUR PASSWORD",
text: `Follow this link to reset your password: ${process.env.BASE_URL}/${id}/${token}`,
};
return sendGrid
.send(msg)
.then(() => {
console.log(`password rest link has been sent to: ${email}`);
})
.catch((err) => {
console.log(err);
});
}
sendLink Component
export const resetUserPassword = asynchandler(async (req, res) => {
const { email } = req.body;
const user = await userModel.findOne({ email });
if (!user) {
res.status(404);
res.json({ message: "the email provided was not found" });
} else if (user) {
const token = AuthToken(user._id);
try {
await sendGridEmail.sendResetPasswordEmail(user.email, token, user._id);
res.status(200);
res.json({
message: `a link to reset your password has been sent to: ${user.email}`,
});
} catch (error) {
res.status(500);
res.json({ message: error });
}
} else {
res.status(500);
res.json({ message: "Internal Server Error" });
}
});
The Component that tries to update the password in the Database but I get an error user.save() is not a function
export const saveResetPassword = asynchandler(async (req, res) => {
const { id, authorization } = req.params;
const user = userModel.findOne(req.params.id);
const private_key=process.env.PRIVATE_KEY
const payload = jwt.verify(authorization, private_key);
if (user._id === id || payload.id) {
try {
user.password = req.body.password;
await user.save();
} catch (error) {
res.status(404);
res.json({ message: `an error occured: ${error}` });
}
}else{
res.status(500)
res.json({message: "an error occured"})
}
});
My Routes
import { loginUser, registerUser, resetUserPassword, saveResetPassword } from "./controllers/user.controller.js";
export const Routes =(app)=>{
app.get("/health", (req,res) => {
res.send(200).json({message:"Server health check is Ok"});
});
// user api's
app.post('/api/registeruser', registerUser);
app.post('/api/loginuser', loginUser);
app.post('/api/password-reset', resetUserPassword);
app.post("/api/save-password/:id/:authorization", saveResetPassword);
}
const user = await userModel.findOne(req.params.id);
You forgot await, model.findOne() returns a Promise
I have a simple web service and it has a route for register user ,
I want when email exists in DB throw an error with status of 400 or other
I've done it like this
controllers/user.js
const { User } = require('../models/user')
exports.create = async (req, res) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
return res.json({ err: 'email already exists' })
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
res.status(400).send({ err })
}
}
BUT , it always give status of 200,
where is the problem ?
Add the status to your response:
if (user) {
return res.status(400).json({ err: 'email already exists' })
}
You can simply send the status 400 when checking if(user)
if(user){
res.status(400).jsom({ err: "Email already exists" });
}
OR
Threat the errors and add a middleware using next (a little bit more complicated then the first one, but more proffessional)
exports.create = async (req, res, next) => {
try {
const { email } = req.body
const user = await User.findOne({ email })
if (user) {
throw new Error("Email already exists");
}
await User.userValidation(req.body)
await User.create(req.body)
return res.json({})
} catch (err) {
next(err, req, res, next);
}
}
In the next middleware you can threat the error and send whatever response you need. (err, req, res objects are sent like references, so you can use them there)
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"
}
After making request to the server, am getting net::ERR_TOO_MANY_REDIRECTS. This was working earlier, now the App cant make any request to the server.
Though the API's are working when tested with Postman.
This is the action that makes the request to the server
//Login User
export const login = (email, password) => async (dispatch) => {
const config = {
headers: {
"Content-Type": "application/json",
},
};
const body = JSON.stringify({ email, password });
console.log(email, password); //This is where the run time stops and catch error
try {
const res = await axios.post(authLogin, body, config);
console.log(res);
dispatch({
type: LOGIN_SUCCESS,
payload: res.data,
});
dispatch(loadUser());
} catch (err) {
const errors = err.response.data.errors;
if (errors) {
errors.forEach((error) => dispatch(setAlert(error.msg, "danger")));
}
dispatch({
type: LOGIN_FAIL,
});
}
};
This is the controller for the API that's been called
// #route POST api/auth/login
// #desc Login user and return JWT token
// #access Public
const loginUser = async (req, res) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
try {
const { email, password } = req.body;
const user = await User.findOne({ email });
if (!user)
return res.status(401).json({
errors: [
{
msg:
"The email address " +
email +
" is not associated with any account. Double-check your email address and try again.",
},
],
});
//validate password
if (!user.comparePassword(password))
return res
.status(401)
.json({ errors: [{ msg: "Invalid email or password" }] });
// Make sure the user has been verified
if (!user.isVerified)
return res.status(401).json({
errors: [
{
type: "not-verified",
message: "Your account has not been verified.",
},
],
});
// Login successful, write token, and send back user
res.status(200).json({ token: user.generateJWT() });
} catch (error) {
console.error(error.message);
res
.status(500)
.json({ errors: [{ msg: "Server unavailable, try again latter" }] });
}
};
This is a react, node.js, mongoDB and Redux project. Have not experience this before.
Kindly help if you have any idea what i did wrong.
Thanks.
Resolved. It turns out that the SSL-VPN i was using on my machine caused the issue. All the APIs started working immediately i disconnected the VPN.