From d8d26c85640abcd550079ebbd1a779314a800ce0 Mon Sep 17 00:00:00 2001 From: Matiic77 Date: Fri, 22 May 2026 18:14:29 +0200 Subject: [PATCH] =?UTF-8?q?feat(security)=20=E2=9C=A8=20implement=20API=20?= =?UTF-8?q?rate=20limiting=20with=20throttler?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - install and configure @nestjs/throttler globally - set default global limit to 100 requests per minute - apply strict limits to sensitive auth endpoints (login, signup, OTP) - implement spam protection for friend requests --- package-lock.json | 12 ++++++++++++ package.json | 1 + src/app.module.ts | 16 +++++++++++++++- src/auth/auth.controller.ts | 4 ++++ src/friend/friend.controller.ts | 2 ++ 5 files changed, 34 insertions(+), 1 deletion(-) diff --git a/package-lock.json b/package-lock.json index 5a18a2d..3b8e999 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@nestjs/platform-express": "^11.0.1", "@nestjs/platform-socket.io": "^11.1.23", "@nestjs/swagger": "^11.4.2", + "@nestjs/throttler": "^6.5.0", "@nestjs/websockets": "^11.1.23", "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", @@ -2615,6 +2616,17 @@ } } }, + "node_modules/@nestjs/throttler": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@nestjs/throttler/-/throttler-6.5.0.tgz", + "integrity": "sha512-9j0ZRfH0QE1qyrj9JjIRDz5gQLPqq9yVC2nHsrosDVAfI5HHw08/aUAWx9DZLSdQf4HDkmhTTEGLrRFHENvchQ==", + "license": "MIT", + "peerDependencies": { + "@nestjs/common": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "@nestjs/core": "^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0", + "reflect-metadata": "^0.1.13 || ^0.2.0" + } + }, "node_modules/@nestjs/websockets": { "version": "11.1.23", "resolved": "https://registry.npmjs.org/@nestjs/websockets/-/websockets-11.1.23.tgz", diff --git a/package.json b/package.json index 337094b..13e6c62 100644 --- a/package.json +++ b/package.json @@ -32,6 +32,7 @@ "@nestjs/platform-express": "^11.0.1", "@nestjs/platform-socket.io": "^11.1.23", "@nestjs/swagger": "^11.4.2", + "@nestjs/throttler": "^6.5.0", "@nestjs/websockets": "^11.1.23", "@prisma/adapter-pg": "^7.8.0", "@prisma/client": "^7.8.0", diff --git a/src/app.module.ts b/src/app.module.ts index d2331df..eae2c96 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -15,10 +15,18 @@ import { ProgressionModule } from './progression/progression.module'; import { BattleModule } from './battle/battle.module'; import { AdminModule } from './admin/admin.module'; import { FriendModule } from './friend/friend.module'; +import { ThrottlerModule, ThrottlerGuard } from '@nestjs/throttler'; +import { APP_GUARD } from '@nestjs/core'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true }), + ThrottlerModule.forRoot([ + { + ttl: 60000, // 1 minute + limit: 100, // 100 requests per minute globally + }, + ]), AuthModule, UserModule, PrismaModule, @@ -34,6 +42,12 @@ import { FriendModule } from './friend/friend.module'; FriendModule, ], controllers: [AppController], - providers: [AppService], + providers: [ + AppService, + { + provide: APP_GUARD, + useClass: ThrottlerGuard, + }, + ], }) export class AppModule {} diff --git a/src/auth/auth.controller.ts b/src/auth/auth.controller.ts index 76a03ca..c945750 100644 --- a/src/auth/auth.controller.ts +++ b/src/auth/auth.controller.ts @@ -12,6 +12,7 @@ import { CreateAuthWithOtpDto, createOtpDto } from './dto/create-auth.dto'; import { LoginAuthDto } from './dto/login-auth.dto'; import { AuthGuard } from './guards/auth.guard'; +import { Throttle } from '@nestjs/throttler'; import { ApiOperation, ApiTags, @@ -35,6 +36,7 @@ export class AuthController { }, }) @ApiBadRequestResponse({ description: 'Bad Request.' }) + @Throttle({ default: { limit: 5, ttl: 60000 } }) @Post('signup') create(@Body() createAuthDto: CreateAuthWithOtpDto) { return this.authService.create(createAuthDto); @@ -42,6 +44,7 @@ export class AuthController { @ApiOperation({ summary: 'Request Registration OTP' }) @ApiCreatedResponse({ description: 'OTP sent successfully.' }) + @Throttle({ default: { limit: 3, ttl: 60000 } }) @Post('signup-otp') createWithOtp(@Body() createAuthWithOtpDto: createOtpDto) { return this.authService.emailconfirmation(createAuthWithOtpDto); @@ -58,6 +61,7 @@ export class AuthController { }, }) @ApiBadRequestResponse({ description: 'Invalid credentials.' }) + @Throttle({ default: { limit: 10, ttl: 60000 } }) @Post('signin') signIn(@Body() loginAuthDto: LoginAuthDto) { return this.authService.signIn(loginAuthDto); diff --git a/src/friend/friend.controller.ts b/src/friend/friend.controller.ts index caaf6a1..7084351 100644 --- a/src/friend/friend.controller.ts +++ b/src/friend/friend.controller.ts @@ -1,6 +1,7 @@ import { Controller, Get, Post, Patch, Param, UseGuards, Request } from '@nestjs/common'; import { FriendService } from './friend.service'; import { AuthGuard } from '../auth/guards/auth.guard'; +import { Throttle } from '@nestjs/throttler'; import { ApiTags, ApiOperation, @@ -19,6 +20,7 @@ export class FriendController { @Post('request/:id') @ApiOperation({ summary: 'Send a friend request' }) @ApiCreatedResponse({ description: 'Friend request sent successfully.' }) + @Throttle({ default: { limit: 10, ttl: 60000 } }) sendRequest(@Request() req: { user: { id: string } }, @Param('id') receiverId: string) { return this.friendService.sendFriendRequest(req.user.id, receiverId); }