简体   繁体   中英

Password reset token is invalid or has been expired in Node.js

It's me again asking for help in Nodejs. I tried processing the resetPassword function that I created on Nodejs using Postman but I kept having the same error. I also noticed from my MongoDB Compass that the resetPasswordToken on the database and the resetPasswordToken sent using mailtrap is not the same. Here are my codes:

//user.js
const mongoose = require('mongoose');
const validator = require('validator');
const bcrypt = require('bcryptjs');
const jwt = require('jsonwebtoken');
const crypto = require('crypto')

const userSchema = new mongoose.Schema({
    name:{
        type: String,
        required: [true, 'Please enter your name'],
        maxLength: [30, 'Your name cannot exceed 30 characters']
    },
    email:{
        type: String,
        required: [true, ' Please enter your email'],
        unique: true,
        validate:[validator.isEmail, 'Please enter a valid email address.']
    },
    password:{
        type:String,
        required: [true, 'Please enter your password'],
        minlength: [6, 'Your password must be longer that 6 characters.'],
        select: false
    },
    role:{
        type: String,
        default: 'admin'
    },
    createdAt: {
        type: Date,
        default:Date.now
    },
    resetPasswordToken: String,
    resetPasswordExpire: Date
})
//Encrypting password before saving user
userSchema.pre('save',async function(next){
    if(!this.isModified('password')){
        next()
    }
    this.password = await bcrypt.hash(this.password, 10)
})

//Compare user password
userSchema.methods.comparePassword = async function(enteredPassword){
    return await bcrypt.compare(enteredPassword, this.password)
}

//Return JWT token
userSchema.methods.getJwtToken = function(){
    return jwt.sign({id:this._id}, process.env.JWT_SECRET,{
        expiresIn: process.env.JWT_EXPIRES_TIME
    })
}
// Generate password reset token
userSchema.methods.getResetPasswordToken = function(){
    //Generate token
    const resetToken = crypto.randomBytes(20).toString('hex');

    //Hash and set to resetPasswordToken
    this.resetPasswordToken = crypto.createHash('sha256').update(resetToken).digest('hex')

    //Set token expire time
    this.resetpasswordExpire = Date.now() + 30 * 60 * 1000

    return resetToken
}

module.exports = mongoose.model('User', userSchema);
//authController.js
const User = require('../models/user')

const ErrorHandler = require('../utils/errorHandler');
const catchAsyncErrors = require('../middlewares/catchAsynchErrors');
const sendToken = require('../utils/jwtToken');
const sendEmail = require('../utils/sendEmail')

const crypto = require('crypto')

//Register a user => /api/v1/register
exports.registerUser = catchAsyncErrors (async (req, res, next) =>{

    const { name, email, password } =req.body;

    const user = await User.create({
        name,
        email,
        password
    })

    sendToken(user, 200, res)
})

//Login User  => api/v1/login
exports.loginUser = catchAsyncErrors (async (req,res,next) =>{
    const { email, password} = req.body;

    //Checks if email and password is entered by user
    if(!email || !password){
        return next(new ErrorHandler('Please enter email and password', 400))
    }
    //Finding the user in database
    const user = await User.findOne({email}).select('+password')

    if(!user){
        return next(new ErrorHandler('Invalid Email or Password', 401));
    }

    //Checks if password or correct or not
    const isPasswordMatched = await user.comparePassword(password)

    if (!isPasswordMatched) {
        return next(new ErrorHandler('Invalid Email or Password', 401));
    }
    sendToken(user,200,res)
})
//Forgot Password => api/v1/password/forgot
exports.forgotPassword = catchAsyncErrors(async(req, res, next) => {

    const user = await User.findOne({email: req.body.email});

    if(!user){
        return next(new ErrorHandler('User not found', 404));
    }
    //Get reset token
    const resetToken = user.getResetPasswordToken();

    await user.save({validateBeforeSave: false })

    //Create reset password url
    const resetUrl =`${req.protocol}://${req.get('host')}/api/v1/password/reset/${resetToken}`;
    
    const message = `Your password reset token is as follows:\n\n${resetUrl}\n\n If you have not requested this email, then please ignore.`

    try{

        await sendEmail({
            email: user.email,
            subject: "KPOPStore Password Recovery",
            message
        })
        
        res.status(200).json({
            success: true,
            message: `Email sent to ${user.email}`
        })
    }catch (error){
        user.resetPasswordToken = undefined;
        user.resetPasswordExpire = undefined;

        await user.save({validateBeforeSave: false })

        return next(new ErrorHandler(error.message, 500))
 
    }
})
//ResetPassword => /api/v1/password/reset/:token
exports.resetPassword = catchAsyncErrors(async(req, res, next) =>{

    //Hash URL Token
    const resetPasswordToken = crypto.createHash('sha256').update(req.params.token).digest('hex')

    const user = await User.findOne({
        resetPasswordToken,
        resetPasswordExpire: { $gt: Date.now() }

    })
    if(!user){
        return next(new ErrorHandler('Password reset token is invalid or has been expired.', 400)
        )
    }

    if(req.body.password !== req.body.confirmPassword){
        return next(new ErrorHandler('Password does not match', 400))
    }
    //Setup new password
    user.password = req.body.password;

    user.resetPasswordToken = undefined;
    user.resetPasswordExpire = undefined;

    await user.save();

    sendToken(user, 200, res)
})
//Logout user => /api/v1/logout
exports.logout = catchAsyncErrors(async (req,res,next)=>{
    res.cookie('token', null, {
        expires: new Date(Date.now()),
        httpOnly: true
    })
    res.status(200).json({
        success: true,
        message: 'Logged out'
    })
})
//jwtToken.js
//Create and send token and save in cookie.
const sendToken =( user, statusCode, res)=>{

    //Create Jwt token
    const token = user.getJwtToken();

    //Options for cookie
    const options = {
        expires: new Date(
            Date.now() + process.env.COOKIE_EXPIRES_TIME * 24 * 60 * 60 * 1000
        ),
        httpOnly: true
    }

    res.status(statusCode).cookie('token', token, options).json({
        success: true,
        token,
        user
    })
}
module.exports = sendToken;
//auth.js
const express = require('express');
const router = express.Router();

const { registerUser, loginUser, logout, forgotPassword, resetPassword} = require('../controllers/authController')

router.route('/register').post(registerUser);
router.route('/login').post(loginUser);

router.route('/password/forgot').post(forgotPassword)
router.route('/password/reset/:token').put(resetPassword)


router.route('/logout').get(logout);

    
module.exports = router;

I'm sorry for the long blocks of code. I've been stuck in this part for 3 days. Please help me again. Thank you!

It's a typo in the User model's method:

//Set token expire time
    this.resetpasswordExpire = Date.now() + 30 * 60 * 1000

It sets resetpasswordExpire , not resetPasswordExpire , so the change is not picked up by the Object-Document Mapper and not saved in the DB. Then, your search fails:

User.findOne({
        resetPasswordToken,
        resetPasswordExpire: { $gt: Date.now() }

    })

because resetPasswordExpire is not set.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM