diff --git a/src/auth/controllers/auth.controller.ts b/src/auth/controllers/auth.controller.ts index 336a8b4..a36d5de 100644 --- a/src/auth/controllers/auth.controller.ts +++ b/src/auth/controllers/auth.controller.ts @@ -10,6 +10,7 @@ import { EnableBiometricRequestDto, ForgetPasswordRequestDto, LoginRequestDto, + RefreshTokenRequestDto, SendForgetPasswordOtpRequestDto, SetEmailRequestDto, setJuniorPasswordRequestDto, @@ -85,6 +86,13 @@ export class AuthController { return this.authService.setJuniorPasscode(setPasscodeDto); } + @Post('refresh-token') + @Public() + async refreshToken(@Body() { refreshToken }: RefreshTokenRequestDto) { + const [res, user] = await this.authService.refreshToken(refreshToken); + return ResponseFactory.data(new LoginResponseDto(res, user)); + } + @Post('login') async login(@Body() loginDto: LoginRequestDto, @Headers(DEVICE_ID_HEADER) deviceId: string) { const [res, user] = await this.authService.login(loginDto, deviceId); diff --git a/src/auth/dtos/request/index.ts b/src/auth/dtos/request/index.ts index a73f75a..16fe0e0 100644 --- a/src/auth/dtos/request/index.ts +++ b/src/auth/dtos/request/index.ts @@ -3,6 +3,7 @@ export * from './disable-biometric.request.dto'; export * from './enable-biometric.request.dto'; export * from './forget-password.request.dto'; export * from './login.request.dto'; +export * from './refresh-token.request.dto'; export * from './send-forget-password-otp.request.dto'; export * from './set-email.request.dto'; export * from './set-junior-password.request.dto'; diff --git a/src/auth/dtos/request/refresh-token.request.dto.ts b/src/auth/dtos/request/refresh-token.request.dto.ts new file mode 100644 index 0000000..3359b05 --- /dev/null +++ b/src/auth/dtos/request/refresh-token.request.dto.ts @@ -0,0 +1,9 @@ +import { ApiProperty } from '@nestjs/swagger'; +import { IsNotEmpty, IsString } from 'class-validator'; +import { i18nValidationMessage as i18n } from 'nestjs-i18n'; +export class RefreshTokenRequestDto { + @ApiProperty() + @IsString({ message: i18n('validation.isString', { path: 'general', property: 'auth.refreshToken' }) }) + @IsNotEmpty({ message: i18n('validation.required', { path: 'general', property: 'auth.refreshToken' }) }) + refreshToken!: string; +} diff --git a/src/auth/services/auth.service.ts b/src/auth/services/auth.service.ts index e7c4e3b..c9f6579 100644 --- a/src/auth/services/auth.service.ts +++ b/src/auth/services/auth.service.ts @@ -19,7 +19,7 @@ import { import { VerifyUserRequestDto } from '../dtos/request/verify-user.request.dto'; import { User } from '../entities'; import { GrantType, Roles } from '../enums'; -import { ILoginResponse } from '../interfaces'; +import { IJwtPayload, ILoginResponse } from '../interfaces'; import { removePadding, verifySignature } from '../utils'; import { DeviceService } from './device.service'; import { UserService } from './user.service'; @@ -197,6 +197,22 @@ export class AuthService { await this.juniorTokenService.invalidateToken(body.qrToken); } + async refreshToken(refreshToken: string): Promise<[ILoginResponse, User]> { + try { + const isValid = await this.jwtService.verifyAsync(refreshToken, { + secret: this.configService.getOrThrow('JWT_REFRESH_TOKEN_SECRET'), + }); + + const user = await this.userService.findUserOrThrow({ id: isValid.sub }); + + const tokens = await this.generateAuthToken(user); + + return [tokens, user]; + } catch (error) { + throw new BadRequestException('AUTH.INVALID_REFRESH_TOKEN'); + } + } + private async loginWithPassword(loginDto: LoginRequestDto, user: User): Promise { const isPasswordValid = bcrypt.compareSync(loginDto.password, user.password);