From e2bb0373ec469e2cc20860df4cba00782118939f Mon Sep 17 00:00:00 2001 From: padi-dev-hoangpa <31430555+PhamAnhHoang@users.noreply.github.com> Date: Sun, 17 Dec 2023 00:35:08 +0700 Subject: [PATCH] Refresh token --- apps/api/src/auth/auth.controller.ts | 19 +++- apps/api/src/auth/auth.module.ts | 3 +- apps/api/src/auth/auth.service.spec.ts | 2 +- apps/api/src/auth/auth.service.ts | 78 ++++++++++--- apps/api/src/auth/guards/jwt-refresh-token.ts | 5 + .../interceptors/token.interceptor.spec.ts | 14 +-- .../auth/interceptors/token.interceptor.ts | 15 +-- .../strategies/jwt-refresh-token.strategy.ts | 30 +++++ .../api/src/auth/strategies/local.strategy.ts | 2 +- apps/api/src/entities/organization.entity.ts | 4 +- .../src/entities/user-organization.entity.ts | 2 +- ...702703853544-create-organizations-table.ts | 82 +++++++------- ...03944825-create-user-organization-table.ts | 104 +++++++++--------- ...add-foreign-key-user-organization-table.ts | 74 +++++++------ .../src/organization/organization.module.ts | 6 +- .../src/organization/organization.service.ts | 14 ++- .../src/user/enum/user-organization-enum.ts | 6 +- apps/api/src/user/user.service.ts | 4 + .../auth/refreshTokenResponse.interface.ts | 6 + libs/interfaces/src/index.ts | 1 + package.json | 2 + 21 files changed, 296 insertions(+), 177 deletions(-) create mode 100644 apps/api/src/auth/guards/jwt-refresh-token.ts create mode 100644 apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts create mode 100644 libs/interfaces/src/auth/refreshTokenResponse.interface.ts diff --git a/apps/api/src/auth/auth.controller.ts b/apps/api/src/auth/auth.controller.ts index b680319..d591686 100644 --- a/apps/api/src/auth/auth.controller.ts +++ b/apps/api/src/auth/auth.controller.ts @@ -24,19 +24,21 @@ import { JWTAuthGuard } from './guards/jwt-auth.guard' import { LocalAuthGuard } from './guards/local-auth.guard' import { SessionAuthGuard } from './guards/session-auth.guard' import { TokenInterceptor } from './interceptors/token.interceptor' -import { ConfirmCodeService } from '../user/confirm-code.service' import { PasswordResetPerformInterface, PasswordResetRequestInterface, Pure, + RefreshTokenResponseInterface, StatusType } from '@isomera/interfaces' +import { UserService } from '../user/user.service' +import { JwtRefreshTokenGuard } from './guards/jwt-refresh-token' @Controller('auth') export class AuthController { constructor( private readonly authService: AuthService, - private readonly confirmCodeService: ConfirmCodeService + private readonly userService: UserService ) {} @Post('register') @@ -97,4 +99,17 @@ export class AuthController { const result = await this.authService.setNewPassword(body) return { status: result ? StatusType.OK : StatusType.FAIL } } + + @UseGuards(JwtRefreshTokenGuard) + @Post('/refresh-token') + @HttpCode(HttpStatus.OK) + async refreshToken( + @AuthUser() user: Pure + ): Promise { + 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 a141bf9..94a8281 100644 --- a/apps/api/src/auth/auth.module.ts +++ b/apps/api/src/auth/auth.module.ts @@ -11,6 +11,7 @@ import { LocalStrategy } from './strategies/local.strategy' import { MailerModule } from '../mailer/mailer.module' import { ConfirmCodeModule } from '../user/confirm-code.module' import { OrganizationModule } from '../organization/organization.module' +import { JwtRefreshTokenStrategy } from './strategies/jwt-refresh-token.strategy' @Module({ imports: [ @@ -31,6 +32,6 @@ import { OrganizationModule } from '../organization/organization.module' OrganizationModule ], controllers: [AuthController], - providers: [AuthService, LocalStrategy, JwtStrategy, SessionSerializer] + providers: [AuthService, LocalStrategy, JwtStrategy, SessionSerializer, JwtRefreshTokenStrategy] }) export class AuthModule {} diff --git a/apps/api/src/auth/auth.service.spec.ts b/apps/api/src/auth/auth.service.spec.ts index 92bd3b6..6aae11a 100644 --- a/apps/api/src/auth/auth.service.spec.ts +++ b/apps/api/src/auth/auth.service.spec.ts @@ -165,7 +165,7 @@ describe('AuthService', () => { const user = createMock({ email: 'john@doe.me' }) mockedJwtService.sign.mockReturnValueOnce('j.w.t') - const {refresh_token, access_token} = service.signToken(user) + const { refresh_token, access_token } = service.signToken(user) expect(access_token).toEqual(expect.any(String)) expect(refresh_token).toEqual(expect.any(String)) diff --git a/apps/api/src/auth/auth.service.ts b/apps/api/src/auth/auth.service.ts index bc2a512..347c8dd 100644 --- a/apps/api/src/auth/auth.service.ts +++ b/apps/api/src/auth/auth.service.ts @@ -1,4 +1,5 @@ import { + BadRequestException, HttpException, HttpStatus, Injectable, @@ -20,6 +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' @Injectable() export class AuthService { @@ -62,7 +64,7 @@ export class AuthService { } } - async login(email: string, password: string): Promise { + async login(email: string, password: string): Promise & { refresh_token: string, access_token: string}> { let user: UserEntity try { @@ -80,7 +82,11 @@ export class AuthService { } delete user.password - return user + const {refresh_token, access_token} = this.signToken(user); + + await this.storeRefreshToken(user, refresh_token); + + return {...user, refresh_token, access_token} } async verifyPayload(payload: JwtPayload): Promise { @@ -98,35 +104,35 @@ export class AuthService { return user } - signToken(user: UserEntity): {refresh_token: string, access_token: string} { + signToken(user: UserEntity): { refresh_token: string; access_token: string } { return { refresh_token: this.generateRefreshToken(user.email), access_token: this.generateAccessToken(user.email) } } - private generateAccessToken(email: string): string { + public generateAccessToken(email: string): string { const payload = { sub: email } return this.jwtService.sign(payload, { expiresIn: `${this.configService.get( - 'JWT_ACCESS_TOKEN_EXPIRATION_TIME', - )}s`, - }); + 'JWT_ACCESS_TOKEN_EXPIRATION_TIME' + )}s` + }) } - private generateRefreshToken(email: string): string { + public generateRefreshToken(email: string): string { const payload = { sub: email } return this.jwtService.sign(payload, { expiresIn: `${this.configService.get( - 'JWT_REFRESH_TOKEN_EXPIRATION_TIME', - )}s`, - }); + 'JWT_REFRESH_TOKEN_EXPIRATION_TIME' + )}s` + }) } async sendGreetings(user: UserEntity) { @@ -177,16 +183,52 @@ export class AuthService { /** * After verify user, create personal organization for this user and send email - * @param code - * @param email + * @param code + * @param email */ - public async verifyCode({code, email}: Pure): Promise { - const user = await this.confirmCode.verifyCode(code, email); + public async verifyCode({ + code, + email + }: Pure): Promise { + const user = await this.confirmCode.verifyCode(code, email) - await this.organizationService.createDefaultOrganization(user.id); + await this.organizationService.createDefaultOrganization(user.id) + + await this.sendGreetings(user) - await this.sendGreetings(user); - return user } + + 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; + } + + private async verifyPlainContentWithHashedContent( + plainText: string, + hashedText: string, +) { + const is_matching = await bcrypt.compare(plainText, hashedText); + if (!is_matching) { + throw new BadRequestException(); + } +} + +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); +} + + } diff --git a/apps/api/src/auth/guards/jwt-refresh-token.ts b/apps/api/src/auth/guards/jwt-refresh-token.ts new file mode 100644 index 0000000..e1ab288 --- /dev/null +++ b/apps/api/src/auth/guards/jwt-refresh-token.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common' +import { AuthGuard } from '@nestjs/passport' + +@Injectable() +export class JwtRefreshTokenGuard extends AuthGuard('jwt-refresh-token') {} diff --git a/apps/api/src/auth/interceptors/token.interceptor.spec.ts b/apps/api/src/auth/interceptors/token.interceptor.spec.ts index 1a0a701..4b56140 100644 --- a/apps/api/src/auth/interceptors/token.interceptor.spec.ts +++ b/apps/api/src/auth/interceptors/token.interceptor.spec.ts @@ -47,14 +47,12 @@ describe('TokenInterceptor', () => { lastValueFrom(interceptor.intercept(context, next)) - jest - .spyOn(mockedAuthService, 'signToken') - .mockImplementationOnce(() => { - return { - refresh_token: 'refresh_token', - access_token: 'jwt' - } - }) + jest.spyOn(mockedAuthService, 'signToken').mockImplementationOnce(() => { + return { + refresh_token: 'refresh_token', + access_token: 'jwt' + } + }) jest.spyOn(res, 'getHeader').mockReturnValue('Bearer j.w.t') expect(res.getHeader('Authorization')).toBe('Bearer j.w.t') diff --git a/apps/api/src/auth/interceptors/token.interceptor.ts b/apps/api/src/auth/interceptors/token.interceptor.ts index 00a5466..4fd7dd2 100644 --- a/apps/api/src/auth/interceptors/token.interceptor.ts +++ b/apps/api/src/auth/interceptors/token.interceptor.ts @@ -17,22 +17,23 @@ export class TokenInterceptor implements NestInterceptor { intercept( context: ExecutionContext, - next: CallHandler - ): Observable & {access_token: string, refresh_token: string}> { + next: CallHandler & { access_token: string; refresh_token: string }> + ): Observable< + Partial & { access_token: string; refresh_token: string } + > { return next.handle().pipe( - map(user => { + map(data => { const response = context.switchToHttp().getResponse() - const {refresh_token, access_token} = this.authService.signToken(user) - response.setHeader('Authorization', `Bearer ${access_token}`) - response.cookie('token', access_token, { + response.setHeader('Authorization', `Bearer ${data.access_token}`) + response.cookie('token', data.access_token, { httpOnly: true, signed: true, sameSite: 'strict', secure: process.env.NODE_ENV === 'production' }) - return {...user, access_token, refresh_token} + return data }) ) } diff --git a/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts b/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts new file mode 100644 index 0000000..cff996b --- /dev/null +++ b/apps/api/src/auth/strategies/jwt-refresh-token.strategy.ts @@ -0,0 +1,30 @@ +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', +) { + 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 diff --git a/apps/api/src/auth/strategies/local.strategy.ts b/apps/api/src/auth/strategies/local.strategy.ts index cad06c8..4672ece 100644 --- a/apps/api/src/auth/strategies/local.strategy.ts +++ b/apps/api/src/auth/strategies/local.strategy.ts @@ -14,7 +14,7 @@ export class LocalStrategy extends PassportStrategy(Strategy, 'local') { }) } - validate(email: string, password: string): Promise { + validate(email: string, password: string): Promise & { refresh_token: string, access_token: string}> { return this.authService.login(email, password) } } diff --git a/apps/api/src/entities/organization.entity.ts b/apps/api/src/entities/organization.entity.ts index 62227f4..0744f8d 100644 --- a/apps/api/src/entities/organization.entity.ts +++ b/apps/api/src/entities/organization.entity.ts @@ -3,7 +3,7 @@ import { CreateDateColumn, Entity, PrimaryGeneratedColumn, - UpdateDateColumn, + UpdateDateColumn } from 'typeorm' import { OrganizationInterface } from '@isomera/interfaces' @@ -21,5 +21,5 @@ export class OrganizationEntity implements OrganizationInterface { @UpdateDateColumn() updatedAt: Date - static DEFAULT_ORGANIZATION_NAME = 'Isomera personal user' + static DEFAULT_ORGANIZATION_NAME = 'Isomera personal user' } diff --git a/apps/api/src/entities/user-organization.entity.ts b/apps/api/src/entities/user-organization.entity.ts index ea5692b..7dbcd1a 100644 --- a/apps/api/src/entities/user-organization.entity.ts +++ b/apps/api/src/entities/user-organization.entity.ts @@ -2,7 +2,7 @@ import { Column, CreateDateColumn, Entity, - PrimaryGeneratedColumn, + PrimaryGeneratedColumn } from 'typeorm' import { UserOrganizationInterace } from '@isomera/interfaces' diff --git a/apps/api/src/migrations/1702703853544-create-organizations-table.ts b/apps/api/src/migrations/1702703853544-create-organizations-table.ts index d758cc8..a501d7e 100644 --- a/apps/api/src/migrations/1702703853544-create-organizations-table.ts +++ b/apps/api/src/migrations/1702703853544-create-organizations-table.ts @@ -1,45 +1,45 @@ -import { MigrationInterface, QueryRunner, Table, TableColumn } from "typeorm" +import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateOrganizationsTable1702703853544 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: 'organizations', - columns: [ - new TableColumn({ - name: 'id', - type: 'int', - isPrimary: true, - isGenerated: true, - generationStrategy: 'increment' - }), - new TableColumn({ - name: 'name', - type: 'varchar(255)', - isNullable: false - }), - new TableColumn({ - name: 'createdAt', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false - }), - new TableColumn({ - name: 'updatedAt', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - onUpdate: 'CURRENT_TIMESTAMP()', - isNullable: false - }) - ] +export class CreateOrganizationsTable1702703853544 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'organizations', + columns: [ + new TableColumn({ + name: 'id', + type: 'int', + isPrimary: true, + isGenerated: true, + generationStrategy: 'increment' }), - true - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('organizations') - } + new TableColumn({ + name: 'name', + type: 'varchar(255)', + isNullable: false + }), + new TableColumn({ + name: 'createdAt', + type: 'timestamp', + default: 'CURRENT_TIMESTAMP', + isNullable: false + }), + new TableColumn({ + name: 'updatedAt', + type: 'timestamp', + default: 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP()', + isNullable: false + }) + ] + }), + true + ) + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('organizations') + } } diff --git a/apps/api/src/migrations/1702703944825-create-user-organization-table.ts b/apps/api/src/migrations/1702703944825-create-user-organization-table.ts index 20bbf28..2ed0fee 100644 --- a/apps/api/src/migrations/1702703944825-create-user-organization-table.ts +++ b/apps/api/src/migrations/1702703944825-create-user-organization-table.ts @@ -1,55 +1,55 @@ -import { MigrationInterface, QueryRunner, Table, TableColumn } from "typeorm" +import { MigrationInterface, QueryRunner, Table, TableColumn } from 'typeorm' -export class CreateUserOrganizationTable1702703944825 implements MigrationInterface { - - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createTable( - new Table({ - name: 'user-organization', - columns: [ - new TableColumn({ - name: 'id', - type: 'int', - isPrimary: true, - isGenerated: true, - generationStrategy: 'increment' - }), - new TableColumn({ - name: 'userId', - type: 'int', - isNullable: false - }), - new TableColumn({ - name: 'organizationId', - type: 'int', - isNullable: false - }), - new TableColumn({ - name: 'role', - type: 'int', - isNullable: false - }), - new TableColumn({ - name: 'createdAt', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - isNullable: false - }), - new TableColumn({ - name: 'updatedAt', - type: 'timestamp', - default: 'CURRENT_TIMESTAMP', - onUpdate: 'CURRENT_TIMESTAMP()', - isNullable: false - }) - ] - }), - true - ) - } - - public async down(queryRunner: QueryRunner): Promise { - await queryRunner.dropTable('user-organization') - } +export class CreateUserOrganizationTable1702703944825 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createTable( + new Table({ + name: 'user-organization', + columns: [ + new TableColumn({ + name: 'id', + type: 'int', + isPrimary: true, + isGenerated: true, + generationStrategy: 'increment' + }), + new TableColumn({ + name: 'userId', + type: 'int', + isNullable: false + }), + new TableColumn({ + name: 'organizationId', + type: 'int', + isNullable: false + }), + new TableColumn({ + name: 'role', + type: 'int', + isNullable: false + }), + new TableColumn({ + name: 'createdAt', + type: 'timestamp', + default: 'CURRENT_TIMESTAMP', + isNullable: false + }), + new TableColumn({ + name: 'updatedAt', + type: 'timestamp', + default: 'CURRENT_TIMESTAMP', + onUpdate: 'CURRENT_TIMESTAMP()', + isNullable: false + }) + ] + }), + true + ) + } + public async down(queryRunner: QueryRunner): Promise { + await queryRunner.dropTable('user-organization') + } } diff --git a/apps/api/src/migrations/1702704151500-add-foreign-key-user-organization-table.ts b/apps/api/src/migrations/1702704151500-add-foreign-key-user-organization-table.ts index f889ba7..5f59596 100644 --- a/apps/api/src/migrations/1702704151500-add-foreign-key-user-organization-table.ts +++ b/apps/api/src/migrations/1702704151500-add-foreign-key-user-organization-table.ts @@ -1,41 +1,45 @@ -import { MigrationInterface, QueryRunner, TableForeignKey } from "typeorm" +import { MigrationInterface, QueryRunner, TableForeignKey } from 'typeorm' -export class AddForeignKeyUserOrganizationTable1702704151500 implements MigrationInterface { +export class AddForeignKeyUserOrganizationTable1702704151500 + implements MigrationInterface +{ + public async up(queryRunner: QueryRunner): Promise { + await queryRunner.createForeignKey( + 'user-organization', + new TableForeignKey({ + name: 'FK_users_of_user-organization', + columnNames: ['userId'], + referencedColumnNames: ['id'], + referencedTableName: 'users', + onDelete: 'CASCADE' + }) + ) - public async up(queryRunner: QueryRunner): Promise { - await queryRunner.createForeignKey( - 'user-organization', - new TableForeignKey({ - name: 'FK_users_of_user-organization', - columnNames: ['userId'], - referencedColumnNames: ['id'], - referencedTableName: 'users', - onDelete: 'CASCADE' - }) + await queryRunner.createForeignKey( + 'user-organization', + new TableForeignKey({ + name: 'FK_organizations_of_user-organization', + columnNames: ['organizationId'], + referencedColumnNames: ['id'], + referencedTableName: 'organizations', + onDelete: 'CASCADE' + }) ) + } - await queryRunner.createForeignKey( - 'user-organization', - new TableForeignKey({ - name: 'FK_organizations_of_user-organization', - columnNames: ['organizationId'], - referencedColumnNames: ['id'], - referencedTableName: 'organizations', - onDelete: 'CASCADE' - }) - ) - } - - public async down(queryRunner: QueryRunner): Promise { - const table = await queryRunner.getTable('user-organization') - const foreignKeyUser = table.foreignKeys.find( - fk => fk.columnNames.indexOf('userId') !== -1 - ) - await queryRunner.dropForeignKey('user_organization', foreignKeyUser) + public async down(queryRunner: QueryRunner): Promise { + const table = await queryRunner.getTable('user-organization') + const foreignKeyUser = table.foreignKeys.find( + fk => fk.columnNames.indexOf('userId') !== -1 + ) + await queryRunner.dropForeignKey('user_organization', foreignKeyUser) - const foreignKeyOrganization = table.foreignKeys.find( - fk => fk.columnNames.indexOf('organizationId') !== -1 - ) - await queryRunner.dropForeignKey('user_organization', foreignKeyOrganization) - } + const foreignKeyOrganization = table.foreignKeys.find( + fk => fk.columnNames.indexOf('organizationId') !== -1 + ) + await queryRunner.dropForeignKey( + 'user_organization', + foreignKeyOrganization + ) + } } diff --git a/apps/api/src/organization/organization.module.ts b/apps/api/src/organization/organization.module.ts index 570c76d..b107d7b 100644 --- a/apps/api/src/organization/organization.module.ts +++ b/apps/api/src/organization/organization.module.ts @@ -5,7 +5,11 @@ import { OrganizationEntity } from '../entities/organization.entity' import { UserOrganizationEntity } from '../entities/user-organization.entity' @Module({ - imports: [TypeOrmModule.forFeature([OrganizationEntity, UserOrganizationEntity]), OrganizationEntity, UserOrganizationEntity], + imports: [ + TypeOrmModule.forFeature([OrganizationEntity, UserOrganizationEntity]), + OrganizationEntity, + UserOrganizationEntity + ], providers: [OrganizationService], exports: [OrganizationService] }) diff --git a/apps/api/src/organization/organization.service.ts b/apps/api/src/organization/organization.service.ts index b510049..1618b35 100644 --- a/apps/api/src/organization/organization.service.ts +++ b/apps/api/src/organization/organization.service.ts @@ -12,7 +12,7 @@ export class OrganizationService { private readonly organizationRepository: Repository, @InjectRepository(UserOrganizationEntity) - private readonly userOrganizationRepository: Repository, + private readonly userOrganizationRepository: Repository ) {} async create(data: Partial): Promise { @@ -22,9 +22,15 @@ export class OrganizationService { } async createDefaultOrganization(userId: number): Promise { - const organization = await this.create({name: OrganizationEntity.DEFAULT_ORGANIZATION_NAME}) - - const user = this.userOrganizationRepository.create({organizationId: organization.id, userId: userId, role: UserOrganizationRoleEnum.OWNER}) + const organization = await this.create({ + name: OrganizationEntity.DEFAULT_ORGANIZATION_NAME + }) + + const user = this.userOrganizationRepository.create({ + organizationId: organization.id, + userId: userId, + role: UserOrganizationRoleEnum.OWNER + }) await this.userOrganizationRepository.save(user) diff --git a/apps/api/src/user/enum/user-organization-enum.ts b/apps/api/src/user/enum/user-organization-enum.ts index cb95cd5..7bac6e6 100644 --- a/apps/api/src/user/enum/user-organization-enum.ts +++ b/apps/api/src/user/enum/user-organization-enum.ts @@ -1,4 +1,4 @@ export enum UserOrganizationRoleEnum { - OWNER = 1, - MEMBER = 2, -} \ No newline at end of file + OWNER = 1, + MEMBER = 2 +} diff --git a/apps/api/src/user/user.service.ts b/apps/api/src/user/user.service.ts index 28c3288..67c6aff 100644 --- a/apps/api/src/user/user.service.ts +++ b/apps/api/src/user/user.service.ts @@ -65,4 +65,8 @@ 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 }) + } } diff --git a/libs/interfaces/src/auth/refreshTokenResponse.interface.ts b/libs/interfaces/src/auth/refreshTokenResponse.interface.ts new file mode 100644 index 0000000..e1c3c79 --- /dev/null +++ b/libs/interfaces/src/auth/refreshTokenResponse.interface.ts @@ -0,0 +1,6 @@ +import { StatusType } from '../generic/Status.type' + +export interface RefreshTokenResponseInterface { + access_token: string, + status: StatusType +} diff --git a/libs/interfaces/src/index.ts b/libs/interfaces/src/index.ts index 3ecc8e2..3823384 100644 --- a/libs/interfaces/src/index.ts +++ b/libs/interfaces/src/index.ts @@ -8,4 +8,5 @@ export * from './auth/passwordResetRequest.interface' export * from './auth/passwordResetPerform.interface' export * from './auth/signUpResponse.interface' export * from './auth/confirmationCode.interface' +export * from './auth/refreshTokenResponse.interface' export * from './organization/Organization.interface' diff --git a/package.json b/package.json index 62f5085..5ff3766 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "async-mutex": "^0.4.0", "axios": "^1.0.0", "axios-auth-refresh": "^3.3.6", + "bcrypt": "^5.1.1", "class-transformer": "^0.5.1", "class-transformer-validator": "^0.9.1", "class-validator": "^0.14.0", @@ -82,6 +83,7 @@ "@swc/cli": "~0.1.62", "@swc/core": "~1.3.85", "@testing-library/react": "14.0.0", + "@types/bcrypt": "^5.0.2", "@types/bcryptjs": "^2.4.6", "@types/connect-pg-simple": "^7.0.3", "@types/cookie-parser": "^1.4.6",