use cookie to send refreshToken
This commit is contained in:
		
							parent
							
								
									9be9a1a2b3
								
							
						
					
					
						commit
						699897b071
					
				| @ -39,6 +39,8 @@ | |||||||
|     "bcrypt": "^5.1.0", |     "bcrypt": "^5.1.0", | ||||||
|     "class-transformer": "^0.5.1", |     "class-transformer": "^0.5.1", | ||||||
|     "class-validator": "^0.14.0", |     "class-validator": "^0.14.0", | ||||||
|  |     "cookie-parser": "^1.4.6", | ||||||
|  |     "ms": "3.0.0-canary.1", | ||||||
|     "nestjs-prisma": "^0.20.0", |     "nestjs-prisma": "^0.20.0", | ||||||
|     "nodemailer": "^6.9.1", |     "nodemailer": "^6.9.1", | ||||||
|     "passport": "^0.6.0", |     "passport": "^0.6.0", | ||||||
| @ -50,6 +52,7 @@ | |||||||
|     "@nestjs/cli": "^9.0.0", |     "@nestjs/cli": "^9.0.0", | ||||||
|     "@nestjs/schematics": "^9.0.0", |     "@nestjs/schematics": "^9.0.0", | ||||||
|     "@types/bcrypt": "^5.0.0", |     "@types/bcrypt": "^5.0.0", | ||||||
|  |     "@types/cookie-parser": "^1.4.3", | ||||||
|     "@types/express": "^4.17.13", |     "@types/express": "^4.17.13", | ||||||
|     "@types/node": "18.11.18", |     "@types/node": "18.11.18", | ||||||
|     "@types/passport-jwt": "^3.0.8", |     "@types/passport-jwt": "^3.0.8", | ||||||
|  | |||||||
| @ -14,6 +14,7 @@ specifiers: | |||||||
|   '@nestjs/swagger': ^6.2.1 |   '@nestjs/swagger': ^6.2.1 | ||||||
|   '@prisma/client': ^4.10.1 |   '@prisma/client': ^4.10.1 | ||||||
|   '@types/bcrypt': ^5.0.0 |   '@types/bcrypt': ^5.0.0 | ||||||
|  |   '@types/cookie-parser': ^1.4.3 | ||||||
|   '@types/express': ^4.17.13 |   '@types/express': ^4.17.13 | ||||||
|   '@types/jsonwebtoken': ^9.0.1 |   '@types/jsonwebtoken': ^9.0.1 | ||||||
|   '@types/node': 18.11.18 |   '@types/node': 18.11.18 | ||||||
| @ -23,11 +24,13 @@ specifiers: | |||||||
|   bcrypt: ^5.1.0 |   bcrypt: ^5.1.0 | ||||||
|   class-transformer: ^0.5.1 |   class-transformer: ^0.5.1 | ||||||
|   class-validator: ^0.14.0 |   class-validator: ^0.14.0 | ||||||
|  |   cookie-parser: ^1.4.6 | ||||||
|   eslint: ^8.0.1 |   eslint: ^8.0.1 | ||||||
|   eslint-config-prettier: ^8.3.0 |   eslint-config-prettier: ^8.3.0 | ||||||
|   eslint-plugin-prettier: ^4.0.0 |   eslint-plugin-prettier: ^4.0.0 | ||||||
|   husky: ^8.0.0 |   husky: ^8.0.0 | ||||||
|   lint-staged: ^13.1.2 |   lint-staged: ^13.1.2 | ||||||
|  |   ms: 3.0.0-canary.1 | ||||||
|   nestjs-prisma: ^0.20.0 |   nestjs-prisma: ^0.20.0 | ||||||
|   nodemailer: ^6.9.1 |   nodemailer: ^6.9.1 | ||||||
|   passport: ^0.6.0 |   passport: ^0.6.0 | ||||||
| @ -58,6 +61,8 @@ dependencies: | |||||||
|   bcrypt: 5.1.0 |   bcrypt: 5.1.0 | ||||||
|   class-transformer: 0.5.1 |   class-transformer: 0.5.1 | ||||||
|   class-validator: 0.14.0 |   class-validator: 0.14.0 | ||||||
|  |   cookie-parser: 1.4.6 | ||||||
|  |   ms: 3.0.0-canary.1 | ||||||
|   nestjs-prisma: 0.20.0_uhhmeuf5jto6tk72f36tv2cdfe |   nestjs-prisma: 0.20.0_uhhmeuf5jto6tk72f36tv2cdfe | ||||||
|   nodemailer: 6.9.1 |   nodemailer: 6.9.1 | ||||||
|   passport: 0.6.0 |   passport: 0.6.0 | ||||||
| @ -69,6 +74,7 @@ devDependencies: | |||||||
|   '@nestjs/cli': 9.2.0 |   '@nestjs/cli': 9.2.0 | ||||||
|   '@nestjs/schematics': 9.0.4_typescript@4.9.5 |   '@nestjs/schematics': 9.0.4_typescript@4.9.5 | ||||||
|   '@types/bcrypt': 5.0.0 |   '@types/bcrypt': 5.0.0 | ||||||
|  |   '@types/cookie-parser': 1.4.3 | ||||||
|   '@types/express': 4.17.17 |   '@types/express': 4.17.17 | ||||||
|   '@types/node': 18.11.18 |   '@types/node': 18.11.18 | ||||||
|   '@types/passport-jwt': 3.0.8 |   '@types/passport-jwt': 3.0.8 | ||||||
| @ -756,6 +762,12 @@ packages: | |||||||
|       '@types/node': 18.11.18 |       '@types/node': 18.11.18 | ||||||
|     dev: true |     dev: true | ||||||
| 
 | 
 | ||||||
