How can i resset my password from email link? Nodejs ReactJS -MERN

Hello and excuse me if I’m not clear enough, I’m a beginner. How can I change password from a link? The sent mail contains a new token (different from the one from the user’s registration) based on which you should change the password. Or so I thought, I’m willing to listen to other opinions. When the user enters the "Forgot password", he enters his email and receives a link with a token generated to change the password. Once on the password change page, it does not change. The function created to change the password is in ControlUser and it’s called resetPassword, here I think that’s the problem. But even in the client I think I have small problems, I did not understand very well how to take the token and how it should be used. Thanks for any answers and help!

I belive the problem is in : server-> control user-> resetPassword: async…. and in client-> forgotPassword , and client-> route -> this ":token" is ok?

Server run localhost:3001
Client run localhost:3000

=========================Server===============================================

User Schema

const mongoose = require('mongoose');
    const validator = require('validator');
    require('dotenv').config();
    const bcrypt = require('bcrypt');
    const jwt = require('jsonwebtoken');

const userSchema = mongoose.Schema({
    email:{
        type:String,
        required:[true,"Emailul este obligatoriu"],
        unique: true,
        trim:true,
        validate(value){
            if(!validator.isEmail(value)){
                throw new Error('Email gresit,introdu un email corect' )
            }
        }
    },
    parola:{
        type:String,
        required: [true, "Parola este obligatorie"],
        trim:true,
        validate(value) {
            if (!validator.isLength(value, { min: 6, max: 100 })) {
              throw Error("Parola este prea scurta.Minim 6 caractere");
            }
            
        if (value.toLowerCase().includes("parola")) {
            throw Error(
              'Parola nu trebuie sa contina cuvantul cheie "parola'
            );
          }
        }
    },


    nume:{
        type:String,
        maxlength:100,
        trim: true,
        default:''
    },

    prenume:{
        type:String,
        maxlength:100,
        trim: true,
        default:''
    },

    cos:{
        type:Array,
        default: []
    },

    istoric:{
        type:Array,
        default: []
    },

    role:{
        type:String,
        enum:['user','admin'],
        default:'user'
    },
    verified:{
        type: Boolean,
        default: false
    }

});
userSchema.pre('save',async function(next){
    let user = this

    if(user.isModified('parola')){
        const salt = await bcrypt.genSalt(10);
        const hash = await bcrypt.hash(user.parola, salt);
        user.parola = hash;
    }

    next()
});
userSchema.methods.generateAuthToken = function(){
    let user = this;
    const userObj = { sub: user._id.toHexString(), email:user.email };
    const token = jwt.sign(userObj, process.env.DB_SECRET,{ expiresIn:'1d'});
    return token;
}
userSchema.statics.emailTaken = async function(email){
    const user = await this.findOne({email});
    return !!user;
}

userSchema.methods.comparePassword = async function(candidatePassword){
    /// candidate password = extragem parola (decriptare hash) normala
    const user = this;
    const match = await bcrypt.compare(candidatePassword, user.parola);
    return match;
}

userSchema.methods.generateRegisterToken = function(){
    let user = this;
    const userObj = { sub: user._id.toHexString() };
    const token = jwt.sign(userObj, process.env.DB_SECRET,{ expiresIn:'10h'});
    return token;
}


const User = mongoose.model('User',userSchema);
module.exports = { User };

ControlUser

Here, important for changing the password are, finally, forgotPassword where the email is sent to user-> gasireuserprinEmail is finduserby Email and changePassword is changePassword. It works, send me an email (with a new token) to reset the password. In ResetPassword I think something is wrong.

