NestboltNestbolt

@nestbolt/authentication

Configuration

Complete reference for all @nestbolt/authentication module configuration options, including JWT settings, rate limiting, two-factor authentication, and async configuration.

The AuthenticationModule accepts a configuration object via forRoot() (synchronous) or forRootAsync() (asynchronous). This page documents every available option.

Synchronous Configuration

Use forRoot() when all configuration values are available at module definition time:

import { AuthenticationModule, Feature } from "@nestbolt/authentication";

AuthenticationModule.forRoot({
  // Required
  features: [Feature.REGISTRATION, Feature.TWO_FACTOR_AUTHENTICATION],
  userRepository: TypeOrmUserRepository,
  jwtSecret: "your-jwt-signing-secret",
  refreshSecret: "your-refresh-token-secret",
  encryptionKey: "base64-encoded-32-byte-key",

  // Optional
  passwordResetRepository: TypeOrmPasswordResetRepository,
  jwtExpiresIn: "15m",
  refreshExpiresIn: "7d",
  usernameField: "email",
  lowercaseUsernames: true,
  appName: "MyApp",
  passwordTimeout: 900,
  loginRateLimit: { ttl: 60000, limit: 5 },
  twoFactorRateLimit: { ttl: 60000, limit: 5 },
  verificationRateLimit: { ttl: 60000, limit: 6 },
  twoFactorOptions: {
    confirm: false,
    confirmPassword: false,
    window: 1,
    secretLength: 20,
  },
});

With forRoot(), the module only registers controllers and services for the features you list. If Feature.TWO_FACTOR_AUTHENTICATION is not in the array, the two-factor controllers and services are never instantiated.

Asynchronous Configuration

Use forRootAsync() when you need to inject dependencies (like ConfigService) to resolve configuration values:

import { AuthenticationModule, Feature } from "@nestbolt/authentication";
import { ConfigModule, ConfigService } from "@nestjs/config";

AuthenticationModule.forRootAsync({
  imports: [ConfigModule],
  inject: [ConfigService],
  useFactory: (config: ConfigService) => ({
    features: [
      Feature.REGISTRATION,
      Feature.RESET_PASSWORDS,
      Feature.EMAIL_VERIFICATION,
      Feature.UPDATE_PROFILE_INFORMATION,
      Feature.UPDATE_PASSWORDS,
      Feature.TWO_FACTOR_AUTHENTICATION,
    ],
    userRepository: TypeOrmUserRepository,
    jwtSecret: config.get<string>("JWT_SECRET")!,
    refreshSecret: config.get<string>("REFRESH_SECRET")!,
    encryptionKey: config.get<string>("ENCRYPTION_KEY")!,
    appName: config.get<string>("APP_NAME", "MyApp"),
    jwtExpiresIn: config.get<string>("JWT_EXPIRES_IN", "15m"),
    refreshExpiresIn: config.get<string>("REFRESH_EXPIRES_IN", "7d"),
  }),
});

With forRootAsync(), all controllers and services are registered eagerly (since the features array is not available at static definition time). The FeatureEnabledGuard gates access at runtime, returning a 404 Not Found response for routes whose feature is not enabled.

Async Options Interface

interface AuthenticationAsyncOptions {
  imports?: any[];
  inject?: any[];
  useFactory: (
    ...args: any[]
  ) => Promise<AuthenticationModuleOptions> | AuthenticationModuleOptions;
}

The useFactory function can be synchronous or asynchronous. The imports array specifies modules that export the providers listed in inject.

Options Reference

features

Type: Feature[] | Required

An array of Feature enum values that determines which authentication features are enabled. Only the routes and services for enabled features are registered (with forRoot()) or accessible (with forRootAsync()).

import { Feature } from "@nestbolt/authentication";

