简体   繁体   English

Node.js 中的密码重置令牌无效或已过期

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

It's me again asking for help in Nodejs.又是我在 Nodejs 中寻求帮助。 I tried processing the resetPassword function that I created on Nodejs using Postman but I kept having the same error.我尝试处理我使用 Postman 在 Nodejs 上创建的 resetPassword function,但我一直遇到同样的错误。 I also noticed from my MongoDB Compass that the resetPasswordToken on the database and the resetPasswordToken sent using mailtrap is not the same.我还从我的 MongoDB Compass 中注意到,数据库上的 resetPasswordToken 和使用 mailtrap 发送的 resetPasswordToken 不一样。 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.我已经被困在这部分3天了。 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.它设置resetpasswordExpire ,而不是resetPasswordExpire ,因此对象-文档映射器不会拾取更改,也不会保存在数据库中。 Then, your search fails:然后,您的搜索失败:

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

    })

because resetPasswordExpire is not set.因为未设置 resetPasswordExpire。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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