const { userService,emailService } = require('../servicii');
const httpStatus = require('http-status');
const { errapi } = require('../middleware/errapi');
const { authService } = require('../servicii');
const { User } = require("../mscheme/user")
const bcrypt = require('bcrypt')
const ControlUser = {
    async profile(req,res, next){
        try{
            const user = await userService.GasireUserID(req.user._id);
            if(!user){
                throw new errapi(httpStatus.NOT_FOUND,'Userul nu a fost gasit')
            }
            res.json(res.locals.permission.filter(user._doc))
        }catch(error){
            next(error);
        }
    },
async updateProfile(req,res, next){
    try{
        const user = await userService.updateProfil(req);
        res.json(user); ///. filter response fields
    }catch(error){
        next(error);
    }
},
async updateUserEmail(req,res, next){
    try{
        const user = await userService.updateUserEmail(req);
        const token = await authService.AutentificareToken(user);

        // verificare mail 
        await emailService.inregistrareEmail(user.email,user);
       
        res.cookie('cookie_token',token)
        .send({
            user,
            token
        })           
    }catch(error){
        next(error);
    }
},
async verificareCont(req,res, next){
    try {
        const token = await userService.validateToken(req.query.validation);
        const user = await userService.GasireUserID(token.sub);

        if(!user) throw new errapi(httpStatus.NOT_FOUND,'Userul nu a fost gasit');
        if(user.verified) throw new errapi(httpStatus.BAD_REQUEST,'Contul a fost deja activat')

        user.verified = true;
        user.save();
        res.status(httpStatus.CREATED).send({
            user
        })
    } catch(error){
        next(error);
    }
},
forgotPassword: async (req, res) => {
    try {
        const {email} = req.body.email
        const user = await userService.GasUserPrinEmail(req.body.email)
        if(!user) return res.status(400).json({msg: "Mail inexistent"})
        await emailService.schimbareParola(user.email,user);
        res.json({msg: "Verifica mail"})
    } catch (err) {
        return res.status(500).json({msg: err.message})
    }
},
resetPassword: async (req, res) => {
    try {
        const {parola} = req.body
        console.log(parola)
        const passwordHash = await bcrypt.hash(parola, 12)

        await Users.findOneAndUpdate({_id: req.user.id}, {
            parola: passwordHash
        })

        res.json({msg: "Password successfully changed!"})
    } catch (err) {
        return res.status(500).json({msg: err.message})
    }
}

}


module.exports = ControlUser;

User Service , GasUserPrinEmail =find User From Email, gasireUserId=find user id,

 const { errapi } = require("../middleware/errapi");
    const { User } = require("../mscheme/user")
    const httpStatus = require('http-status');
    const jwt = require('jsonwebtoken');
    require('dotenv').config()
    const bcrypt = require('bcrypt')
    
const GasUserPrinEmail = async(email) =>{
    return await User.findOne({email:email})
}
const GasireUserID =  async(_id) =>{
    return await User.findById(_id);
}

const validateToken = async(token)=>{
    return jwt.verify(token, process.env.DB_SECRET);
}
const updateProfil = async(req) => {
    try{

        
        const user = await User.findOneAndUpdate(
            { _id: req.user._id },
            {
                "$set":{ 
                    ...req.body.data /// cum validez???
                }
            },
            { new: true }
        );
        if(!user){
            throw new errapi(httpStatus.NOT_FOUND,'User inexistent');
        }
        return user;
    } catch(error){
        throw error;
    }
}
const updateUserEmail = async(req) => {
    try {
        if(await User.emailTaken(req.body.newemail)){
            throw new errapi(httpStatus.BAD_REQUEST,'Scuze emailul este deja utilizat');
        }

        const user = await User.findOneAndUpdate(
            { _id: req.user._id, email: req.user.email },
            {
                "$set":{ 
                    email: req.body.newemail,
                    verified:false
                }
            },
            { new: true }
        );
        if(!user){
            throw new errapi(httpStatus.NOT_FOUND,'User negasit');
        }
        return user
    }catch(error){
        throw error;
    }
}





module.exports = {
    GasUserPrinEmail,
    GasireUserID,
    updateProfil,
    updateUserEmail,
    validateToken
    
}

Email Service, Here, it is important SchimbareParola= changePassword, inregistrareEmail is for verify mail and activate account . And here I have problems, I posted another post because I didn’t understand very well how to work with tokens and I can’t activate my account.

const nodemailer = require('nodemailer');
const Mailgen = require('mailgen');
require('dotenv').config();
const jwt = require('jsonwebtoken');
let transporter = nodemailer.createTransport({
    service:"Gmail",
    secure:true,
    auth:{
        user: process.env.EMAIL,
        pass: process.env.EMAIL_PASSWORD
    }
});