features: [
  Feature.REGISTRATION,
  Feature.RESET_PASSWORDS,
  Feature.EMAIL_VERIFICATION,
  Feature.UPDATE_PROFILE_INFORMATION,
  Feature.UPDATE_PASSWORDS,
  Feature.TWO_FACTOR_AUTHENTICATION,
]

See the Features page for details on each value.

userRepository

Type: Type<UserRepository> | Required

The class that implements the UserRepository interface. This is the primary database adapter for the authentication module.

interface UserRepository {
  findById(id: string): Promise<AuthUser | null>;
  findByField(field: string, value: string): Promise<AuthUser | null>;
  save(user: Partial<AuthUser> & { id: string }): Promise<AuthUser>;
  create(data: Omit<AuthUser, "id">): Promise<AuthUser>;
}

The module instantiates this class and injects it using the USER_REPOSITORY token. You can also inject it in your own services:

import { Inject, Injectable } from "@nestjs/common";
import { USER_REPOSITORY, UserRepository } from "@nestbolt/authentication";

@Injectable()
export class MyService {
  constructor(
    @Inject(USER_REPOSITORY) private userRepository: UserRepository,
  ) {}
}

passwordResetRepository

Type: Type<PasswordResetRepository> | Optional

Required when Feature.RESET_PASSWORDS is enabled. Provides storage for password reset tokens.

interface PasswordResetRepository {
  createToken(email: string, hashedToken: string): Promise<void>;
  findByEmail(email: string): Promise<{ token: string; createdAt: Date } | null>;
  deleteByEmail(email: string): Promise<void>;
}

Example implementation with TypeORM:

import { Injectable } from "@nestjs/common";
import { InjectRepository } from "@nestjs/typeorm";
import { Repository } from "typeorm";
import { PasswordResetRepository } from "@nestbolt/authentication";
import { PasswordReset } from "./password-reset.entity";

@Injectable()
export class TypeOrmPasswordResetRepository implements PasswordResetRepository {
  constructor(
    @InjectRepository(PasswordReset)
    private readonly repo: Repository<PasswordReset>,
  ) {}

  async createToken(email: string, hashedToken: string): Promise<void> {
    await this.repo.delete({ email });
    await this.repo.save({ email, token: hashedToken, createdAt: new Date() });
  }

  async findByEmail(
    email: string,
  ): Promise<{ token: string; createdAt: Date } | null> {
    return this.repo.findOneBy({ email });
  }

  async deleteByEmail(email: string): Promise<void> {
    await this.repo.delete({ email });
  }
}

jwtSecret

Type: string | Required

The secret used to sign and verify JWT access tokens and two-factor challenge tokens. Use a long, random string generated with a cryptographically secure random number generator.

node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

refreshSecret

Type: string | Required

A separate secret used to sign and verify JWT refresh tokens. Must be different from jwtSecret.

node -e "console.log(require('crypto').randomBytes(64).toString('hex'))"

encryptionKey

Type: string | Required

A 32-byte key encoded as base64, used for AES-256-GCM encryption of two-factor secrets and recovery codes stored in the database. Even if you do not enable two-factor authentication, this key is required because the EncryptionService is always registered.

node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"

If the key is not exactly 32 bytes when decoded, the module throws an error at startup:

Error: Encryption key must be 32 bytes (base64-encoded).

jwtExpiresIn

Type: string | Default: "15m"

The expiry duration for JWT access tokens. Accepts any duration string supported by the ms library (e.g., "15m", "1h", "30s", "2d").

Short-lived access tokens are recommended. When a token expires, the client should use the refresh token to obtain a new access token via the POST /refresh endpoint.

refreshExpiresIn

Type: string | Default: "7d"

The expiry duration for JWT refresh tokens. Accepts the same format as jwtExpiresIn. This should be significantly longer than the access token expiry.

usernameField

Type: string | Default: "email"

The field name used for login identification. This affects:

  • The Passport local strategy configuration (which field to extract from the request body)
  • The findByField() call when looking up users during login
  • The throttle key computation for rate limiting
  • The CanonicalizeUsernameInterceptor (which field to lowercase)

