[英]Password reset token is invalid or has been expired in Node.js
又是我在 Nodejs 中寻求帮助。 我尝试处理我使用 Postman 在 Nodejs 上创建的 resetPassword function,但我一直遇到同样的错误。 我还从我的 MongoDB Compass 中注意到,数据库上的 resetPasswordToken 和使用 mailtrap 发送的 resetPasswordToken 不一样。 这是我的代码:
//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;
对于长长的代码块,我很抱歉。 我已经被困在这部分3天了。 请再次帮助我。 谢谢!
这是用户模型方法中的一个错字:
//Set token expire time
this.resetpasswordExpire = Date.now() + 30 * 60 * 1000
它设置resetpasswordExpire
,而不是resetPasswordExpire
,因此对象-文档映射器不会拾取更改,也不会保存在数据库中。 然后,您的搜索失败:
User.findOne({
resetPasswordToken,
resetPasswordExpire: { $gt: Date.now() }
})
因为未设置 resetPasswordExpire。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.