const inregistrareEmail = async(userEmail,user) => {
    try{
        const emailToken = user.generateRegisterToken();

        let mailGenerator =  new Mailgen({
            theme:"default",
            product:{
                name:"Diva Impex",
                link:`${process.env.EMAIL_MAIL_URL}`
            }
        });

        const email = {
            body:{
                name:userEmail,
                intro: 'Bine ai venit la Diva impex. Speram sa iti satisfacem nevoile si sa gasesti ceea ce iti doresti',
                action:{
                    instructions: 'Pentru a iti valida contul, click aici',
                    button:{
                        color:'#1a73e8',
                        text: 'Valideaza-ti contul.',
                        link: `${process.env.SITE_DOMAIN}verification?t=${emailToken}`
                    }
                },
                outro: 'Daca ai nevoie de ajutor, lasa-ne un reply si iti vom raspunde in cel mai scurt timp posibil'
            }
        }
        let emailBody = mailGenerator.generate(email);
        let message = {
            from: process.env.EMAIL,
            to:userEmail,
            subject:"Bine ai venit la Diva Impex!",
            html: emailBody
        };

        await transporter.sendMail(message);
        return true

        


    } catch(error){
        throw error
    }
}
const schimbareParola = async(userEmail,user) => {
    try{
        const access_token = createAccessToken({id: user._id});

        let mailGenerator =  new Mailgen({
            theme:"default",
            product:{
                name:"Diva Impex",
                link:`${process.env.EMAIL_MAIL_URL}`
            }
        });
       console.log(access_token)
        const email = {
            body:{
                name:userEmail,
                intro: 'Schimbare parola',
                action:{
                    instructions: 'Click pe buton pentru a schimba parola',
                    button:{
                        color:'#1a73e8',
                        text: 'Du-te pe site.',
                        link: `${process.env.SITE_DOMAIN}users/reset/${access_token}`
                    }
                },
                outro: 'Daca ai nevoie de ajutor, lasa-ne un reply si iti vom raspunde in cel mai scurt timp posibil'
            }
        }
        let emailBody = mailGenerator.generate(email);
        let message = {
            from: process.env.EMAIL,
            to:userEmail,
            subject:"Bine ai venit la Diva Impex!",
            html: emailBody
        };

        await transporter.sendMail(message);
        return true

        


    } catch(error){
        throw error
    }
}
const createAccessToken = (payload) => {
    return jwt.sign(payload, process.env.ACCESS_TOKEN_SECRET, {expiresIn: '15m'})
}


module.exports = {
    inregistrareEmail,
    schimbareParola
}

Middleware auth:

const autentificare = require('passport');
const { errapi } = require('./errapi');
const httpStatus = require('http-status');
const { roles } = require('../roluri/rol');




const verificare = ( req, res, resolve, reject,rights ) => async(err, user)=>{
    
     if( err || !user){
        return reject(new errapi(httpStatus.UNAUTHORIZED,'Neautorizat'))
     }
    req.user = user;
    if(rights.length){
        const action = rights[0] /// createAny, readyAny
        const resource = rights[1] /// dog, profile, user
        const permission = roles.can(req.user.role)[action](resource);
        if(!permission.granted){
            return reject(new errapi(httpStatus.FORBIDDEN,"Nu aveti drepturi" ))
        }
        res.locals.permission = permission;
    }
    resolve()
}


const auth = (...rights) => async(req,res,next) => {
    return new Promise((resolve, reject)=>{
        autentificare.authenticate('jwt',{ session:false}, verificare( req, res, resolve, reject,
            rights))(req,res,next)
    })
    .then(()=> next())
    .catch((err)=> next(err))
}

module.exports = auth;

Passport:

const { User } = require('../mscheme/user');
require('dotenv').config();

const { Strategy: JwtStrategy, ExtractJwt } = require('passport-jwt');

const jwtOptiuni = {
    secretOrKey: process.env.DB_SECRET,
    jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken()
};

const jwtVerificare = async(payload, done) => {
    try{
        const user = await User.findById(payload.sub);
        if(!user) {
            return done(null, false)
        }
        done(null,user)
    }catch(error){
        done(error,false)
    }
}

const jwtStrategy = new JwtStrategy(jwtOptiuni,jwtVerificare)

module.exports = {
    jwtStrategy
}

ControlAuth

const { authService,emailService} = require('../servicii');
const httpStatus = require('http-status');
const ControlAuth = {
    //req= primim date trimise de user(email si parola)
    //res=ce dorim sa primim cand trimitem rsp
    async inregistrare(req, res, next){
        try{  
        const { email, parola } = req.body;
        const user = await authService.createUser(email, parola);
        const token = await authService.AutentificareToken(user)
      
        await emailService.inregistrareEmail(email,user);
      
      
        res.cookie('cookie_token',token).status(httpStatus.CREATED).send({
            user,
            token
        });
     } catch( error){
     //  console.log(error)
        next(error);
       //if(error) return res.status(400).send(error);

    }
    },
    async conectare(req, res, next){
        try{
            const { email, parola } = req.body;
            const user = await authService.conectareEmailPass(email, parola);
            const token = await authService.AutentificareToken(user)
            res.cookie('cookie_token',token)
            .send({ user, token});

        } catch(error){
           // console.log(error)
           next(error);
            //if(error) return res.status(400).send(error);
        }
    },
    async isauth(req, res, next){
        res.json(req.user)
    }

}