If your application uses usernames instead of email addresses for login:

AuthenticationModule.forRoot({
  usernameField: "username",
  // ...
})

The login endpoint will then expect a username field in the request body instead of email.

lowercaseUsernames

Type: boolean | Default: true

When true, the CanonicalizeUsernameInterceptor lowercases the username field in the request body before authentication. This prevents case-sensitive duplicate accounts (e.g., User@Example.com and user@example.com being treated as different users).

Set to false if your application requires case-sensitive usernames.

appName

Type: string | Default: "NestBolt"

The application name used in TOTP QR codes. When a user scans the QR code with their authenticator app, this name appears as the issuer label.

appName: "My Application"

The QR code URL will be formatted as: otpauth://totp/My%20Application:user@example.com?secret=...&issuer=My%20Application

passwordTimeout

Type: number (seconds) | Default: 900

The number of seconds a password confirmation remains valid. After this timeout, the user must re-confirm their password before performing sensitive operations (such as enabling or disabling two-factor authentication).

The default of 900 seconds (15 minutes) matches the behavior of Laravel's password confirmation middleware.

passwordTimeout: 1800 // 30 minutes

loginRateLimit

Type: { ttl: number; limit: number } | Default: { ttl: 60000, limit: 5 }

Rate limiting configuration for the login endpoint.

  • ttl -- the time window in milliseconds. After this period, the attempt counter resets.
  • limit -- the maximum number of failed login attempts allowed within the time window.

When the limit is reached, subsequent login attempts receive a 429 Too Many Requests response with a message indicating how many seconds remain before they can try again. The auth.lockout event is also emitted.

loginRateLimit: { ttl: 120000, limit: 10 } // 10 attempts per 2 minutes

The throttle key is composed of the username and the client IP address, so rate limiting is per-user-per-IP.

twoFactorRateLimit

Type: { ttl: number; limit: number } | Default: { ttl: 60000, limit: 5 }

Rate limiting configuration for the two-factor challenge endpoint (POST /two-factor-challenge). Works the same as loginRateLimit but the throttle key is based on IP address only.

twoFactorRateLimit: { ttl: 60000, limit: 3 } // 3 attempts per minute

verificationRateLimit

Type: { ttl: number; limit: number } | Default: { ttl: 60000, limit: 6 }

Rate limiting configuration for the email verification resend endpoint (POST /email/verification-notification). The throttle key is composed of the user ID and IP address.

verificationRateLimit: { ttl: 60000, limit: 3 } // 3 attempts per minute

routePrefix

Type: string | Optional

A prefix applied to all authentication routes. Note that this option is defined in the interface but route prefixing should typically be handled at the NestJS application level using app.setGlobalPrefix() or via controller-level prefixes.

twoFactorOptions

Type: TwoFactorOptions | Optional

An object containing configuration specific to two-factor authentication:

interface TwoFactorOptions {
  confirm?: boolean;
  confirmPassword?: boolean;
  window?: number;
  secretLength?: number;
}

twoFactorOptions.confirm

Type: boolean | Default: false

When true, two-factor authentication requires an explicit confirmation step after setup. The user must call POST /user/confirmed-two-factor-authentication with a valid TOTP code before 2FA takes effect during login.

When false, 2FA takes effect immediately after calling POST /user/two-factor-authentication (the enable endpoint).

This is useful when you want to ensure the user has successfully configured their authenticator app before 2FA is enforced.

twoFactorOptions.confirmPassword

Type: boolean | Default: false

When true, the PasswordConfirmedGuard requires the user to have recently confirmed their password (within the passwordTimeout window) before accessing two-factor management endpoints (enable, disable, confirm, view QR code, view secret, manage recovery codes).

When false, the PasswordConfirmedGuard always passes, allowing access to 2FA management endpoints without password re-confirmation.

