diff --git a/.env.docker b/.env.docker index 9fcf34b..90d8632 100644 --- a/.env.docker +++ b/.env.docker @@ -7,6 +7,14 @@ APP_URL=http://localhost JWT_SECRET=aSubMyjTUd4aBzTJmypcdFY5sJcql8qphncJHeZiH+Vqxdnk06lrlJ8TmuoffX/IFwhG3NCp9c11v9U0w+OQpQ== JWT_EXPIRATION=30d # 30 days +# XSecurity Config +XSECURITY_ENABLED=false +XSECURITY_SECRET=your-XSecurity-secret +XSECURITY_MAX_ATTEMPTS=5 +XSECURITY_DECAY_MINUTES=1 +XSECURITY_RATE_LIMIT_ENABLED=false +XSECURITY_RATE_LIMIT_STORE_LIMIT=10000 + # Database Config DB_DRIVER=postgres DB_HOST=db diff --git a/.env.example b/.env.example index 9bf3dab..751ca72 100644 --- a/.env.example +++ b/.env.example @@ -11,6 +11,10 @@ JWT_EXPIRATION=3600 # 1 hour in seconds # XSecurity Config XSECURITY_ENABLED=false XSECURITY_SECRET=your-XSecurity-secret +XSECURITY_MAX_ATTEMPTS=5 +XSECURITY_DECAY_MINUTES=1 +XSECURITY_RATE_LIMIT_ENABLED=true +XSECURITY_RATE_LIMIT_STORE_LIMIT=10000 #throttle config THROTTLE_TTL=10 diff --git a/README.md b/README.md index 3c3cab4..77e39b3 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ ## About this project -`ims-nest-api-starter` is a backend API starter template using [NestJS](https://nestjs.com/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/), [BullMQ](https://bullmq.io/) and [MikroORM](https://mikro-orm.io/) designed for scalable applications. +`ims-nest-api-starter` is a backend API starter template using [NestJS](https://nestjs.com/), [PostgreSQL](https://www.postgresql.org/), [Redis](https://redis.io/), [BullMQ](https://bullmq.io/), [MikroORM](https://mikro-orm.io/) and [XSECURITY](https://www.npmjs.com/package/nestjs-xsecurity) designed for scalable applications. ### Key Features @@ -16,7 +16,7 @@ - **Database Management**: MikroORM setup with PostgreSQL for efficient data handling. - **Queue Management**: BullMQ for asynchronous tasks and event-driven architecture. - **Email Notification**: Send emails using [Nodemailer](https://nodemailer.com/) with BullMQ asynchronously. -- **XSECURITY**: An added security layer that safeguards APIs against unauthorized access, ensuring data protection and integrity. +- **XSECURITY**: [XSECURITY](https://www.npmjs.com/package/nestjs-xsecurity) is an added security layer that safeguards APIs against unauthorized access, ensuring data protection and integrity. ## Getting Started Guide Without Docker @@ -220,36 +220,17 @@ npm run test ## Xsecurity Setup -IMS introduces an additional layer of security, enhancing the API's reliability and resilience. With this system, only applications possessing a shared XSECURITY_TOKEN can send API requests to the server; others will be blocked. To get started, follow the guide below. +To ensure the security of your application, we have integrated [XSECURITY](https://www.npmjs.com/package/nestjs-xsecurity) which is a security layer that safeguards APIs against unauthorized access by token validation, rate limiting. here is the [XSECURITY Guide](https://github.com/AHS12/nestjs-xsecurity/wiki). -Getting Started -By default, XSecure is disabled! To enable it, set the XSECURITY_ENABLED value to true in your .env file: +for quick start, you can run the following command: ```bash -XSECURITY_ENABLED=true +npx nestjs-xsecurity install ``` - -Other wise it will be disabled. - -Installation -Execute the following command to set up XSECURITY: - -```bash -npm run xsecurity:install -``` - -This command generates a secret for your application and updates your .env file with the `XSECURITY_SECRET` field. - -After running the command, you will receive output similar to this: - -```bash -Generated secret: N+6WQq7RjqvE+KhMRFDtk1n09M98lBAb/P/8j/I3w/7ibNzgbJeg2a+gBjNpPbMgyXSgq0sebXzYwPwnFSmleg== -XSECURITY_SECRET key has been updated in the .env file. -``` - -Use this secret in your frontend or mobile app to generate a short-lived XSecure token, which will be verified by the backend server. - -For more information on how to use XSECURITY, refer to the [XSECURITY Guide](https://github.com/Innovix-Matrix-Systems/ims-laravel-api-starter/wiki/XSECURE-setup). +This command will: +- Generate a secure random secret +- Set up required environment variables +- update the existing `.env` file with the new environment variables ## Extra CLI Commands @@ -294,7 +275,7 @@ nest generate --help You can create custom CLI commands tailored to your specific needs using the [nestjs-command](https://www.npmjs.com/package/nestjs-command) package. This project already includes integration with [nestjs-command](https://www.npmjs.com/package/nestjs-command) package. -For reference, check out the `xsecurity` command implemented in [src/commands/xsecurity.command.ts](https://github.com/Innovix-Matrix-Systems/ims-nest-api-starter/blob/main/src/commands/xsecurity.command.ts). +For reference, check out the `create-module` command implemented in [src/commands/create-module.command.ts](https://github.com/Innovix-Matrix-Systems/ims-nest-api-starter/blob/main/src/commands/create-module.command.ts). ### Custom Module Creation Command diff --git a/package-lock.json b/package-lock.json index 23a1383..ec7df7a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -7,7 +7,7 @@ "": { "name": "ims-nest-api-starter", "version": "0.0.1", - "license": "MIT", + "license": "CC0-1.0", "dependencies": { "@faker-js/faker": "^9.0.3", "@mikro-orm/cli": "^6.3.13", @@ -38,6 +38,7 @@ "ims-nest-api-starter": "file:", "ioredis": "^5.4.1", "nestjs-command": "^3.1.4", + "nestjs-xsecurity": "^1.0.0", "nodemailer": "^6.9.16", "passport": "^0.7.0", "passport-jwt": "^4.0.1", @@ -8199,6 +8200,22 @@ "yargs": "^16.0.0 || ^17.0.0" } }, + "node_modules/nestjs-xsecurity": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/nestjs-xsecurity/-/nestjs-xsecurity-1.0.0.tgz", + "integrity": "sha512-Spi95qcB1P5xtqiX+4j42IdS8zfut+X2kgNXDgDdu6LouaFZBOPiyPeVGxB0RJCXvbX9gdH5vyF4cafpQ8ZUGQ==", + "license": "MIT", + "bin": { + "nestjs-xsecurity": "dist/cli/bin/install.js" + }, + "engines": { + "node": ">=14" + }, + "peerDependencies": { + "@nestjs/common": "^9.0.0 || ^10.0.0", + "@nestjs/core": "^9.0.0 || ^10.0.0" + } + }, "node_modules/node-abort-controller": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", diff --git a/package.json b/package.json index b22d206..c450b61 100644 --- a/package.json +++ b/package.json @@ -59,6 +59,7 @@ "ims-nest-api-starter": "file:", "ioredis": "^5.4.1", "nestjs-command": "^3.1.4", + "nestjs-xsecurity": "^1.0.0", "nodemailer": "^6.9.16", "passport": "^0.7.0", "passport-jwt": "^4.0.1", diff --git a/src/app.module.ts b/src/app.module.ts index 6ff80b9..3610818 100644 --- a/src/app.module.ts +++ b/src/app.module.ts @@ -1,17 +1,18 @@ import { MikroOrmModule } from '@mikro-orm/nestjs'; import { RedisModule } from '@nestjs-modules/ioredis'; -import { MiddlewareConsumer, Module, NestModule } from '@nestjs/common'; +import { BullModule } from '@nestjs/bullmq'; +import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { APP_GUARD } from '@nestjs/core'; import { ThrottlerGuard, ThrottlerModule } from '@nestjs/throttler'; import { CommandModule } from 'nestjs-command'; +import { XSecurityModule } from 'nestjs-xsecurity'; import { AppController } from './app.controller'; import { CreateModuleCommand } from './commands/create-module.command'; -import { XSecureInstallCommand } from './commands/xsecurity.command'; import bullRedisConfig from './config/bull-redis.config'; import mikroOrmConfig from './config/mikro-orm.config'; import redisConfig from './config/redis.config'; -import { XSecurityMiddleware } from './middlewares/xsecurity.middleware'; +import xsecurityConfig from './config/xsecurity.config'; import { AuthModule } from './modules/auth/auth.module'; import { CacheModule } from './modules/cache/cache.module'; import { EmailModule } from './modules/email/email.module'; @@ -20,7 +21,6 @@ import { MiscModule } from './modules/misc/misc.module'; import { PermissionModule } from './modules/permission/permission.module'; import { RoleModule } from './modules/role/role.module'; import { UserModule } from './modules/user/user.module'; -import { BullModule } from '@nestjs/bullmq'; @Module({ imports: [ @@ -55,9 +55,19 @@ import { BullModule } from '@nestjs/bullmq'; inject: [ConfigService], useFactory: (configService: ConfigService) => ({ connection: bullRedisConfig(configService), - defaultJobOptions: { attempts: 3, removeOnComplete: true }, + defaultJobOptions: { + attempts: 3, + removeOnComplete: true, + removeOnFail: true, + }, }), }), + XSecurityModule.registerAsync({ + imports: [ConfigModule], + inject: [ConfigService], + useFactory: (configService: ConfigService) => + xsecurityConfig(configService), + }), CommandModule, HealthModule, MiscModule, @@ -74,12 +84,7 @@ import { BullModule } from '@nestjs/bullmq'; provide: APP_GUARD, useClass: ThrottlerGuard, }, - XSecureInstallCommand, CreateModuleCommand, ], }) -export class AppModule implements NestModule { - configure(consumer: MiddlewareConsumer) { - consumer.apply(XSecurityMiddleware).forRoutes('*'); - } -} +export class AppModule {} diff --git a/src/commands/xsecurity.command.ts b/src/commands/xsecurity.command.ts deleted file mode 100644 index 714a7b7..0000000 --- a/src/commands/xsecurity.command.ts +++ /dev/null @@ -1,51 +0,0 @@ -import { Injectable } from '@nestjs/common'; -import * as crypto from 'crypto'; -import * as dotenv from 'dotenv'; -import * as fs from 'fs'; -import { Command } from 'nestjs-command'; - -@Injectable() -export class XSecureInstallCommand { - @Command({ - command: 'xsecurity:install', - describe: 'Install XSecurity', - }) - async run() { - // Generate a secret - const secret = crypto.randomBytes(64).toString('base64'); - - // Update .env file - const envPath = '.env'; - let envContent = fs.existsSync(envPath) - ? fs.readFileSync(envPath, 'utf8') - : ''; - - // Update or add XSECURITY_SECRET - const regex = /^XSECURITY_SECRET=.*/m; - const newLine = `XSECURITY_SECRET=${secret}`; - - if (regex.test(envContent)) { - envContent = envContent.replace(regex, newLine); - } else { - envContent += `\n${newLine}`; - } - - // Update or add XSECURITY_ENABLED - const enabledRegex = /^XSECURITY_ENABLED=.*/m; - const enabledLine = 'XSECURITY_ENABLED=true'; - - if (enabledRegex.test(envContent)) { - envContent = envContent.replace(enabledRegex, enabledLine); - } else { - envContent += `\n${enabledLine}`; - } - - fs.writeFileSync(envPath, envContent); - - console.log(`Generated secret: ${secret}`); - console.log('XSECURITY_SECRET key has been updated in the .env file.'); - - // Reload environment variables - dotenv.config(); - } -} diff --git a/src/config/xsecurity.config.ts b/src/config/xsecurity.config.ts new file mode 100644 index 0000000..b2410c3 --- /dev/null +++ b/src/config/xsecurity.config.ts @@ -0,0 +1,61 @@ +import { ConfigService } from '@nestjs/config'; +import { config } from 'dotenv'; +import { XSecurityConfig } from 'nestjs-xsecurity'; +import { getConfigValue } from '../utils/helper'; + +// Load environment variables for CLI usage +config(); + +export class XsecurityConfig { + constructor(private readonly configService?: ConfigService) {} + + configureOptions(): XSecurityConfig { + return { + enabled: !!getConfigValue( + 'XSECURITY_ENABLED', + false, + this.configService, + ), + secret: getConfigValue( + 'XSECURITY_SECRET', + '', + this.configService, + ), + token: { + headerName: 'X-SECURITY-TOKEN', + }, + rateLimit: { + enabled: !!getConfigValue( + 'XSECURITY_RATE_LIMIT_ENABLED', + false, + this.configService, + ), + maxAttempts: Number( + getConfigValue( + 'XSECURITY_MAX_ATTEMPTS', + '0', + this.configService, + ), + ), + decayMinutes: Number( + getConfigValue( + 'XSECURITY_DECAY_MINUTES', + '0', + this.configService, + ), + ), + storeLimit: Number( + getConfigValue( + 'XSECURITY_RATE_LIMIT_STORE_LIMIT', + '10000', + this.configService, + ), + ), + }, + exclude: ['/health', '/api/v1/ping'], + }; + } +} + +export default (configService?: ConfigService) => + new XsecurityConfig(configService).configureOptions();