@@ -2,8 +2,10 @@ import crypto from 'crypto';
22import { CookieOptions , NextFunction , Request , Response } from 'express' ;
33import bcrypt from 'bcryptjs' ;
44import {
5+ ForgotPasswordInput ,
56 LoginUserInput ,
67 RegisterUserInput ,
8+ ResetPasswordInput ,
79 VerifyEmailInput ,
810} from '../schemas/user.schema' ;
911import {
@@ -263,3 +265,132 @@ export const verifyEmailHandler = async (
263265 next ( err ) ;
264266 }
265267} ;
268+
269+ export const forgotPasswordHandler = async (
270+ req : Request <
271+ Record < string , never > ,
272+ Record < string , never > ,
273+ ForgotPasswordInput
274+ > ,
275+ res : Response ,
276+ next : NextFunction
277+ ) => {
278+ try {
279+ // Get the user from the collection
280+ const user = await findUser ( { email : req . body . email . toLowerCase ( ) } ) ;
281+ const message =
282+ 'You will receive a reset email if user with that email exist' ;
283+ if ( ! user ) {
284+ return res . status ( 200 ) . json ( {
285+ status : 'success' ,
286+ message,
287+ } ) ;
288+ }
289+
290+ if ( ! user . verified ) {
291+ return res . status ( 403 ) . json ( {
292+ status : 'fail' ,
293+ message : 'Account not verified' ,
294+ } ) ;
295+ }
296+
297+ if ( user . provider ) {
298+ return res . status ( 403 ) . json ( {
299+ status : 'fail' ,
300+ message :
301+ 'We found your account. It looks like you registered with a social auth account. Try signing in with social auth.' ,
302+ } ) ;
303+ }
304+
305+ const resetToken = crypto . randomBytes ( 32 ) . toString ( 'hex' ) ;
306+ const passwordResetToken = crypto
307+ . createHash ( 'sha256' )
308+ . update ( resetToken )
309+ . digest ( 'hex' ) ;
310+
311+ await updateUser (
312+ { id : user . id } ,
313+ {
314+ passwordResetToken,
315+ passwordResetAt : new Date ( Date . now ( ) + 10 * 60 * 1000 ) ,
316+ } ,
317+ { email : true }
318+ ) ;
319+
320+ try {
321+ const url = `${ config . get < string > ( 'origin' ) } /resetPassword/${ resetToken } ` ;
322+ await new Email ( user , url ) . sendPasswordResetToken ( ) ;
323+
324+ res . status ( 200 ) . json ( {
325+ status : 'success' ,
326+ message,
327+ } ) ;
328+ } catch ( err : any ) {
329+ await updateUser (
330+ { id : user . id } ,
331+ { passwordResetToken : null , passwordResetAt : null } ,
332+ { }
333+ ) ;
334+ return res . status ( 500 ) . json ( {
335+ status : 'error' ,
336+ message : 'There was an error sending email' ,
337+ } ) ;
338+ }
339+ } catch ( err : any ) {
340+ next ( err ) ;
341+ }
342+ } ;
343+
344+ export const resetPasswordHandler = async (
345+ req : Request <
346+ ResetPasswordInput [ 'params' ] ,
347+ Record < string , never > ,
348+ ResetPasswordInput [ 'body' ]
349+ > ,
350+ res : Response ,
351+ next : NextFunction
352+ ) => {
353+ try {
354+ // Get the user from the collection
355+ const passwordResetToken = crypto
356+ . createHash ( 'sha256' )
357+ . update ( req . params . resetToken )
358+ . digest ( 'hex' ) ;
359+
360+ const user = await findUser ( {
361+ passwordResetToken,
362+ passwordResetAt : {
363+ gt : new Date ( ) ,
364+ } ,
365+ } ) ;
366+
367+ if ( ! user ) {
368+ return res . status ( 403 ) . json ( {
369+ status : 'fail' ,
370+ message : 'Invalid token or token has expired' ,
371+ } ) ;
372+ }
373+
374+ const hashedPassword = await bcrypt . hash ( req . body . password , 12 ) ;
375+ // Change password data
376+ await updateUser (
377+ {
378+ id : user . id ,
379+ } ,
380+ {
381+ password : hashedPassword ,
382+ passwordResetToken : null ,
383+ passwordResetAt : null ,
384+ } ,
385+ { email : true }
386+ ) ;
387+
388+ logout ( res ) ;
389+ res . status ( 200 ) . json ( {
390+ status : 'success' ,
391+ message : 'Password data updated successfully' ,
392+ } ) ;
393+ } catch ( err : any ) {
394+ next ( err ) ;
395+ }
396+ } ;
0 commit comments