|  |   /@types/cookie-parser/1.4.3: | ||||||
|  |     resolution: {integrity: sha512-CqSKwFwefj4PzZ5n/iwad/bow2hTCh0FlNAeWLtQM3JA/NX/iYagIpWG2cf1bQKQ2c9gU2log5VUCrn7LDOs0w==} | ||||||
|  |     dependencies: | ||||||
|  |       '@types/express': 4.17.17 | ||||||
|  |     dev: true | ||||||
|  | 
 | ||||||
|   /@types/ejs/3.1.2: |   /@types/ejs/3.1.2: | ||||||
|     resolution: {integrity: sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==} |     resolution: {integrity: sha512-ZmiaE3wglXVWBM9fyVC17aGPkLo/UgaOjEiI2FXQfyczrCefORPxIe+2dVmnmk3zkVIbizjrlQzmPGhSYGXG5g==} | ||||||
|     requiresBuild: true |     requiresBuild: true | ||||||
| @ -1754,10 +1766,23 @@ packages: | |||||||
|     engines: {node: '>= 0.6'} |     engines: {node: '>= 0.6'} | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|  |   /cookie-parser/1.4.6: | ||||||
|  |     resolution: {integrity: sha512-z3IzaNjdwUC2olLIB5/ITd0/setiaFMLYiZJle7xg5Fe9KWAceil7xszYfHHBtDFYLSgJduS2Ty0P1uJdPDJeA==} | ||||||
|  |     engines: {node: '>= 0.8.0'} | ||||||
|  |     dependencies: | ||||||
|  |       cookie: 0.4.1 | ||||||
|  |       cookie-signature: 1.0.6 | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /cookie-signature/1.0.6: |   /cookie-signature/1.0.6: | ||||||
|     resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} |     resolution: {integrity: sha512-QADzlaHc8icV8I7vbaJXJwod9HWYp8uCqf1xa4OfNu1T7JVxQIrUgOWtHdNDtPiywmFbiS12VjotIXLrKM3orQ==} | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|  |   /cookie/0.4.1: | ||||||
|  |     resolution: {integrity: sha512-ZwrFkGJxUR3EIoXtO+yVE69Eb7KlixbaeAWfBQB9vVsNn/o+Yw69gBWSSDK825hQNdN+wF8zELf3dFNl/kxkUA==} | ||||||
|  |     engines: {node: '>= 0.6'} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /cookie/0.5.0: |   /cookie/0.5.0: | ||||||
|     resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} |     resolution: {integrity: sha512-YZ3GUyn/o8gfKJlnlX7g7xq4gyO6OSuhGPKaaGssGB2qgDUS0gPgtTvoyZLTt9Ab6dC4hfc9dV5arkvc/OCmrw==} | ||||||
|     engines: {node: '>= 0.6'} |     engines: {node: '>= 0.6'} | ||||||
| @ -3989,6 +4014,11 @@ packages: | |||||||
|     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} |     resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} | ||||||
|     dev: false |     dev: false | ||||||
| 
 | 
 | ||||||