module.exports = ControlAuth;

Service Auth

const { User } = require('../mscheme/user');
const httpStatus = require('http-status');
const { errapi } = require('../middleware/errapi');
const userService = require('./user.service');
const createUser = async(email,parola) => {
    try{
        if(await User.emailTaken(email)){
            throw new errapi(httpStatus.BAD_REQUEST, 'Email duplicat');
      
    } 
    
    const user = new User({
        email,
        parola
    });
    await user.save();
    return user;
}
    catch(error){
        
        throw error;
    }
}
const AutentificareToken = (user) => {
    const token = user.generateAuthToken();
    return token;
}
const conectareEmailPass = async(email, parola) => {
    try{
        const user = await userService.GasUserPrinEmail(email);
        if(!user){
            throw new errapi(httpStatus.UNAUTHORIZED, 'Email introdus gresit');
        }
        if(!(await user.comparePassword(parola))){
            throw new errapi(httpStatus.UNAUTHORIZED, 'Parola gresita');
        }

        return user;
    } catch(error) {
        throw error;
    }
}
module.exports = {
    createUser,
    AutentificareToken,
    conectareEmailPass
}

User Route:

const express = require('express');
const router = express.Router();
const ControlUser = require('../controlere/ControlUser');
const auth = require('../middleware/auth');


router.route('/profile')
.get(auth('readOwn','profile'),ControlUser.profile)
.patch(auth('updateOwn','profile'),ControlUser.updateProfile)
router.patch('/email',auth('updateOwn','profile'), ControlUser.updateUserEmail);
router.get('/verificare', ControlUser.verificareCont)
router.post('/forgot',ControlUser.forgotPassword)
router.post('/reset',auth,ControlUser.resetPassword)


module.exports = router;

Index Route

const express = require('express');
const rutaAuth = require('./RutaAutentificare');
const router = express.Router();
const usersRoute = require('./RutaUser');

const indexRute= [
    {
        path:'/auth',
        route: rutaAuth
    },
    {
        path:'/users',
        route: usersRoute 
    }
]

indexRute.forEach((route)=>{
    router.use(route.path, route.route);
});


module.exports = router;

======================CLIENT==================================

Forgot password(mail from account with a new token for reset password): -it’s working

import React, {useState} from 'react'
import axios from 'axios'



const initialState = {
    email: '',
    err: '',
    success: ''
}

function ForgotPassword() {
    const [data, setData] = useState(initialState)

    const {email, err, success} = data

    const handleChangeInput = e => {
        const {name, value} = e.target
        setData({...data, [name]:value, err: '', success: ''})
    }

    const forgotPassword = async () => {
        //if(!email)
         //   return setData({...data, err: 'Invalid emails.', success: ''})
            
        try {
            const res = await axios.post('/api/users/forgot', {email})

            return setData({...data, err: '', success: res.data.msg})
        } catch (err) {
            err.response.data.msg && setData({...data, err:  err.response.data.msg, success: ''})
        }
    }
    
    return (
        <div className="container">
            <h2>Ti-ai uitat parola? </h2>
            <p>Introdu mai jos mail-ul tau</p>

            <div className="row">
                {err }
                {success}
                <p> </p>
                <div></div>
                <label htmlFor="email"></label>
                <input type="email" name="email" id="email" value={email}
                onChange={handleChangeInput} 
                />
                <button onClick={forgotPassword}>
                    <p>verifica mail</p>
                </button>
            </div>
        </div>
    )
}

export default ForgotPassword

ResetPassowrd- appear 2 fields(newpass and confirm new pass, but password is not update):

import React, {useState} from 'react'
import axios from 'axios'
import {getTokenCookie} from '../../utils/tools';

const initialState = {
    parola: '',
    cf_password: '',
    err: '',
    success: ''
}

