From d7a051d6c5411f2af311a65f29466bdb90cee54e Mon Sep 17 00:00:00 2001 From: padi-dev-hoangpa <31430555+PhamAnhHoang@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:37:12 +0700 Subject: [PATCH] Refresh token --- apps/api/src/auth/auth.controller.ts | 8 +-- apps/api/src/auth/auth.module.ts | 8 ++- apps/api/src/auth/auth.service.ts | 63 ++++++++++--------- .../auth/interceptors/token.interceptor.ts | 4 +- .../strategies/jwt-refresh-token.strategy.ts | 48 +++++++------- .../api/src/auth/strategies/local.strategy.ts | 7 ++- apps/api/src/user/user.service.ts | 12 +++- .../auth/refreshTokenResponse.interface.ts | 2 +- 8 files changed, 86 insertions(+), 66 deletions(-) diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index d591686..a17f423 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -106,10 +106,10 @@ export class AuthController { async refreshToken( @AuthUser() user: Pure ): Promise { - const accessToken = this.authService.generateAccessToken(user.email); - return { - access_token: accessToken, + const accessToken = this.authService.generateAccessToken(user.email) + return { + access_token: accessToken, status: StatusType.OK - }; + } } } diff --git a/apps/api/src/auth/auth.module.ts b/apps/api/src/auth/auth.module.ts index 94a8281..00c61e8 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -32,6 +32,12 @@ import { JwtRefreshTokenStrategy } from './strategies/jwt-refresh-token.strategy OrganizationModule ], controllers: [AuthController], - providers: [AuthService, LocalStrategy, JwtStrategy, SessionSerializer, JwtRefreshTokenStrategy] + providers: [ + AuthService, + LocalStrategy, + JwtStrategy, + SessionSerializer, + JwtRefreshTokenStrategy + ] }) export class AuthModule {} diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts index 347c8dd..09568a9 100644 --- a/apps/api/src/auth/auth.service.ts +++ b/apps/api/src/auth/auth.service.ts @@ -21,7 +21,7 @@ import { Pure } from '@isomera/interfaces' import { generateRandomStringUtil } from '@isomera/utils' import { OrganizationService } from '../organization/organization.service' import { ConfigService } from '@nestjs/config' -import * as bcrypt from 'bcrypt' +import * as bcrypt from 'bcrypt' @Injectable() export class AuthService { @@ -64,7 +64,12 @@ export class AuthService { } } - async login(email: string, password: string): Promise & { refresh_token: string, access_token: string}> { + async login( + email: string, + password: string + ): Promise< + Partial & { refresh_token: string; access_token: string } + > { let user: UserEntity try { @@ -82,11 +87,11 @@ export class AuthService { } delete user.password - const {refresh_token, access_token} = this.signToken(user); + const { refresh_token, access_token } = this.signToken(user) - await this.storeRefreshToken(user, refresh_token); - - return {...user, refresh_token, access_token} + await this.storeRefreshToken(user, refresh_token) + + return { ...user, refresh_token, access_token } } async verifyPayload(payload: JwtPayload): Promise { @@ -200,35 +205,33 @@ export class AuthService { } async getUserIfRefreshTokenMatched( - email: string, - refreshToken: string, - ): Promise { - const user = await this.userService.findOne({ where: { email } }) - if (!user) { - throw new UnauthorizedException(); - } - await this.verifyPlainContentWithHashedContent( - refreshToken, - user.refreshToken, - ); - return user; - } + email: string, + refreshToken: string + ): Promise { + const user = await this.userService.findOne({ where: { email } }) + if (!user) { + throw new UnauthorizedException() + } + await this.verifyPlainContentWithHashedContent( + refreshToken, + user.refreshToken + ) + return user + } private async verifyPlainContentWithHashedContent( plainText: string, - hashedText: string, -) { - const is_matching = await bcrypt.compare(plainText, hashedText); + hashedText: string + ) { + const is_matching = await bcrypt.compare(plainText, hashedText) if (!is_matching) { - throw new BadRequestException(); + throw new BadRequestException() } -} + } -async storeRefreshToken(user: UserEntity, token: string): Promise { + async storeRefreshToken(user: UserEntity, token: string): Promise { const salt = await bcrypt.genSalt() - const hashed_token = await bcrypt.hash(token, salt); - await this.userService.storeRefreshToken(user, hashed_token); -} - - + const hashed_token = await bcrypt.hash(token, salt) + await this.userService.storeRefreshToken(user, hashed_token) + } } diff --git a/apps/api/src/auth/interceptors/token.interceptor.ts b/apps/api/src/auth/interceptors/token.interceptor.ts index 4fd7dd2..bf53fa8 100644 --- a/apps/api/src/auth/interceptors/token.interceptor.ts +++ b/apps/api/src/auth/interceptors/token.interceptor.ts @@ -17,7 +17,9 @@ export class TokenInterceptor implements NestInterceptor { intercept( context: ExecutionContext, - next: CallHandler & { access_token: string; refresh_token: string }> + next: CallHandler< + Partial & { access_token: string; refresh_token: string } + > ): Observable< Partial & { access_token: string; refresh_token: string } > { diff --git a/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts b/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts index cff996b..92d1af7 100644 --- a/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts +++ b/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts @@ -1,30 +1,28 @@ -import { Request } from 'express'; -import { Injectable } from '@nestjs/common'; -import { PassportStrategy } from '@nestjs/passport'; -import { ExtractJwt, Strategy } from 'passport-jwt'; -import { AuthService } from '../auth.service'; -import { JwtPayload } from '@isomera/interfaces'; +import { Request } from 'express' +import { Injectable } from '@nestjs/common' +import { PassportStrategy } from '@nestjs/passport' +import { ExtractJwt, Strategy } from 'passport-jwt' +import { AuthService } from '../auth.service' +import { JwtPayload } from '@isomera/interfaces' @Injectable() export class JwtRefreshTokenStrategy extends PassportStrategy( - Strategy, - 'jwt-refresh-token', + Strategy, + 'jwt-refresh-token' ) { - constructor( - private readonly authService: AuthService, - ) { - super({ - jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), - ignoreExpiration: false, - secretOrKey: process.env.APP_SECRET, - passReqToCallback: true, - }); - } + constructor(private readonly authService: AuthService) { + super({ + jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), + ignoreExpiration: false, + secretOrKey: process.env.APP_SECRET, + passReqToCallback: true + }) + } - async validate(request: Request, payload: JwtPayload) { - return await this.authService.getUserIfRefreshTokenMatched( - payload.sub, - request.headers.authorization.split('Bearer ')[1], - ); - } -} \ No newline at end of file + async validate(request: Request, payload: JwtPayload) { + return await this.authService.getUserIfRefreshTokenMatched( + payload.sub, + request.headers.authorization.split('Bearer ')[1] + ) + } +} diff --git a/apps/api/src/auth/strategies/local.strategy.ts b/apps/api/src/auth/strategies/local.strategy.ts index 4672ece..eb51716 100644 --- a/apps/api/src/auth/strategies/local.strategy.ts +++ b/apps/api/src/auth/strategies/local.strategy.ts @@ -14,7 +14,12 @@ export class LocalStrategy extends PassportStrategy(Strategy, 'local') { }) } - validate(email: string, password: string): Promise & { refresh_token: string, access_token: string}> { + validate( + email: string, + password: string + ): Promise< + Partial & { refresh_token: string; access_token: string } + > { return this.authService.login(email, password) } } diff --git a/apps/api/src/user/user.service.ts b/apps/api/src/user/user.service.ts index 67c6aff..5ebb3e1 100644 --- a/apps/api/src/user/user.service.ts +++ b/apps/api/src/user/user.service.ts @@ -66,7 +66,13 @@ export class UserService { return await this.userRepository.update({ id }, { password }) } - async storeRefreshToken(user: UserEntity, token: string): Promise { - return await this.userRepository.update({ id: user.id }, { refreshToken: token }) - } + async storeRefreshToken( + user: UserEntity, + token: string + ): Promise { + return await this.userRepository.update( + { id: user.id }, + { refreshToken: token } + ) + } } diff --git a/libs/interfaces/src/auth/refreshTokenResponse.interface.ts b/libs/interfaces/src/auth/refreshTokenResponse.interface.ts index e1c3c79..bca7486 100644 --- a/libs/interfaces/src/auth/refreshTokenResponse.interface.ts +++ b/libs/interfaces/src/auth/refreshTokenResponse.interface.ts @@ -1,6 +1,6 @@ import { StatusType } from '../generic/Status.type' export interface RefreshTokenResponseInterface { - access_token: string, + access_token: string status: StatusType }