|  |   /ms/3.0.0-canary.1: | ||||||
|  |     resolution: {integrity: sha512-kh8ARjh8rMN7Du2igDRO9QJnqCb2xYTJxyQYK7vJJS4TvLLmsbyhiKpSW+t+y26gyOyMd0riphX0GeWKU3ky5g==} | ||||||
|  |     engines: {node: '>=12.13'} | ||||||
|  |     dev: false | ||||||
|  | 
 | ||||||
|   /multer/1.4.4-lts.1: |   /multer/1.4.4-lts.1: | ||||||
|     resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} |     resolution: {integrity: sha512-WeSGziVj6+Z2/MwQo3GvqzgR+9Uc+qt8SwHKh3gvNPiISKfsMfG4SvCOFYlxxgkXt7yIV2i1yczehm0EOKIxIg==} | ||||||
|     engines: {node: '>= 6.0.0'} |     engines: {node: '>= 6.0.0'} | ||||||
|  | |||||||
| @ -1,10 +1,11 @@ | |||||||
| import { registerAs, ConfigType } from '@nestjs/config' | import { registerAs, ConfigType } from '@nestjs/config' | ||||||
|  | import ms, { StringValue } from 'ms' | ||||||
| 
 | 
 | ||||||
| export const securityConfig = registerAs('security', () => ({ | export const securityConfig = registerAs('security', () => ({ | ||||||
|   jwt_access_secret: process.env.JWT_ACCESS_SECRET || 'JWT_ACCESS_SECRET', |   jwt_access_secret: process.env.JWT_ACCESS_SECRET || 'JWT_ACCESS_SECRET', | ||||||
|   jwt_refresh_secret: process.env.JWT_REFRESH_SECRET || 'JWT_REFRESH_SECRET', |   jwt_refresh_secret: process.env.JWT_REFRESH_SECRET || 'JWT_REFRESH_SECRET', | ||||||
|   expiresIn: process.env.expiresIn || '15m', |   expiresIn: ms((process.env.expiresIn || '15m') as StringValue), | ||||||
|   refreshIn: process.env.refreshIn || '7d', |   refreshIn: ms((process.env.refreshIn || '7d') as StringValue), | ||||||
|   bcryptSaltOrRound: Number(process.env.bcryptSaltOrRound) || 10, |   bcryptSaltOrRound: Number(process.env.bcryptSaltOrRound) || 10, | ||||||
| })) | })) | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										9
									
								
								src/common/decorators/cookies.decorator.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/common/decorators/cookies.decorator.ts
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,9 @@ | |||||||
|  | import { createParamDecorator, ExecutionContext } from '@nestjs/common' | ||||||
|  | 
 | ||||||
