Skip to content

Quick Start Guide

Azizul Hakim edited this page Nov 1, 2024 · 7 revisions

After installing nestjs-xsecurity, follow these steps to implement security in your NestJS application.

Module Configuration

You can configure the XSecurity module using one of two approaches:

⭐ Recommended: Async Configuration

The async configuration approach is recommended as it provides better flexibility and integration with NestJS's configuration system:

import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XSecurityModule } from 'nestjs-xsecurity';

@Module({
  imports: [
    ConfigModule.forRoot(),
    XSecurityModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        enabled: config.get('XSECURITY_ENABLED', true),
        secret: config.get('XSECURITY_SECRET'),
        rateLimit: {
          enabled: config.get('XSECURITY_RATE_LIMIT_ENABLED', true),
          maxAttempts: config.get('XSECURITY_MAX_ATTEMPTS', 5),
          decayMinutes: config.get('XSECURITY_DECAY_MINUTES', 1),
        },
        exclude: ['/health', '/metrics', '/api/docs/*'],
      }),
    }),
  ],
})
export class AppModule {}

Alternative: Static Configuration

For simpler applications, you can use static configuration:

import { Module } from '@nestjs/common';
import { XSecurityModule } from 'nestjs-xsecurity';

@Module({
  imports: [
    XSecurityModule.register({
      enabled: true,
      secret: process.env.XSECURITY_SECRET,
      rateLimit: {
        enabled: true
        maxAttempts: 5,
        decayMinutes: 1,
      },
      exclude: ['/health'],
    }),
  ],
})
export class AppModule {}

Separating Configuration Logic

For better organization, especially in larger applications, you can separate the configuration logic into a dedicated file:

// xsecurity.config.ts
import { ConfigService } from '@nestjs/config';
import { XSecurityConfig } from 'nestjs-xsecurity';

export class XSecurityConfig {
  constructor(private readonly configService: ConfigService) {}

  configureOptions(): XSecurityConfig {
    return {
      enabled: this.configService.get<boolean>('XSECURITY_ENABLED') ?? true,
      secret: this.configService.get<string>('XSECURITY_SECRET'),
      token: {
        headerName: 'X-SECURITY-TOKEN',
      },
      rateLimit: {
        enabled: config.get('XSECURITY_RATE_LIMIT_ENABLED', true),
        maxAttempts: 3,
        decayMinutes: 1,
      },
      exclude: ['/health'],
    };
  }
}

export default (configService: ConfigService) =>
  new XSecurityConfig(configService).configureOptions();

Then use it in your app module:

// app.module.ts
import { Module } from '@nestjs/common';
import { ConfigModule, ConfigService } from '@nestjs/config';
import { XSecurityModule } from 'nestjs-xsecurity';
import { AppController } from './app.controller';
import { AppService } from './app.service';
import xsecurityConfig from './xsecurity.config';

@Module({
  imports: [
    ConfigModule.forRoot({
      isGlobal: true,
      envFilePath: '.env',
      cache: true,
    }),
    XSecurityModule.registerAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (configService: ConfigService) =>
        xsecurityConfig(configService),
    }),
  ],
  controllers: [AppController],
  providers: [AppService],
})
export class AppModule {}

Token Generation

Here are implementations for generating security tokens in various languages:

Node.js / TypeScript

import crypto from 'crypto';

function generateXSecurityToken(secretKey: string, expirySeconds = 10): string {
  const expiryTimestamp = Math.floor(Date.now() / 1000) + expirySeconds;
  const payload = { expiry: expiryTimestamp };
  const token = Buffer.from(JSON.stringify(payload)).toString('base64');
  const signature = crypto
    .createHmac('sha256', secretKey)
    .update(token)
    .digest('hex');

  return `${token}.${signature}`;
}

// Usage
const token = generateXSecurityToken('your-secret-key');

Python

import hmac
import json
import base64
import hashlib
import time

def generate_xsecurity_token(secret_key: str, expiry_seconds: int = 10) -> str:
    expiry = int(time.time()) + expiry_seconds
    payload = {'expiry': expiry}

    # Create token
    token = base64.b64encode(
        json.dumps(payload).encode()
    ).decode()

    # Generate signature
    signature = hmac.new(
        secret_key.encode(),
        token.encode(),
        hashlib.sha256
    ).hexdigest()

    return f"{token}.{signature}"

Flutter / Dart

import 'dart:convert';
import 'package:crypto/crypto.dart';

String generateXSecurityToken(String secretKey, {int expirySeconds = 10}) {
  final expiry = DateTime.now().millisecondsSinceEpoch ~/ 1000 + expirySeconds;
  final payload = {'expiry': expiry};

  final token = base64Url.encode(utf8.encode(jsonEncode(payload)));
  final signature = Hmac(sha256, utf8.encode(secretKey))
      .convert(utf8.encode(token))
      .toString();

  return '$token.$signature';
}

Java

import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.util.Base64;
import java.time.Instant;
import org.json.JSONObject;

public class XSecurityTokenGenerator {
    public static String generateXSecurityToken(String secretKey, int expirySeconds) {
        try {
            // Create payload
            long expiryTimestamp = Instant.now().getEpochSecond() + expirySeconds;
            JSONObject payload = new JSONObject();
            payload.put("expiry", expiryTimestamp);

            // Encode payload
            String token = Base64.getEncoder()
                .encodeToString(payload.toString().getBytes(StandardCharsets.UTF_8));

            // Generate signature
            Mac sha256Hmac = Mac.getInstance("HmacSHA256");
            SecretKeySpec secretKeySpec = new SecretKeySpec(
                secretKey.getBytes(StandardCharsets.UTF_8), 
                "HmacSHA256"
            );
            sha256Hmac.init(secretKeySpec);
            String signature = bytesToHex(
                sha256Hmac.doFinal(token.getBytes(StandardCharsets.UTF_8))
            );

            return token + "." + signature;
        } catch (Exception e) {
            throw new RuntimeException("Failed to generate security token", e);
        }
    }

    private static String bytesToHex(byte[] bytes) {
        StringBuilder hexString = new StringBuilder();
        for (byte b : bytes) {
            String hex = Integer.toHexString(0xff & b);
            if (hex.length() == 1) hexString.append('0');
            hexString.append(hex);
        }
        return hexString.toString();
    }
}

Next Steps

Clone this wiki locally