twoFactorOptions.window

Type: number | Default: 1

The TOTP time window tolerance. A window of 1 means the server accepts codes from the previous, current, and next time step. Increasing this value makes 2FA more lenient to clock skew between the server and the user's authenticator app, but also slightly less secure.

twoFactorOptions.secretLength

Type: number | Default: 20

The length of the TOTP secret key generated for each user. The default of 20 bytes produces a 32-character base32-encoded secret, which is the standard length used by most authenticator apps.

Complete Configuration Example

Here is a full example with all options specified:

import { Module } from "@nestjs/common";
import { ConfigModule, ConfigService } from "@nestjs/config";
import { EventEmitterModule } from "@nestjs/event-emitter";
import { TypeOrmModule } from "@nestjs/typeorm";
import {
  AuthenticationModule,
  Feature,
  CREATES_NEW_USERS,
  UPDATES_USER_PASSWORDS,
  UPDATES_USER_PROFILE,
  RESETS_USER_PASSWORDS,
} from "@nestbolt/authentication";
import { User } from "./users/user.entity";
import { PasswordReset } from "./users/password-reset.entity";
import { TypeOrmUserRepository } from "./users/typeorm-user.repository";
import { TypeOrmPasswordResetRepository } from "./users/typeorm-password-reset.repository";
import { CreateNewUser } from "./users/actions/create-new-user.action";
import { UpdateUserPassword } from "./users/actions/update-user-password.action";
import { UpdateUserProfile } from "./users/actions/update-user-profile.action";
import { ResetUserPassword } from "./users/actions/reset-user-password.action";

@Module({
  imports: [
    ConfigModule.forRoot(),
    EventEmitterModule.forRoot(),
    TypeOrmModule.forRoot({ /* ... */ }),
    TypeOrmModule.forFeature([User, PasswordReset]),
    AuthenticationModule.forRootAsync({
      imports: [ConfigModule],
      inject: [ConfigService],
      useFactory: (config: ConfigService) => ({
        features: [
          Feature.REGISTRATION,
          Feature.RESET_PASSWORDS,
          Feature.EMAIL_VERIFICATION,
          Feature.UPDATE_PROFILE_INFORMATION,
          Feature.UPDATE_PASSWORDS,
          Feature.TWO_FACTOR_AUTHENTICATION,
        ],
        userRepository: TypeOrmUserRepository,
        passwordResetRepository: TypeOrmPasswordResetRepository,
        jwtSecret: config.get<string>("JWT_SECRET")!,
        refreshSecret: config.get<string>("REFRESH_SECRET")!,
        encryptionKey: config.get<string>("ENCRYPTION_KEY")!,
        jwtExpiresIn: "15m",
        refreshExpiresIn: "7d",
        usernameField: "email",
        lowercaseUsernames: true,
        appName: config.get<string>("APP_NAME", "MyApp"),
        passwordTimeout: 900,
        loginRateLimit: { ttl: 60000, limit: 5 },
        twoFactorRateLimit: { ttl: 60000, limit: 5 },
        verificationRateLimit: { ttl: 60000, limit: 6 },
        twoFactorOptions: {
          confirm: true,
          confirmPassword: true,
          window: 1,
          secretLength: 20,
        },
      }),
    }),
  ],
  providers: [
    TypeOrmUserRepository,
    TypeOrmPasswordResetRepository,
    { provide: CREATES_NEW_USERS, useClass: CreateNewUser },
    { provide: UPDATES_USER_PASSWORDS, useClass: UpdateUserPassword },
    { provide: UPDATES_USER_PROFILE, useClass: UpdateUserProfile },
    { provide: RESETS_USER_PASSWORDS, useClass: ResetUserPassword },
  ],
})
export class AppModule {}

Next Steps

  • Features -- learn what each feature toggle enables.
  • API Routes -- see all available endpoints.