0

I'm creating a password reset functionality and my database doesn't seem to update the users password, when submitting the new password form I get a 404.

Here is my post route for the form

// Token URL :post
router.post('/users/reset/:token', (req, res, next) => {
  if(req.body.password === req.body['password-confirm']) {
    next();
    return;
  }

  req.flash('error', 'Passwords do not match!');
  res.redirect('back');

  User.findOne({
    resetPasswordToken: req.params.token,
    resetPasswordExpires: { $gt: Date.now() }
  }, function(err, user) {
    if(!user) {
      req.flash('error', ' Password reset is invalid or has expired');
      res.redirect(302, '/login');
    }

    const setPassword = promisify(user.setPassword, user);
    setPassword(req.body.password);
    user.resetPasswordToken = undefined;
    user.resetPasswordExpires = undefined;
    const updatedUser = user.save();
    req.login(updatedUser);
    req.flash('success_msg', 'Your password has been reset successfully! You are now logged in!');
    res.redirect('/dashboard');
  });
});

and here are the logs with mongodb debug on

Thu Jan 25 2018 20:06:23 GMT+0000 (GMT): GET /users/forgot
Thu Jan 25 2018 20:06:24 GMT+0000 (GMT): GET /favicon.ico
Thu Jan 25 2018 20:06:26 GMT+0000 (GMT): POST /users/forgot
Mongoose: users.findOne({ email: 'ben@benbagley.co.uk' }, { fields: {} })
{ email: 'user@website.co.uk' }
Mongoose: users.update({ _id: ObjectId("5a5c6740b9e210087e098fd6") }, { '$set': { resetPasswordExpires: new Date("Thu, 25 Jan 2018 21:06:26 GMT"), resetPasswordToken: '566c509df009f6f43c3d2b5f324764173bd2d251' } })
Message sent: <ceef6d47-4d91-0a3c-a4e1-1a8f090365e1@website.co.uk>

Preview URL: https://ethereal.email/message/WlVWjq0qIgpSmhJbWmo4xEK5Zwpruz6bAAAAp8kW.z.4aFEFOL5zp93OWds
Thu Jan 25 2018 20:06:28 GMT+0000 (GMT): GET /users/login
Thu Jan 25 2018 20:06:29 GMT+0000 (GMT): GET /favicon.ico
Thu Jan 25 2018 20:06:45 GMT+0000 (GMT): GET /users/reset/566c509df009f6f43c3d2b5f324764173bd2d251
Mongoose: users.findOne({ resetPasswordExpires: { '$gt': new Date("Thu, 25 Jan 2018 20:06:45 GMT") }, resetPasswordToken: '566c509df009f6f43c3d2b5f324764173bd2d251' }, { fields: {} })
Thu Jan 25 2018 20:06:45 GMT+0000 (GMT): GET /favicon.ico
Thu Jan 25 2018 20:06:53 GMT+0000 (GMT): POST /users/reset/566c509df009f6f43c3d2b5f324764173bd2d251
Thu Jan 25 2018 20:06:53 GMT+0000 (GMT): GET /favicon.ico

When I click on the generated link in the email, it goes to the password reset, no issues there. It's just the form that doesn't submit.

Here is a gif of the issue

amyloula
  • 1,556
  • 2
  • 17
  • 26
  • According to the code at the very top, if the passwords match, you call `next()` and exit the function. And since there is no other handler for the route, express ends up sending back an error 404. That's probably not what you want the code to do, yet that's what you implemented. Do you know what `next()` does? What do you expect to do here? –  Jan 25 '18 at 20:15
  • 1
    So this is just checking that the passwords match if it does, it moves onto updating the database as far as I can tell, if you have any suggestions @ChrisG that would be most helpful. –  Jan 25 '18 at 21:06
  • 1
    That's my point, it does *not* move on to updating the database. Even without `next()`, you are calling `return;`, which exits the entire function right then and there. Your form does get submitted, but your entire `User.findOne` part only gets called if the passwords do *not* match. I'm sorry, but it's plain as day: "if the passwords match, leave this function" –  Jan 25 '18 at 21:09
  • You say "it's plain as day" @ChrisG however I am new to node so just need to be aware of that. So what would you recommend? Remove the return? –  Jan 25 '18 at 21:11
  • I say it's plain as day because I honestly have no idea how you wrote all that code and are also still stumped by this. I don't mean to be rude, it's just curious that you manage to do the hard parts but fail at the extremely basic parts. –  Jan 25 '18 at 21:20
  • It's easy to do @ChrisG all part of the learning process. –  Jan 25 '18 at 21:25
  • I would suggest not submitting both password fields to the server for comparison. That should be implemented on the client then one sent for processing. It will take out the complexity that you are trying to solve here as well as ensure that what is sent to the server is valid data. – RickyM Jan 25 '18 at 21:28

1 Answers1

1

Try this:

router.post('/users/reset/:token', (req, res, next) => {

  // if passwords don't match, flash error and send back to form
  if (req.body.password != req.body['password-confirm']) {
    req.flash('error', 'Passwords do not match!');
    res.redirect('/users/change-password');  // insert actual form URL
    return; // we're done handling the route, exit function
  }

  // if we get to here, the passwords match
  User.findOne({
    resetPasswordToken: req.params.token,
    resetPasswordExpires: {
      $gt: Date.now()
    }
  }, function(err, user) {
    if (!user) {
      req.flash('error', ' Password reset is invalid or has expired');
      res.redirect(302, '/login');
    }

    const setPassword = promisify(user.setPassword, user);
    setPassword(req.body.password);
    user.resetPasswordToken = undefined;
    user.resetPasswordExpires = undefined;
    const updatedUser = user.save();
    req.login(updatedUser);
    req.flash('success_msg', 'Your password has been reset successfully! You are now logged in!');
    res.redirect('/dashboard');
  });
});
  • This is what I get ```Error: req#login requires a callback function``` https://gyazo.com/a8135fd131a6f11032bd7d3727c783a3 –  Jan 25 '18 at 21:23
  • Ok so I tried this https://gist.github.com/benbagley/4ab4650eae7d1839c379a748732069ce and I got the error that is below the code. –  Jan 25 '18 at 21:49
  • and also `Error [ERR_HTTP_HEADERS_SENT]: /Users/benbagley/Code/poetry-out-loud/views/reset.hbs: Cannot set headers after they are sent to the client` –  Jan 25 '18 at 21:55
  • @B.J.B The first error means you're using the Promise wrong that is returned by `promisify()`. The second error means you're trying to send a response back to the client twice, most likely because you aren't `return`ing after redirecting to `/login`. –  Jan 26 '18 at 13:10