From b2a4cd3f4fe908c727a849dc2b12db909f34aec4 Mon Sep 17 00:00:00 2001 From: Renolation Date: Thu, 22 May 2025 22:02:54 +0700 Subject: [PATCH] add jwt --- src/app.module.ts | 21 ++++++++----- .../authentication.controller.ts | 14 +++++++-- .../jwt-refresh-token.strategy.ts | 30 +++++++++++++++++++ src/authentication/jwtRefreshGuard.guard.ts | 5 ++++ src/users/user.service.ts | 14 ++++++++- 5 files changed, 73 insertions(+), 11 deletions(-) create mode 100644 src/authentication/jwt-refresh-token.strategy.ts create mode 100644 src/authentication/jwtRefreshGuard.guard.ts diff --git a/src/app.module.ts b/src/app.module.ts index 02b112e..bc3a029 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,13 +1,13 @@ -import { Module } from '@nestjs/common'; -import { AppController } from './app.controller'; -import { AppService } from './app.service'; -import { TypeOrmModule } from '@nestjs/typeorm'; -import { ConfigModule, ConfigService } from '@nestjs/config'; +import {Module} from '@nestjs/common'; +import {AppController} from './app.controller'; +import {AppService} from './app.service'; +import {TypeOrmModule} from '@nestjs/typeorm'; +import {ConfigModule, ConfigService} from '@nestjs/config'; import * as Joi from 'joi'; import {UsersModule} from "./users/user.module"; import {AuthenticationModule} from "./authentication/authentication.module"; -import { PostsModule } from './posts/posts.module'; -import { CategoriesModule } from './categories/categories.module'; +import {PostsModule} from './posts/posts.module'; +import {CategoriesModule} from './categories/categories.module'; import {FilesModule} from "./files/file.module"; @Module({ @@ -36,6 +36,10 @@ import {FilesModule} from "./files/file.module"; S3_BUCKET: Joi.string().required(), S3_ACCESS_KEY: Joi.string().required(), S3_ENDPOINT: Joi.string().required(), + JWT_ACCESS_TOKEN_SECRET: Joi.string().required(), + JWT_ACCESS_TOKEN_EXPIRATION_TIME: Joi.string().required(), + JWT_REFRESH_TOKEN_SECRET: Joi.string().required(), + JWT_REFRESH_TOKEN_EXPIRATION_TIME: Joi.string().required(), }) }), UsersModule, @@ -47,4 +51,5 @@ import {FilesModule} from "./files/file.module"; controllers: [AppController], providers: [AppService], }) -export class AppModule {} \ No newline at end of file +export class AppModule { +} \ No newline at end of file diff --git a/src/authentication/authentication.controller.ts b/src/authentication/authentication.controller.ts index 373e955..df36392 100644 --- a/src/authentication/authentication.controller.ts +++ b/src/authentication/authentication.controller.ts @@ -17,12 +17,12 @@ import JwtAuthenticationGuard from './jwt-authentication.guard'; // import {EmailConfirmationService} from '../emailConfirmation/emailConfirmation.service'; import {ApiBody} from '@nestjs/swagger'; import LogInDto from './dto/logIn.dto'; -import { UsersService } from 'src/users/user.service'; +import {UsersService} from 'src/users/user.service'; @Controller('authentication') @UseInterceptors(ClassSerializerInterceptor) @SerializeOptions({ - strategy: 'excludeAll' + strategy: 'excludeAll' }) export class AuthenticationController { constructor( @@ -87,4 +87,14 @@ export class AuthenticationController { return user; } + @UseGuards(JwtRefreshGuard) + @Get('refresh') + refresh(@Req() request: RequestWithUser) { + const accessTokenCookie = this.authenticationService.getCookieWithJwtAccessToken(request.user.id); + + request.res.setHeader('Set-Cookie', accessTokenCookie); + return request.user; + } + + } \ No newline at end of file diff --git a/src/authentication/jwt-refresh-token.strategy.ts b/src/authentication/jwt-refresh-token.strategy.ts new file mode 100644 index 0000000..abf9006 --- /dev/null +++ b/src/authentication/jwt-refresh-token.strategy.ts @@ -0,0 +1,30 @@ +import { ExtractJwt, Strategy } from 'passport-jwt'; +import { PassportStrategy } from '@nestjs/passport'; +import { Injectable } from '@nestjs/common'; +import { ConfigService } from '@nestjs/config'; +import { Request } from 'express'; +import {UsersService} from "../users/user.service"; + +@Injectable() +export class JwtRefreshTokenStrategy extends PassportStrategy( + Strategy, + 'jwt-refresh-token' +) { + constructor( + private readonly configService: ConfigService, + private readonly userService: UsersService, + ) { + super({ + jwtFromRequest: ExtractJwt.fromExtractors([(request: Request) => { + return request?.cookies?.Refresh; + }]), + secretOrKey: configService.get('JWT_REFRESH_TOKEN_SECRET'), + passReqToCallback: true, + }); + } + + async validate(request: Request, payload: TokenPayload) { + const refreshToken = request.cookies?.Refresh; + return this.userService.getUserIfRefreshTokenMatches(refreshToken, payload.userId); + } +} \ No newline at end of file diff --git a/src/authentication/jwtRefreshGuard.guard.ts b/src/authentication/jwtRefreshGuard.guard.ts new file mode 100644 index 0000000..2b7317e --- /dev/null +++ b/src/authentication/jwtRefreshGuard.guard.ts @@ -0,0 +1,5 @@ +import { Injectable } from '@nestjs/common'; +import { AuthGuard } from '@nestjs/passport'; + +@Injectable() +export default class JwtRefreshGuard extends AuthGuard('jwt-refresh-token') {} \ No newline at end of file diff --git a/src/users/user.service.ts b/src/users/user.service.ts index 907635b..0328168 100644 --- a/src/users/user.service.ts +++ b/src/users/user.service.ts @@ -34,6 +34,19 @@ export class UsersService { throw new HttpException('User with this id does not exist', HttpStatus.NOT_FOUND); } + async getUserIfRefreshTokenMatches(refreshToken: string, userId: number) { + const user = await this.getById(userId); + + const isRefreshTokenMatching = await bcrypt.compare( + refreshToken, + user.currentHashedRefreshToken + ); + + if (isRefreshTokenMatching) { + return user; + } + } + async addAvatar(userId: number, imageBuffer: Buffer, filename: string) { const avatar = await this.filesService.uploadPublicFile(imageBuffer, filename); const user = await this.getById(userId); @@ -62,5 +75,4 @@ export class UsersService { currentHashedRefreshToken: null, }); } - }