Nir Shihor
Nir Shihor

Reputation: 31

Getting an error that token is expired when trying to reset password following click on email link. Using Node js, express and mongoose

I have created a user registration and login. All working fine. Trying to implement password reset. All is working fine up to the point of filling in the new password and confirming same. Then getting an error that token has expired. No idea why. I'm pretty new to coding (a few month) so sincere apologies if I am not posting this properly. Have used the site a lot but it's my first time posting... Any help would be very much appreciated. Thank you. p.s I'm including the main js file. I'm not sure if I need to include any of the other files but as they are quite a number, I'll wait to see if anyone asks to see anything else. Thanks again.

//users.js
const express = require('express');
const router = express.Router();
const passport = require('passport');
const bcrypt = require('bcryptjs');
const async = require('async');
const nodemailer = require('nodemailer');
const crypto = require('crypto');


// User model
const User = require('../models/User');

// Login Page
router.get('/login', function(req, res){
    res.render('login');
});

// Register Page
router.get('/register', function(req, res){
    res.render('register');
});

// Register Handle
router.post('/register', function (req, res){
    const { name, email, password, password2 } = req.body;
    let errors = [];


    // Check required fields
    if(!name || !email || !password || !password2) {
        errors.push({ msg: 'Please fill in all fields' });
    }
    // Check passwords match
    if(password !== password2) {
        errors.push({ msg: 'Passwords do not match' });
    };

    // Check password length
    if(password.length < 6) {
        errors.push({ msg: 'Password should be at least 6 characters' });
    };

    // If there are errors
    if(errors.length > 0){
        res.render('register', {
            errors, 
            name, 
            email,
            password,
            password2
        });
    } else {
        //Validation passed
        User.findOne({ email: email })//This is going to return a promise
            .then(function (user){
                //If User exists
                if(user){
                    errors.push({ msg: 'Email already registered' });
                    res.render('register', {
                        errors, 
                        name, 
                        email,
                        password,
                        password2
                    });
                } else {
                    //If email not registered create a new User
                    const newUser = new User({
                        name,// es6 instead of 'name: name' and same for below
                        email,
                        password
                    });

                    // Hash Password
                    bcrypt.genSalt(10, function(err, salt){
                        bcrypt.hash(newUser.password, salt, function(err, hash){
                            if(err) throw err;
                            //Set password to hashed
                            newUser.password = hash;
                            // Save user
                            newUser.save()
                                .then(function(user){
                                    req.flash('success_msg', 'You have been successuflly registered and can now log in');
                                    res.redirect('/users/login');
                                })
                                .catch(err, function(){
                                    console.log(err);
                                });
                        });
                    });
                }
            });
    }
});

// Login Handle
router.post('/login', function(req, res, next){
    passport.authenticate('local', {
        successRedirect: '/dashboard',
        failureRedirect: '/users/login',
        failureFlash: true
    })(req, res, next);
});

// Logout Handle
router.get('/logout', function(req, res){
    req.logout();
    req.flash('success_msg', 'You have been logged out');
    res.redirect('/users/login');
});

// Forgot Password Page
router.get('/forgot', function (req, res){
    res.render('forgot');
});

// Forgot Password Handle
router.post('/forgot', function(req, res, next) {
    async.waterfall([
      function(done) {
        crypto.randomBytes(20, function(err, buf) {
          var token = buf.toString('hex');
          done(err, token);
        });
      },
      function(token, done) {
        User.findOne({ email: req.body.email }, function(err, user) {
          if (!user) {
            req.flash('error', 'No account with that email address exists.');
            return res.redirect('/users/forgot');
          }
  
          user.resetPasswordToken = token;
          user.resetPasswordExpires = Date.now() + 3600000; // 1 hour
  
          user.save(function(err) {
            done(err, token, user);
          });
        });
      },
      function(token, user, done) {
        var smtpTransport = nodemailer.createTransport({
          service: 'Gmail', 
          auth: {
            user: '[email protected]',
            pass: '<password>'
          }
        });
        var mailOptions = {
          to: user.email,
          from: '[email protected]',
          subject: 'WSS Password Reset',
          text: 'You are receiving this because you (or someone else) have requested the reset of the password for your account.\n\n' +
            'Please click on the following link, or paste this into your browser to complete the process:\n\n' +
            'http://' + req.headers.host + '/users/reset/' + token + '\n\n' +
            'If you did not request this, please ignore this email and your password will remain unchanged.\n'
        };
        smtpTransport.sendMail(mailOptions, function(err) {
          console.log('mail sent');
          req.flash('success_mail_sent', 'An e-mail has been sent to ' + user.email + ' with further instructions.');
          done(err, 'done');
        });
      }
    ], function(err) {
      if (err) return next(err);
      res.redirect('/users/forgot');
    });
  });
  
  router.get('/reset/:token', function(req, res) {
    User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
      if (!user) {
        req.flash('error_token', 'Password reset token is invalid or has expired.');
        return res.redirect('/users/forgot');
      }
      res.render('reset', {token: req.params.token});
    });
  });
  
  router.post('/reset/:token', function(req, res) {
    async.waterfall([
      function(done) {
        User.findOne({ resetPasswordToken: req.params.token, resetPasswordExpires: { $gt: Date.now() } }, function(err, user) {
          if (!user) {
            req.flash('error_token', 'Password reset token is invalid or has expired.');
            return res.redirect('back');
          } 
          if(req.body.password === req.body.confirm) {
            user.setPassword(req.body.password, function(err) {
              user.resetPasswordToken = undefined;
              user.resetPasswordExpires = undefined;

              //Update user in database
              user.save(function(err) {
                req.logIn(user, function(err) {
                  done(err, user);
                });
              });
            })
          } else {
              req.flash("error_passwords_match", "Passwords do not match.");
              return res.redirect('back');
          }
        });
      },
      function(user, done) {
        var smtpTransport = nodemailer.createTransport({
          service: 'Gmail', 
          auth: {
            user: '[email protected]',
            pass: 'N1rSh1hor?@'
          }
        });
        var mailOptions = {
          to: user.email,
          from: '[email protected]',
          subject: 'Your password has been changed',
          text: 'Hello,\n\n' +
            'This is a confirmation that the password for your account ' + user.email + ' has just been changed.\n'
        };
        smtpTransport.sendMail(mailOptions, function(err) {
          req.flash('success_password_change', 'Success! Your password has been changed.');
          done(err);
        });
      }
    ], function(err) {
      res.redirect('/home');
    });
  });
  
module.exports = router;

Upvotes: 1

Views: 1014

Answers (1)

Nir Shihor
Nir Shihor

Reputation: 31

Managed to sort it. I put together two different tutorials I followed. One was for registering users and logging in, and the other was for resetting password. I failed to implement the hashing process for the password reset as I did in the first tutorial, which caused the problem. Now resolved. If anyone experiencing the same issue requires code sample - happy to supply.

Upvotes: 1

Related Questions