function ResetPassword() {
    const [data, setData] = useState(initialState)
    const token=`${getTokenCookie()}`
    console.log("test1")
    console.log(token)

    const {parola, cf_password} = data

    const handleChangeInput = e => {
        const {name, value} = e.target
        setData({...data, [name]:value, err: '', success: ''})
    }


    const handleResetPass = async () => {
  
        try {console.log("token")
        console.log("test3")
            const res = await axios.post('/api/users/reset', {parola}, {
                headers:{
                    'content-type':'multipart/form-data',
                    'Authorization':`Bearer ${getTokenCookie()}`
                }
            })
            console.log("token")
            console.log("test2")

            return setData({...data, err: "", success: res.data.msg})

        } catch (err) {
            err.response.data.msg && setData({...data, err: err.response.data.msg, success: ''})
        }
        
    }


    return (
        <div className="container">
            <h2>Reseteaza parola</h2>

            <div className="row">
               

                <label htmlFor="parola">Parola</label>
                <input type="parola" name="parola" id="parola" value={parola}
                onChange={handleChangeInput} />

                <label htmlFor="cf_password">Confirma parola</label>
                <input type="parola" name="cf_password" id="cf_password" value={cf_password}
                onChange={handleChangeInput} />         

                <button onClick={handleResetPass}>Reseteaza Parola</button>
            </div>
        </div>
    )
}

export default ResetPassword

GetAutToken->

export const getTokenCookie = () => cookie.load('cookie_token'); 

->for token, cookie_token= inspect element-> application-> cookies and see coockie

Route

import React,{ useEffect, useState } from 'react';
import { Switch, Route, BrowserRouter } from 'react-router-dom';
import Main from './hoc/Main';
import Loader from 'utils/loader';
import AuthGuard from './hoc/authGuard';
import Header from './componente/navigare/footer';
import Footer from './componente/navigare/header';
import Home from './componente/home';
import RegisterLogin from './componente/auth'
import { useDispatch, useSelector } from 'react-redux';
import { userIsAuth,userSignOut } from 'magazin/actiuni/ActiuneUser';
import Dashboard from './componente/dashboard';
import UserInfo from './componente/dashboard/user/info';
import AdminProducts from './componente/dashboard/admin/produse';
import AddProduct from './componente/dashboard/admin/produse/addedit/add';
import EditProduct from './componente/dashboard/admin/produse/addedit/edit';
import Shop from './componente/magazin2';
import ProductDetail from './componente/produs';
import UserCart from './componente/dashboard/user/cos';
import ManageSite from  './componente/dashboard/admin/site';
import ActivationEmail from './componente/auth/ActivationEmail'
import ForgotPassword from './componente/auth/forgotPass';
import ResetPassword from './componente/auth/Resetpass';
import Newpass from './componente/auth/newpass';
const Routes = (props) => {
  const [loading, setLoading] = useState(true);
  const users = useSelector(state => state.users);
  const dispatch = useDispatch();
  const signOutUser = () => {
    dispatch(userSignOut())
  }


  useEffect(() => {
    dispatch(userIsAuth())
  }, [dispatch])


  useEffect(()=>{
    if(users.auth !== null){
      setLoading(false)
    }
  },[users])


  return (
    <BrowserRouter>
      { loading ?
        <Loader full={true} />
        :
        <>
     <Footer 
    users={users}
    signOutUser={signOutUser}
     />
    
          <Main>
            <Switch>
            <Route path="/users/reset/:token" component={ResetPassword}/>
            <Route path="/user/newpass/" component={Newpass}/>
            <Route path="/dashboard/admin/manage_site" component={AuthGuard(ManageSite)} />
            <Route path="/verification?t=emailToken" component={ActivationEmail} />
            <Route path="/forgot" component={ForgotPassword} />
            <Route path="/dashboard/admin/edit_product/:id" component={AuthGuard(EditProduct)} />
            <Route path="/dashboard/admin/add_products" component={AuthGuard(AddProduct)} />
            <Route path="/dashboard/admin/admin_products" component={AuthGuard(AdminProducts)} />
            <Route path="/dashboard/user/user_cart" component={AuthGuard(UserCart)} />
            <Route path="/dashboard/user/user_info" component={AuthGuard(UserInfo)} />
            <Route path="/dashboard" component={AuthGuard(Dashboard)} />
            <Route path="/produse_detalii/:id" component={ProductDetail} />
            <Route path="/magazin" component={Shop} />
              <Route path="/sign_in" component={RegisterLogin} />
              <Route path="/" component={Home} />
            </Switch>
          </Main>
     
          <Header
           users={users}
           signOutUser={signOutUser}
          />
        </>
      }


    </BrowserRouter>
  );
}

export default Routes;

Source: Ask Javascript Questions

LEAVE A COMMENT