2024-03-18 13:58:05 +01:00
import type { Request , Response } from 'express' ;
2021-04-16 15:29:23 +02:00
import Controller from '../controller' ;
2024-03-18 13:58:05 +01:00
import type UserService from '../../services/user-service' ;
import type { Logger } from '../../logger' ;
import type { IUnleashConfig } from '../../types/option' ;
import type { IUnleashServices } from '../../types' ;
2021-12-03 12:46:50 +01:00
import { NONE } from '../../types/permissions' ;
2022-07-01 08:06:33 +02:00
import { createRequestSchema } from '../../openapi/util/create-request-schema' ;
import { createResponseSchema } from '../../openapi/util/create-response-schema' ;
2024-03-18 13:58:05 +01:00
import type { OpenApiService } from '../../services/openapi-service' ;
2022-06-22 13:31:41 +02:00
import {
tokenUserSchema ,
2024-03-18 13:58:05 +01:00
type TokenUserSchema ,
2022-06-22 13:31:41 +02:00
} from '../../openapi/spec/token-user-schema' ;
2024-03-18 13:58:05 +01:00
import type { EmailSchema } from '../../openapi/spec/email-schema' ;
2023-07-04 10:31:54 +02:00
import {
emptyResponse ,
getStandardResponses ,
} from '../../openapi/util/standard-responses' ;
2024-02-21 08:49:54 +01:00
import rateLimit from 'express-rate-limit' ;
import { minutesToMilliseconds } from 'date-fns' ;
2021-04-16 15:29:23 +02:00
interface IValidateQuery {
token : string ;
}
interface IChangePasswordBody {
token : string ;
password : string ;
}
2021-04-27 09:16:44 +02:00
interface SessionRequest < PARAMS , QUERY , BODY , K >
extends Request < PARAMS , QUERY , BODY , K > {
user ? ;
}
2021-04-16 15:29:23 +02:00
class ResetPasswordController extends Controller {
2021-04-27 09:16:44 +02:00
private userService : UserService ;
2021-04-16 15:29:23 +02:00
2022-06-22 13:31:41 +02:00
private openApiService : OpenApiService ;
2021-04-27 09:16:44 +02:00
private logger : Logger ;
2021-04-16 15:29:23 +02:00
2022-06-22 13:31:41 +02:00
constructor (
config : IUnleashConfig ,
{
userService ,
openApiService ,
} : Pick < IUnleashServices , ' userService ' | ' openApiService ' > ,
) {
2021-04-16 15:29:23 +02:00
super ( config ) ;
this . logger = config . getLogger (
'lib/routes/auth/reset-password-controller.ts' ,
) ;
2022-06-22 13:31:41 +02:00
this . openApiService = openApiService ;
2021-04-16 15:29:23 +02:00
this . userService = userService ;
2022-06-22 13:31:41 +02:00
this . route ( {
method : 'get' ,
path : '/validate' ,
handler : this.validateToken ,
permission : NONE ,
middleware : [
openApiService . validPath ( {
2023-07-04 10:31:54 +02:00
summary : 'Validates a token' ,
description :
'If the token is valid returns the user that owns the token' ,
2022-08-12 11:37:57 +02:00
tags : [ 'Auth' ] ,
2022-06-22 13:31:41 +02:00
operationId : 'validateToken' ,
2023-07-04 10:31:54 +02:00
responses : {
200 : createResponseSchema ( 'tokenUserSchema' ) ,
. . . getStandardResponses ( 401 , 415 ) ,
} ,
2022-06-22 13:31:41 +02:00
} ) ,
] ,
} ) ;
this . route ( {
method : 'post' ,
path : '/password' ,
handler : this.changePassword ,
permission : NONE ,
middleware : [
openApiService . validPath ( {
2022-08-12 11:37:57 +02:00
tags : [ 'Auth' ] ,
2023-07-04 10:31:54 +02:00
summary : ` Changes a user password ` ,
description :
'Allows users with a valid reset token to reset their password without remembering their old password' ,
2022-06-22 13:31:41 +02:00
operationId : 'changePassword' ,
requestBody : createRequestSchema ( 'changePasswordSchema' ) ,
2023-07-04 10:31:54 +02:00
responses : {
200 : emptyResponse ,
. . . getStandardResponses ( 401 , 403 , 415 ) ,
} ,
2022-06-22 13:31:41 +02:00
} ) ,
] ,
} ) ;
this . route ( {
method : 'post' ,
path : '/validate-password' ,
handler : this.validatePassword ,
permission : NONE ,
middleware : [
openApiService . validPath ( {
2022-08-12 11:37:57 +02:00
tags : [ 'Auth' ] ,
2023-07-04 10:31:54 +02:00
summary : 'Validates password' ,
description :
'Verifies that the password adheres to the [Unleash password guidelines](https://docs.getunleash.io/reference/deploy/securing-unleash#password-requirements)' ,
2022-06-22 13:31:41 +02:00
operationId : 'validatePassword' ,
requestBody : createRequestSchema ( 'validatePasswordSchema' ) ,
2023-07-04 10:31:54 +02:00
responses : {
200 : emptyResponse ,
. . . getStandardResponses ( 400 , 415 ) ,
} ,
2022-06-22 13:31:41 +02:00
} ) ,
] ,
} ) ;
this . route ( {
method : 'post' ,
path : '/password-email' ,
handler : this.sendResetPasswordEmail ,
permission : NONE ,
middleware : [
openApiService . validPath ( {
2022-08-12 11:37:57 +02:00
tags : [ 'Auth' ] ,
2023-07-04 10:31:54 +02:00
summary : 'Reset password' ,
description :
'Requests a password reset email for the user. This email can be used to reset the password for a user that has forgotten their password' ,
2022-06-22 13:31:41 +02:00
operationId : 'sendResetPasswordEmail' ,
2022-06-22 14:55:43 +02:00
requestBody : createRequestSchema ( 'emailSchema' ) ,
2023-07-04 10:31:54 +02:00
responses : {
200 : emptyResponse ,
. . . getStandardResponses ( 401 , 404 , 415 ) ,
} ,
2022-06-22 13:31:41 +02:00
} ) ,
2024-02-21 08:49:54 +01:00
rateLimit ( {
windowMs : minutesToMilliseconds ( 1 ) ,
max : config.rateLimiting.passwordResetMaxPerMinute ,
validate : false ,
standardHeaders : true ,
legacyHeaders : false ,
} ) ,
2022-06-22 13:31:41 +02:00
] ,
} ) ;
2021-04-16 15:29:23 +02:00
}
2022-06-22 14:55:43 +02:00
async sendResetPasswordEmail (
req : Request < unknown , unknown , EmailSchema > ,
res : Response ,
) : Promise < void > {
2021-04-16 15:29:23 +02:00
const { email } = req . body ;
2021-08-13 10:36:19 +02:00
await this . userService . createResetPasswordEmail ( email ) ;
res . status ( 200 ) . end ( ) ;
2021-04-16 15:29:23 +02:00
}
async validatePassword ( req : Request , res : Response ) : Promise < void > {
const { password } = req . body ;
2021-08-13 10:36:19 +02:00
this . userService . validatePassword ( password ) ;
res . status ( 200 ) . end ( ) ;
2021-04-16 15:29:23 +02:00
}
async validateToken (
req : Request < unknown , unknown , unknown , IValidateQuery > ,
2022-06-22 13:31:41 +02:00
res : Response < TokenUserSchema > ,
2021-04-16 15:29:23 +02:00
) : Promise < void > {
const { token } = req . query ;
2021-08-13 10:36:19 +02:00
const user = await this . userService . getUserForToken ( token ) ;
await this . logout ( req ) ;
2022-06-22 13:31:41 +02:00
this . openApiService . respondWithValidation < TokenUserSchema > (
200 ,
res ,
tokenUserSchema . $id ,
user ,
) ;
2021-04-16 15:29:23 +02:00
}
async changePassword (
req : Request < unknown , unknown , IChangePasswordBody , unknown > ,
res : Response ,
) : Promise < void > {
2021-04-27 09:16:44 +02:00
await this . logout ( req ) ;
2021-04-16 15:29:23 +02:00
const { token , password } = req . body ;
2021-08-13 10:36:19 +02:00
await this . userService . resetPassword ( token , password ) ;
res . status ( 200 ) . end ( ) ;
2021-04-16 15:29:23 +02:00
}
2021-04-27 09:16:44 +02:00
private async logout ( req : SessionRequest < any , any , any , any > ) {
if ( req . session ) {
2021-08-12 15:04:37 +02:00
req . session . destroy ( ( ) = > { } ) ;
2021-04-27 09:16:44 +02:00
}
}
2021-04-16 15:29:23 +02:00
}
export default ResetPasswordController ;