|  | export const Cookies = createParamDecorator( | ||||||
|  |   (data: string, ctx: ExecutionContext) => { | ||||||
|  |     const request = ctx.switchToHttp().getRequest() | ||||||
|  | 
 | ||||||
|  |     return data ? request.cookies?.[data] : request.cookies | ||||||
|  |   }, | ||||||
|  | ) | ||||||
| @ -3,6 +3,7 @@ import { ValidationPipe } from '@nestjs/common' | |||||||
| import { ConfigService } from '@nestjs/config' | import { ConfigService } from '@nestjs/config' | ||||||
| import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' | import { DocumentBuilder, SwaggerModule } from '@nestjs/swagger' | ||||||
| import { PrismaClientExceptionFilter, PrismaService } from 'nestjs-prisma' | import { PrismaClientExceptionFilter, PrismaService } from 'nestjs-prisma' | ||||||
|  | import * as CookieParser from 'cookie-parser' | ||||||
| import { JwtExceptionsFilter } from './common/filters/jwt-exceptions.filter' | import { JwtExceptionsFilter } from './common/filters/jwt-exceptions.filter' | ||||||
| import { AppModule } from './app.module' | import { AppModule } from './app.module' | ||||||
| 
 | 
 | ||||||
| @ -21,9 +22,13 @@ async function bootstrap() { | |||||||
|   app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter)) |   app.useGlobalFilters(new PrismaClientExceptionFilter(httpAdapter)) | ||||||
|   app.useGlobalFilters(new JwtExceptionsFilter(httpAdapter)) |   app.useGlobalFilters(new JwtExceptionsFilter(httpAdapter)) | ||||||
| 
 | 
 | ||||||
|  |   // CookieParser
 | ||||||
|  |   app.use(CookieParser()) | ||||||
|  | 
 | ||||||
|   // Swagger Api
 |   // Swagger Api
 | ||||||
|   const options = new DocumentBuilder() |   const options = new DocumentBuilder() | ||||||
|     .addBearerAuth() |     .addBearerAuth() | ||||||
|  |     .addCookieAuth('refreshToken') | ||||||
|     .setTitle('Nest Project') |     .setTitle('Nest Project') | ||||||
|     .setDescription('The Nest-Project API description') |     .setDescription('The Nest-Project API description') | ||||||
|     .setVersion('1.0') |     .setVersion('1.0') | ||||||
|  | |||||||
| @ -1,6 +1,5 @@ | |||||||
| import '@nestjs/mapped-types' | import '@nestjs/mapped-types' | ||||||
| import { ApiProperty } from '@nestjs/swagger' | import { ApiProperty } from '@nestjs/swagger' | ||||||
| import { IsString } from 'class-validator' |  | ||||||
| 
 | 
 | ||||||
