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.