| export class Token { | export class Token { | ||||||
|   @ApiProperty() |   @ApiProperty() | ||||||
| @ -9,11 +8,6 @@ export class Token { | |||||||
|   refreshToken: string |   refreshToken: string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export class TokenRefreshPayload { |  | ||||||
|   @IsString() |  | ||||||
|   refreshToken: string |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| export class TokenPayload { | export class TokenPayload { | ||||||
|   userId: string |   userId: string | ||||||
| } | } | ||||||
|  | |||||||
| @ -5,10 +5,14 @@ import { | |||||||
|   Patch, |   Patch, | ||||||
|   Put, |   Put, | ||||||
|   Body, |   Body, | ||||||
|  |   Res, | ||||||
|   UseInterceptors, |   UseInterceptors, | ||||||
|   BadRequestException, |   BadRequestException, | ||||||
|   Query, |   Query, | ||||||
|  |   UnauthorizedException, | ||||||
| } from '@nestjs/common' | } from '@nestjs/common' | ||||||
|  | import type { Response } from 'express' | ||||||
|  | import { Cookies } from 'src/common/decorators/cookies.decorator' | ||||||
| import { MeService } from './me.service' | import { MeService } from './me.service' | ||||||
| import { ApiTags, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger' | import { ApiTags, ApiOperation, ApiUnauthorizedResponse } from '@nestjs/swagger' | ||||||
| import { User } from 'src/common/decorators/user.decorator' | import { User } from 'src/common/decorators/user.decorator' | ||||||
| @ -19,7 +23,6 @@ import { UserEntity } from './entities/user.entity' | |||||||
| import { DeleteUserDto } from './dto/delete-user.dto' | import { DeleteUserDto } from './dto/delete-user.dto' | ||||||
| import { ChangePassword } from './dto/change-password.dto' | import { ChangePassword } from './dto/change-password.dto' | ||||||
| import { UpdateUserDto } from './dto/update-user.dto' | import { UpdateUserDto } from './dto/update-user.dto' | ||||||
| import { TokenRefreshPayload } from './dto/token.dto' |  | ||||||
| import { ChangeEmailDto } from './dto/change-email.dto' | import { ChangeEmailDto } from './dto/change-email.dto' | ||||||
| 
 | 
 | ||||||
| @Controller('api/users/me') | @Controller('api/users/me') | ||||||
| @ -93,7 +96,18 @@ export class MeController { | |||||||
|   @Put('token') |   @Put('token') | ||||||
|   @ApiOperation({ summary: '刷新token' }) |   @ApiOperation({ summary: '刷新token' }) | ||||||
|   @ApiUnauthorizedResponse({ description: 'Unauthorized' }) |   @ApiUnauthorizedResponse({ description: 'Unauthorized' }) | ||||||
|   async updateAccessToken(@Body() payload: TokenRefreshPayload) { |   async updateAccessToken( | ||||||
|     return this.meService.updateAccessToken(payload.refreshToken) |     @Res({ passthrough: true }) res: Response, | ||||||
|  |     @Cookies('refreshToken') refreshToken: string, | ||||||
|  |   ) { | ||||||
|  |     if (!refreshToken) { | ||||||
|  |       throw new UnauthorizedException('no refresh token') | ||||||
|  |     } | ||||||
|  |     try { | ||||||
|  |       return this.meService.updateAccessToken(refreshToken) | ||||||
|  |     } catch (err) { | ||||||
|  |       res.clearCookie('refreshToken') | ||||||
|  |       throw err | ||||||
|  |     } | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
| @ -77,17 +77,13 @@ export class MeService { | |||||||
|     if (iat * 1000 < user.updatedAt.getTime()) { |     if (iat * 1000 < user.updatedAt.getTime()) { | ||||||
|       throw new UnauthorizedException('token失效,请重新登录') |       throw new UnauthorizedException('token失效,请重新登录') | ||||||
|     } |     } | ||||||
|     return { |     return this.jwtService.sign( | ||||||
|       userId, |       { userId }, | ||||||
|       refreshToken: refreshToken, |       { | ||||||
|       accessToken: this.jwtService.sign( |         secret: this.secureConfig.jwt_access_secret, | ||||||
|         { userId }, |         expiresIn: this.secureConfig.refreshIn, | ||||||
|         { |       }, | ||||||
|           secret: this.secureConfig.jwt_refresh_secret, |     ) | ||||||
|           expiresIn: this.secureConfig.refreshIn, |  | ||||||
|         }, |  | ||||||
|       ), |  | ||||||
|     } |  | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   private async checkPassword(pwd: string, hashPwd: string) { |   private async checkPassword(pwd: string, hashPwd: string) { | ||||||
|  | |||||||
| @ -1,31 +1,64 @@ | |||||||
| import { Controller, Post, Patch, Body, Param } from '@nestjs/common' | import { Controller, Inject, Post, Patch, Body, Res } from '@nestjs/common' | ||||||
|  | import type { Response, CookieOptions } from 'express' | ||||||
| import { UsersService } from './users.service' | import { UsersService } from './users.service' | ||||||
| import { ApiTags, ApiOperation } from '@nestjs/swagger' | import { ApiTags, ApiOperation } from '@nestjs/swagger' | ||||||
| import { CreateUserDto } from './dto/create-user.dto' | import { CreateUserDto } from './dto/create-user.dto' | ||||||
| import { LoginInputDto } from './dto/login-input.dto' | import { LoginInputDto } from './dto/login-input.dto' | ||||||
| import { ResetPassword } from './dto/reset-password.dto' | import { ResetPassword } from './dto/reset-password.dto' | ||||||
|  | import { securityConfig, SecurityConfig } from 'src/common/configs' | ||||||
| 
 | 
 | ||||||
| @Controller('api/users') | @Controller('api/users') | ||||||
| @ApiTags('Users') | @ApiTags('Users') | ||||||
| export class UsersController { | export class UsersController { | ||||||
|   constructor(private readonly usersService: UsersService) {} |   private refreshTokenCookieConfig: CookieOptions = { | ||||||
|  |     path: '/api/users/me/token', | ||||||
|  |     secure: true, | ||||||
|  |     maxAge: this.secureConfig.refreshIn / 1000, | ||||||
|  |     httpOnly: true, | ||||||
|  |     sameSite: 'lax', | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   constructor( | ||||||
|  |     private readonly usersService: UsersService, | ||||||
|  |     @Inject(securityConfig.KEY) private secureConfig: SecurityConfig, | ||||||
|  |   ) {} | ||||||
| 
 | 
 | ||||||
|   @Post() |   @Post() | ||||||
|   @ApiOperation({ summary: '邮箱注册' }) |   @ApiOperation({ summary: '邮箱注册' }) | ||||||
|   async registerByEmail(@Body() userData: CreateUserDto) { |   async registerByEmail( | ||||||
|     return this.usersService.registerByEmail(userData) |     @Body() userData: CreateUserDto, | ||||||
|  |     @Res({ passthrough: true }) res: Response, | ||||||
|  |   ) { | ||||||
|  |     const { refreshToken, accessToken } = | ||||||
|  |       await this.usersService.registerByEmail(userData) | ||||||
|  |     res.cookie('refreshToken', refreshToken, this.refreshTokenCookieConfig) | ||||||
|  |     return accessToken | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   // TODO: 限制调用频率,避免暴力破解
 |   // TODO: 限制调用频率,避免暴力破解
 | ||||||
|   @Post('token') |   @Post('token') | ||||||
|   @ApiOperation({ summary: '邮箱登录' }) |   @ApiOperation({ summary: '邮箱登录' }) | ||||||
|   async loginByEmail(@Body() user: LoginInputDto) { |   async loginByEmail( | ||||||
|     return this.usersService.loginByEmail(user.email, user.password) |     @Body() user: LoginInputDto, | ||||||
|  |     @Res({ passthrough: true }) res: Response, | ||||||
|  |   ) { | ||||||
|  |     const { refreshToken, accessToken } = await this.usersService.loginByEmail( | ||||||
|  |       user.email, | ||||||
|  |       user.password, | ||||||
|  |     ) | ||||||
|  |     res.cookie('refreshToken', refreshToken, this.refreshTokenCookieConfig) | ||||||
|  |     return accessToken | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|   @Patch('password') |   @Patch('password') | ||||||
|   @ApiOperation({ summary: '找回密码' }) |   @ApiOperation({ summary: '找回密码' }) | ||||||
|   async forgetPassword(@Body() payload: ResetPassword) { |   async forgetPassword( | ||||||
|     return this.usersService.resetPasswordByEmail(payload) |     @Body() payload: ResetPassword, | ||||||
|  |     @Res({ passthrough: true }) res: Response, | ||||||
|  |   ) { | ||||||
|  |     const { refreshToken, accessToken } = | ||||||
|  |       await this.usersService.resetPasswordByEmail(payload) | ||||||
|  |     res.cookie('refreshToken', refreshToken, this.refreshTokenCookieConfig) | ||||||
|  |     return accessToken | ||||||
|   } |   } | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user