UNPKG

@bitwild/rockets-auth

Version:

Rockets Auth - Complete authentication and authorization solution for NestJS with JWT, OAuth, OTP, role-based access control, and more

1,654 lines (1,272 loc) โ€ข 89.2 kB
<!-- markdownlint-disable MD013 MD024 --> # Rockets Server Auth ## Project [![NPM Latest](https://img.shields.io/npm/v/@bitwild/rockets-auth)](https://www.npmjs.com/package/@bitwild/rockets-auth) [![NPM Downloads](https://img.shields.io/npm/dw/@bitwild/rockets-auth)](https://www.npmjs.com/package/@bitwild/rockets-auth) [![GH Last Commit](https://img.shields.io/github/last-commit/btwld/rockets?logo=github)](https://github.com/btwld/rockets) [![GH Contrib](https://img.shields.io/github/contributors/btwld/rockets?logo=github)](https://github.com/btwld/rockets/graphs/contributors) [![License](https://img.shields.io/npm/l/@bitwild/rockets-auth)](https://github.com/btwld/rockets/blob/main/LICENSE.txt) ## Table of Contents - [Introduction](#introduction) - [Overview](#overview) - [Key Features](#key-features) - [Installation](#installation) - [Tutorial](#tutorial) - [Quick Start](#quick-start) - [Basic Setup](#basic-setup) - [Your First API](#your-first-api) - [Testing the Setup](#testing-the-setup) - [How-to Guides](#how-to-guides) - [Configuration Overview](#configuration-overview) - [settings](#settings) - [authentication](#authentication) - [jwt](#jwt) - [authJwt](#authjwt) - [authLocal](#authlocal) - [authRecovery](#authrecovery) - [refresh](#refresh) - [authVerify](#authverify) - [authRouter](#authrouter) - [user](#user) - [password](#password) - [otp](#otp) - [email](#email) - [services](#services) - [crud](#crud) - [userCrud](#usercrud) - [User Management](#user-management) - [DTO Validation Patterns](#dto-validation-patterns) - [Entity Customization](#entity-customization) - [Best Practices](#best-practices) - [Development Workflow](#development-workflow) - [DTO Design Patterns](#dto-design-patterns) - [Explanation](#explanation) - [Architecture Overview](#architecture-overview) - [Design Decisions](#design-decisions) - [Core Concepts](#core-concepts) --- ## Introduction ### Overview Rockets Server Auth is a comprehensive, enterprise-grade authentication toolkit for building secure and scalable NestJS applications. It provides a unified solution that combines authentication, user management, OTP verification, email notifications, and API documentation into a single, cohesive package. Built with TypeScript and following NestJS best practices, Rockets Server Auth eliminates the complexity of setting up authentication systems while maintaining flexibility for customization and extension. **Note**: This package provides authentication endpoints and services. For core server functionality, use it together with `@bitwild/rockets-server`. ### Key Features - **๐Ÿ” Multiple Authentication Methods**: Password, JWT tokens, refresh tokens, and OAuth - **๐Ÿ”— OAuth Integration**: Support for Google, GitHub, and Apple OAuth providers - **๐Ÿ‘ฅ User Registration & Management**: Complete signup flow with validation - **๐Ÿ”‘ Password Recovery**: Email-based password reset with secure passcodes - **๐Ÿ“ฑ OTP Support**: One-time password generation and validation for secure authentication - **๐Ÿ‘‘ Role-Based Access Control**: Admin role system with user role management - **๐Ÿ“ง Email Notifications**: Built-in email service with template support for OTP and recovery - **๐Ÿ”„ Token Management**: JWT access and refresh token handling with automatic rotation - **๐Ÿ“š API Documentation**: Automatic Swagger/OpenAPI documentation generation - **๐Ÿ”ง Highly Configurable**: Extensive configuration options for all authentication modules - **๐Ÿ—๏ธ Modular Architecture**: Enable/disable specific authentication features as needed - **๐Ÿ›ก๏ธ Type Safety**: Full TypeScript support with comprehensive interfaces - **๐Ÿงช Testing Support**: Complete testing utilities and fixtures including e2e tests - **๐Ÿ”Œ Provider Integration**: JWT auth provider for `@bitwild/rockets-server` ### Installation **About this package**: > Rockets Server Auth provides complete authentication and authorization features including login, signup, recovery, OAuth, OTP, and admin functionality. It works together with `@bitwild/rockets-server` to provide a complete authenticated application solution. **Version Requirements**: - NestJS: `^10.0.0` - Node.js: `>=18.0.0` - TypeScript: `>=4.8.0` Let's create a new NestJS project: ```bash npx @nestjs/cli@10 new my-app-with-rockets --package-manager yarn --language TypeScript --strict ``` Install Rockets Server Auth and required dependencies: ```bash yarn add @bitwild/rockets-server-auth @bitwild/rockets-server \ @concepta/nestjs-typeorm-ext @concepta/nestjs-common \ typeorm @nestjs/typeorm @nestjs/config @nestjs/swagger \ class-transformer class-validator sqlite3 ``` --- ## Tutorial ### Quick Start This tutorial will guide you through setting up a complete authentication system with Rockets Server Auth in just a few steps. We'll use SQLite in-memory database for instant setup without any configuration. ### Basic Setup #### Step 1: Create Your Entities First, create the required database entities by extending the base entities provided by the SDK. These entities support the complete authentication system: ```typescript // entities/user.entity.ts import { Entity, OneToMany } from 'typeorm'; import { UserSqliteEntity } from '@concepta/nestjs-typeorm-ext'; import { UserOtpEntity } from './user-otp.entity'; import { FederatedEntity } from './federated.entity'; @Entity() export class UserEntity extends UserSqliteEntity { @OneToMany(() => UserOtpEntity, (userOtp) => userOtp.assignee) userOtps?: UserOtpEntity[]; @OneToMany(() => FederatedEntity, (federated) => federated.assignee) federatedAccounts?: FederatedEntity[]; } ``` ```typescript // entities/user-otp.entity.ts import { Entity, ManyToOne } from 'typeorm'; import { ReferenceIdInterface } from '@concepta/nestjs-common'; import { OtpSqliteEntity } from '@concepta/nestjs-typeorm-ext'; import { UserEntity } from './user.entity'; @Entity() export class UserOtpEntity extends OtpSqliteEntity { @ManyToOne(() => UserEntity, (user) => user.userOtps) assignee!: ReferenceIdInterface; } ``` ```typescript // entities/federated.entity.ts import { Entity, ManyToOne } from 'typeorm'; import { ReferenceIdInterface } from '@concepta/nestjs-common'; import { FederatedSqliteEntity } from '@concepta/nestjs-typeorm-ext'; import { UserEntity } from './user.entity'; @Entity() export class FederatedEntity extends FederatedSqliteEntity { @ManyToOne(() => UserEntity, (user) => user.federatedAccounts) assignee!: ReferenceIdInterface; } ``` #### Step 2: Set Up Environment Variables (Production Only) For production, create a `.env` file with JWT secrets: ```env # Required for production JWT_MODULE_ACCESS_SECRET=your-super-secret-jwt-access-key-here # Optional - defaults to access secret if not provided JWT_MODULE_REFRESH_SECRET=your-super-secret-jwt-refresh-key-here NODE_ENV=development ``` **Note**: In development, JWT secrets are auto-generated if not provided. #### Step 3: Configure Your Module Create your main application module with the minimal Rockets SDK setup: ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { ConfigModule, ConfigService } from '@nestjs/config'; import { RocketsAuthModule } from '@bitwild/rockets-server-auth'; import { TypeOrmExtModule } from '@concepta/nestjs-typeorm-ext'; import { UserEntity } from './entities/user.entity'; import { UserOtpEntity } from './entities/user-otp.entity'; import { FederatedEntity } from './entities/federated.entity'; @Module({ imports: [ ConfigModule.forRoot({ isGlobal: true, envFilePath: '.env', }), // Database configuration - SQLite in-memory for easy testing TypeOrmExtModule.forRoot({ type: 'sqlite', database: ':memory:', // In-memory database - no files created synchronize: true, // Auto-create tables (dev only) autoLoadEntities: true, logging: false, // Set to true to see SQL queries entities: [UserEntity, UserOtpEntity, FederatedEntity], }), // Rockets SDK configuration - minimal setup RocketsAuthModule.forRootAsync({ imports: [ TypeOrmModule.forFeature([UserEntity]), TypeOrmExtModule.forFeature({ user: { entity: UserEntity }, role: { entity: RoleEntity }, userRole: { entity: UserRoleEntity }, userOtp: { entity: UserOtpEntity }, federated: { entity: FederatedEntity }, }), ], inject: [ConfigService], useFactory: (configService: ConfigService) => ({ // Required services services: { mailerService: { sendMail: (options: any) => { console.log('๐Ÿ“ง Email would be sent:', { to: options.to, subject: options.subject, // Don't log the full content in examples }); return Promise.resolve(); }, }, }, // Email and OTP settings settings: { email: { from: 'noreply@yourapp.com', baseUrl: 'http://localhost:3000', templates: { sendOtp: { fileName: 'otp.template.hbs', subject: 'Your verification code', }, }, }, otp: { assignment: 'userOtp', category: 'auth-login', type: 'numeric', expiresIn: '10m', }, }, // Optional: Admin and Signup endpoints can be enabled via userCrud extras }), }), ], }) export class AppModule {} ``` #### Step 4: Create Your Main Application ```typescript // main.ts import { NestFactory } from '@nestjs/core'; import { ValidationPipe } from '@nestjs/common'; import { SwaggerModule, DocumentBuilder } from '@nestjs/swagger'; import { ExceptionsFilter } from '@concepta/nestjs-common'; import { SwaggerUiService } from '@concepta/nestjs-swagger-ui'; import { AppModule } from './app.module'; async function bootstrap() { const app = await NestFactory.create(AppModule); // Enable validation app.useGlobalPipes(new ValidationPipe()); // get the swagger ui service, and set it up const swaggerUiService = app.get(SwaggerUiService); swaggerUiService.builder().addBearerAuth(); swaggerUiService.setup(app); const exceptionsFilter = app.get(HttpAdapterHost); app.useGlobalFilters(new ExceptionsFilter(exceptionsFilter)); await app.listen(3000); console.log('Application is running on: http://localhost:3000'); console.log('API Documentation: http://localhost:3000/api'); console.log('Using SQLite in-memory database (data resets on restart)'); } bootstrap(); ``` ### Your First API With the basic setup complete, your application now provides these endpoints: #### Authentication Endpoints - `POST /signup` - Register a new user - `POST /token/password` - Login with username/password (returns 200 OK with tokens) - `POST /token/refresh` - Refresh access token - `POST /recovery/login` - Initiate username recovery - `POST /recovery/password` - Initiate password reset - `PATCH /recovery/password` - Reset password with passcode - `GET /recovery/passcode/:passcode` - Validate recovery passcode #### OAuth Endpoints - `GET /oauth/authorize` - Redirect to OAuth provider (Google, GitHub, Apple) - `GET /oauth/callback` - Handle OAuth callback and return tokens - `POST /oauth/callback` - Handle OAuth callback via POST method #### User Profile Endpoints (from @bitwild/rockets-server) When used together with `@bitwild/rockets-server`, these endpoints are also available: - `GET /me` - Get current user profile with metadata - `PATCH /me` - Update current user metadata #### Admin Endpoints (optional) If you enable the admin module (see How-to Guides > admin), these routes become available and are protected by `AdminGuard`: **User Administration:** - `GET /admin/users` - List users - `GET /admin/users/:id` - Get a user - `POST /admin/users` - Create a user - `PATCH /admin/users/:id` - Update a user - `PUT /admin/users/:id` - Replace a user - `DELETE /admin/users/:id` - Delete a user **Role Administration:** - `GET /admin/users/:userId/roles` - List roles assigned to a specific user - `POST /admin/users/:userId/roles` - Assign role to a specific user #### OTP Endpoints - `POST /otp` - Send OTP to user email (returns 200 OK) - `PATCH /otp` - Confirm OTP code (returns 200 OK with tokens) **Note**: Rockets Server Auth provides authentication endpoints. For user profile management (`/me` endpoints), use it together with `@bitwild/rockets-server`. ### Testing the Setup #### 1. Start Your Application ```bash npm run start:dev ``` #### 2. Register a New User ```bash curl -X POST http://localhost:3000/signup \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "password": "SecurePass123", "username": "testuser" }' ``` Expected response: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "testuser", "active": true, "dateCreated": "2024-01-01T00:00:00.000Z", "dateUpdated": "2024-01-01T00:00:00.000Z", "dateDeleted": null, "version": 1 } ``` #### 3. Login and Get Access Token ```bash curl -X POST http://localhost:3000/token/password \ -H "Content-Type: application/json" \ -d '{ "username": "testuser", "password": "SecurePass123" }' ``` Expected response (200 OK): ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` **Note**: The login endpoint returns a 200 OK status (not 201 Created) as it's retrieving tokens, not creating a new resource. **Defaults Working**: All authentication endpoints work out-of-the-box with sensible defaults. #### 4. Access Protected Endpoint ```bash curl -X GET http://localhost:3000/user \ -H "Authorization: Bearer YOUR_ACCESS_TOKEN_HERE" ``` Expected response: ```json { "id": "550e8400-e29b-41d4-a716-446655440000", "email": "user@example.com", "username": "testuser", "active": true, "dateCreated": "2024-01-01T00:00:00.000Z", "dateUpdated": "2024-01-01T00:00:00.000Z", "dateDeleted": null, "version": 1 } ``` #### 5. Test OTP Functionality ```bash # Send OTP (returns 200 OK) curl -X POST http://localhost:3000/otp \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com" }' # Check console for the "email" that would be sent with the OTP code # Then confirm with the code (replace 123456 with actual code) # Returns 200 OK with tokens curl -X PATCH http://localhost:3000/otp \ -H "Content-Type: application/json" \ -d '{ "email": "user@example.com", "passcode": "123456" }' ``` Expected OTP confirm response (200 OK): ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` #### 6. Test OAuth Functionality ```bash # Redirect to Google OAuth (returns 200 OK) curl -X GET "http://localhost:3000/oauth/authorize?provider=google&scopes=email,userMetadata" # Redirect to GitHub OAuth (returns 200 OK) curl -X GET "http://localhost:3000/oauth/authorize?provider=github&scopes=user,email" # Redirect to Apple OAuth (returns 200 OK) curl -X GET "http://localhost:3000/oauth/authorize?provider=apple&scopes=email,name" # Handle OAuth callback (returns 200 OK with tokens) curl -X GET "http://localhost:3000/oauth/callback?provider=google" ``` Expected OAuth callback response (200 OK): ```json { "accessToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...", "refreshToken": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9..." } ``` ๐ŸŽ‰ **Congratulations!** You now have a fully functional authentication system with user management, JWT tokens, OAuth integration, and API documentation running with minimal configuration. **๐Ÿ’ก Pro Tip**: Since we're using an in-memory database, all data is lost when you restart the application. This is perfect for testing and development! ### Integrating with @bitwild/rockets-server Use `RocketsJwtAuthProvider` from this package as the `authProvider` for `@bitwild/rockets-server` so your app has a global guard that validates tokens issued by the auth module: ```typescript // app.module.ts import { Module } from '@nestjs/common'; import { TypeOrmModule } from '@nestjs/typeorm'; import { RocketsAuthModule, RocketsJwtAuthProvider } from '@bitwild/rockets-server-auth'; import { RocketsModule } from '@bitwild/rockets-server'; @Module({ imports: [ TypeOrmModule.forFeature([UserEntity]), // IMPORTANT: RocketsAuthModule MUST be imported BEFORE RocketsModule // because RocketsModule depends on RocketsJwtAuthProvider from RocketsAuthModule RocketsAuthModule.forRootAsync({ imports: [TypeOrmModule.forFeature([UserEntity])], useFactory: () => ({ services: { mailerService: { sendMail: async () => Promise.resolve() }, }, }), }), // RocketsModule imports AFTER RocketsAuthModule to access RocketsJwtAuthProvider RocketsModule.forRootAsync({ inject: [RocketsJwtAuthProvider], useFactory: (authProvider: RocketsJwtAuthProvider) => ({ authProvider, userMetadata: { createDto: UserMetadataCreateDto, updateDto: UserMetadataUpdateDto }, enableGlobalGuard: true, }), }), ], }) export class AppModule {} ``` ### Troubleshooting #### Common Issues #### Module Import Order **Problem**: `Nest can't resolve dependencies of RocketsModule (?). Please make sure that the RocketsJwtAuthProvider is available.` **Cause**: RocketsModule is imported before RocketsAuthModule **Solution**: Always import RocketsAuthModule **before** RocketsModule: ```typescript @Module({ imports: [ RocketsAuthModule.forRootAsync({...}), // โœ… First RocketsModule.forRootAsync({...}), // โœ… Second ], }) ``` **Wrong Order** โŒ: ```typescript RocketsModule.forRootAsync({...}), // Wrong - first RocketsAuthModule.forRootAsync({...}), // Wrong - second ``` #### AuthJwtGuard Reflector dependency If you enable `authJwt.appGuard: true` and see a dependency error regarding `Reflector`, ensure `Reflector` is available (provided in your module or via Nest core). The sample app includes `Reflector` in providers. #### Module Resolution Errors If you're getting dependency resolution errors: 1. **NestJS Version**: Ensure you're using NestJS `^10.0.0` 2. **Alpha Packages**: All `@concepta/*` packages should use the same alpha version (e.g., `^7.0.0-alpha.6`) 3. **Clean Installation**: Try deleting `node_modules` and `package-lock.json`, then run `yarn install` #### Module Resolution Errors (TypeScript) If TypeScript can't find modules like `@concepta/nestjs-typeorm-ext`: ```bash yarn add @concepta/nestjs-typeorm-ext @concepta/nestjs-common \ --save ``` All dependencies listed in the installation section are required and must be installed explicitly. --- ## How-to Guides This section provides comprehensive guides for every configuration option available in the `RocketsAuthOptionsInterface`. Each guide explains what the option does, how it connects with core modules, when you should customize it (since defaults are provided), and includes real-world examples. ### Configuration Overview Rockets Server Auth uses a hierarchical configuration system with the following structure: ```typescript interface RocketsAuthOptionsInterface { settings?: RocketsAuthSettingsInterface; swagger?: SwaggerUiOptionsInterface; authentication?: AuthenticationOptionsInterface; jwt?: JwtOptions; authJwt?: AuthJwtOptionsInterface; authLocal?: AuthLocalOptionsInterface; authRecovery?: AuthRecoveryOptionsInterface; refresh?: AuthRefreshOptions; authVerify?: AuthVerifyOptionsInterface; authRouter?: AuthRouterOptionsInterface; user?: UserOptionsInterface; password?: PasswordOptionsInterface; otp?: OtpOptionsInterface; email?: Partial<EmailOptionsInterface>; services: { userModelService?: RocketsAuthUserModelServiceInterface; notificationService?: RocketsAuthNotificationServiceInterface; verifyTokenService?: VerifyTokenService; issueTokenService?: IssueTokenServiceInterface; validateTokenService?: ValidateTokenServiceInterface; validateUserService?: AuthLocalValidateUserServiceInterface; userPasswordService?: UserPasswordServiceInterface; userPasswordHistoryService?: UserPasswordHistoryServiceInterface; userAccessQueryService?: CanAccess; mailerService: EmailServiceInterface; // Required }; } ``` --- ### settings **What it does**: Global settings that configure the custom OTP and email services provided by RocketsAuth. These settings are used by the custom OTP controller and notification services, not by the core authentication modules. **Core services it connects to**: RocketsAuthOtpService, RocketsAuthNotificationService **When to update**: Required when using the custom OTP endpoints (`POST /otp`, `PATCH /otp`). The defaults use placeholder values that won't work in real applications. **Real-world example**: Setting up email configuration for the custom OTP system: ```typescript settings: { email: { from: 'noreply@mycompany.com', baseUrl: 'https://app.mycompany.com', tokenUrlFormatter: (baseUrl, token) => `${baseUrl}/auth/verify?token=${token}&utm_source=email`, templates: { sendOtp: { fileName: 'custom-otp.template.hbs', subject: 'Your {{appName}} verification code - expires in 10 minutes', }, }, }, otp: { assignment: 'userOtp', category: 'auth-login', type: 'numeric', // Use 6-digit numeric codes instead of UUIDs expiresIn: '10m', // Shorter expiry for security }, } ``` --- ### authentication **What it does**: Core authentication module configuration that handles token verification, validation services and the payload of the token. It provides three key services: - **verifyTokenService**: Handles two-step token verification - first cryptographically verifying JWT tokens using JwtVerifyTokenService, then optionally validating the decoded payload through a validateTokenService. Used by authentication guards and protected routes. - **issueTokenService**: Generates and signs new JWT tokens for authenticated users. Creates both access and refresh tokens with user payload data and builds complete authentication responses. Used during login, signup, and token refresh flows. - **validateTokenService**: Optional service for custom business logic validation beyond basic JWT verification. Can check user existence, token blacklists, account status, or any other custom validation rules. **Core modules it connects to**: AuthenticationModule (the base authentication system) **When to update**: When you need to customize core authentication behavior, provide custom token services or change how the token payload is structured. Common scenarios include: - Implementing custom token verification logic - Adding business-specific token validation rules - Modifying token generation and payload structure - Integrating with external authentication systems **Real-world example**: Custom authentication configuration: ```typescript authentication: { settings: { enableGuards: true, // Default: true }, // Optional: Custom services (defaults are provided) issueTokenService: new CustomTokenIssuanceService(), verifyTokenService: new CustomTokenVerificationService(), validateTokenService: new CustomTokenValidationService(), } ``` **Note**: All token services have working defaults. Only customize if you need specific business logic. --- ### jwt **What it does**: JWT token configuration including secrets, expiration times, and token services. **Core modules it connects to**: JwtModule, AuthJwtModule, AuthRefreshModule **When to update**: Only needed if loading JWT settings from a source other than environment variables (e.g. config files, external services, etc). **Environment Variables**: The JWT module automatically uses these environment variables with sensible defaults: - `JWT_MODULE_DEFAULT_EXPIRES_IN` (default: `'1h'`) - `JWT_MODULE_ACCESS_EXPIRES_IN` (default: `'1h'`) - `JWT_MODULE_REFRESH_EXPIRES_IN` (default: `'99y'`) - `JWT_MODULE_ACCESS_SECRET` (required in production, auto-generated in development, if not provided) - `JWT_MODULE_REFRESH_SECRET` (defaults to access secret if not provided) **Default Behavior**: - **Development**: JWT secrets are auto-generated if not provided - **Production**: `JWT_MODULE_ACCESS_SECRET` is required (with NODE_ENV=production) - **Token Services**: Default `JwtIssueTokenService` and `JwtVerifyTokenService` are provided - **Multiple Token Types**: Separate access and refresh token handling **Security Notes**: - Production requires explicit JWT secrets for security - Development auto-generates secrets for convenience - Refresh tokens have longer expiration by default - All token operations are handled automatically **Real-world example**: Custom JWT configuration (optional - defaults work for most cases): ```typescript jwt: { settings: { default: { signOptions: { issuer: 'mycompany.com', audience: 'mycompany-api', }, }, access: { signOptions: { issuer: 'mycompany.com', audience: 'mycompany-api', }, }, refresh: { signOptions: { issuer: 'mycompany.com', audience: 'mycompany-refresh', }, }, }, // Optional: Custom services (defaults are provided) jwtIssueTokenService: new CustomJwtIssueService(), jwtVerifyTokenService: new CustomJwtVerifyService(), } ``` **Note**: Environment variables are automatically used for secrets and expiration times. Only customize `jwt.settings` if you need specific JWT options like issuer/audience, you can also use the environment variables to configure the JWT module. --- ### authJwt **What it does**: JWT-based authentication strategy configuration, including how tokens are extracted from requests. **Core modules it connects to**: AuthJwtModule, provides JWT authentication guards and strategies **When to update**: When you need custom token extraction logic or want to modify JWT authentication behavior. **Real-world example**: Custom token extraction for mobile apps that send tokens in custom headers: ```typescript authJwt: { settings: { jwtFromRequest: ExtractJwt.fromExtractors([ ExtractJwt.fromAuthHeaderAsBearerToken(), // Standard Bearer token ExtractJwt.fromHeader('x-api-token'), // Custom header for mobile (request) => { // Custom extraction from cookies for web apps return request.cookies?.access_token; }, ]), }, // Optional settings (defaults are sensible) appGuard: true, // Default: true - set true to apply JWT guard globally // Optional services (defaults are provided) verifyTokenService: new CustomJwtVerifyService(), userModelService: new CustomUserLookupService(), } ``` **Note**: Default token extraction uses standard Bearer token from Authorization header. Only customize if you need alternative token sources. --- ### authLocal **What it does**: Local authentication (username/password) configuration and validation services. **Core modules it connects to**: AuthLocalModule, handles login endpoint and credential validation **When to update**: When you need custom password validation, user lookup logic, or want to integrate with external authentication systems. **Real-world example**: Custom local authentication with email login: ```typescript authLocal: { settings: { usernameField: 'email', // Default: 'username' passwordField: 'password', // Default: 'password' }, // Optional services (defaults work with TypeORM entities) validateUserService: new CustomUserValidationService(), userModelService: new CustomUserModelService(), issueTokenService: new CustomTokenIssuanceService(), } ``` **Environment Variables**: - `AUTH_LOCAL_USERNAME_FIELD` - defaults to `'username'` - `AUTH_LOCAL_PASSWORD_FIELD` - defaults to `'password'` **Note**: The default services work automatically with your TypeORM User entity. Only customize if you need specific validation logic. --- ### authRecovery **What it does**: Password recovery and account recovery functionality including email notifications and OTP generation. **Core modules it connects to**: AuthRecoveryModule, provides password reset endpoints **When to update**: When you need custom recovery flows, different notification methods, or integration with external services. **Real-world example**: Multi-channel recovery system with SMS and email options: ```typescript authRecovery: { settings: { tokenExpiresIn: '1h', // Recovery token expiration maxAttempts: 3, // Maximum recovery attempts }, emailService: new CustomEmailService(), otpService: new CustomOtpService(), userModelService: new CustomUserModelService(), userPasswordService: new CustomPasswordService(), notificationService: new MultiChannelNotificationService(), // SMS + Email } ``` --- ### refresh **What it does**: Refresh token configuration for maintaining user sessions without requiring re-authentication. **Core modules it connects to**: AuthRefreshModule, provides token refresh endpoints **When to update**: When you need custom refresh token behavior, different expiration strategies, or want to implement token rotation. **Real-world example**: Secure refresh token rotation for high-security applications: ```typescript refresh: { settings: { jwtFromRequest: ExtractJwt.fromBodyField('refreshToken'), tokenRotation: true, // Issue new refresh token on each use revokeOnUse: true, // Revoke old refresh token }, verifyTokenService: new SecureRefreshTokenVerifyService(), issueTokenService: new RotatingTokenIssueService(), userModelService: new AuditableUserModelService(), // Log refresh attempts } ``` --- ### authVerify **What it does**: Email verification and account verification functionality. **Core modules it connects to**: AuthVerifyModule, provides email verification endpoints **When to update**: When you need custom verification flows, different verification methods, or want to integrate with external verification services. **Real-world example**: Multi-step verification with phone and email: ```typescript authVerify: { settings: { verificationRequired: true, // Require verification before login verificationExpiresIn: '24h', }, emailService: new CustomEmailService(), otpService: new CustomOtpService(), userModelService: new CustomUserModelService(), notificationService: new MultiStepVerificationService(), // Email + SMS } ``` --- ### authRouter **What it does**: OAuth router configuration that handles routing to different OAuth providers (Google, GitHub, Apple) based on the provider parameter in the request. **Core modules it connects to**: AuthRouterModule, provides OAuth routing and guards **When to update**: When you need to add or remove OAuth providers, customize OAuth guard behavior, or modify OAuth routing logic. **Real-world example**: Custom OAuth configuration with multiple providers: ```typescript authRouter: { guards: [ { name: 'google', guard: AuthGoogleGuard }, { name: 'github', guard: AuthGithubGuard }, { name: 'apple', guard: AuthAppleGuard }, // Add custom OAuth providers { name: 'custom', guard: CustomOAuthGuard }, ], settings: { // Custom OAuth router settings defaultProvider: 'google', enableProviderValidation: true, }, } ``` **Default Configuration**: The SDK automatically configures Google, GitHub, and Apple OAuth providers with sensible defaults. **OAuth Flow**: 1. Client calls `/oauth/authorize?provider=google&scopes=email userMetadata` 2. AuthRouterGuard routes to the appropriate OAuth guard based on provider 3. OAuth guard redirects to the provider's authorization URL 4. User authenticates with the OAuth provider 5. Provider redirects back to `/oauth/callback?provider=google` 6. AuthRouterGuard processes the callback and returns JWT tokens --- ### user **What it does**: User management configuration including CRUD operations, password management, and access control. **Core modules it connects to**: UserModule, provides user management endpoints **When to update**: When you need custom user management logic, different access control, or want to integrate with external user systems. **Real-world example**: Enterprise user management with role-based access control: ```typescript user: { imports: [ TypeOrmExtModule.forFeature({ user: { entity: UserEntity }, userMetadata: { entity: UserMetadataEntity }, userPasswordHistory: { entity: UserPasswordHistoryEntity }, }), ], settings: { enableUserMetadatas: true, // Enable user userMetadatas enablePasswordHistory: true, // Track password history }, userModelService: new EnterpriseUserModelService(), userPasswordService: new SecurePasswordService(), userAccessQueryService: new RoleBasedAccessService(), userPasswordHistoryService: new PasswordHistoryService(), } ``` --- ### password **What it does**: Password policy and validation configuration. **Core modules it connects to**: PasswordModule, provides password validation across the system **When to update**: When you need to enforce specific password policies or integrate with external password validation services. **Real-world example**: Enterprise password policy with complexity requirements: ```typescript password: { settings: { minPasswordStrength: 3, // 0-4 scale (default: 2) maxPasswordAttempts: 5, // Default: 3 requireCurrentToUpdate: true, // Default: false passwordHistory: 12, // Remember last 12 passwords }, } ``` **Environment Variables**: - `PASSWORD_MIN_PASSWORD_STRENGTH` - defaults to `4` if production, `0` if development (0-4 scale) - `PASSWORD_MAX_PASSWORD_ATTEMPTS` - defaults to `3` - `PASSWORD_REQUIRE_CURRENT_TO_UPDATE` - defaults to `false` **Note**: Password strength is automatically calculated using zxcvbn. History tracking is optional and requires additional configuration. --- ### otp **What it does**: One-time password configuration for the OTP system. **Core modules it connects to**: OtpModule, provides OTP generation and validation **When to update**: When you need custom OTP behavior, different OTP types, or want to integrate with external OTP services. **Interface**: `OtpSettingsInterface` from `@concepta/nestjs-otp` ```typescript interface OtpSettingsInterface { types: Record<string, OtpTypeServiceInterface>; clearOnCreate: boolean; keepHistoryDays?: number; rateSeconds?: number; rateThreshold?: number; } ``` **Environment Variables**: - `OTP_CLEAR_ON_CREATE` - defaults to `false` - `OTP_KEEP_HISTORY_DAYS` - no default (optional) - `OTP_RATE_SECONDS` - no default (optional) - `OTP_RATE_THRESHOLD` - no default (optional) **Real-world example**: High-security OTP configuration with rate limiting: ```typescript otp: { imports: [ TypeOrmExtModule.forFeature({ userOtp: { entity: UserOtpEntity }, }), ], settings: { types: { uuid: { generator: () => require('uuid').v4(), validator: (value: string, expected: string) => value === expected, }, }, clearOnCreate: true, // Clear old OTPs when creating new ones keepHistoryDays: 30, // Keep OTP history for 30 days rateSeconds: 60, // Minimum 60 seconds between OTP requests rateThreshold: 5, // Maximum 5 attempts within rate window }, } ``` --- ### email **What it does**: Email service configuration for sending notifications and templates. **Core modules it connects to**: EmailModule, used by AuthRecoveryModule and AuthVerifyModule **When to update**: When you need to use a different email service provider or customize email sending behavior. **Interface**: `EmailServiceInterface` from `@concepta/nestjs-email` **Configuration example**: ```typescript email: { service: new YourCustomEmailService(), // Must implement EmailServiceInterface settings: {}, // Settings object is empty } ``` --- ### services The `services` object contains injectable services that customize core functionality. Each service has specific responsibilities: #### services.userModelService **What it does**: Core user lookup service used across multiple authentication modules. **Core modules it connects to**: AuthJwtModule, AuthRefreshModule, AuthLocalModule, AuthRecoveryModule **When to update**: When you need to integrate with external user systems or implement custom user lookup logic. **Interface**: `UserModelServiceInterface` from `@concepta/nestjs-user` **Configuration example**: ```typescript services: { userModelService: new YourCustomUserModelService(), // Must implement UserModelServiceInterface } ``` #### services.notificationService **What it does**: Handles sending notifications for recovery and verification processes. **Core modules it connects to**: AuthRecoveryModule, AuthVerifyModule **When to update**: When you need custom notification channels (SMS, push notifications) or integration with external notification services. **Interface**: `NotificationServiceInterface` from `@concepta/nestjs-authentication` **Configuration example**: ```typescript services: { notificationService: new YourCustomNotificationService(), // Must implement NotificationServiceInterface } ``` #### services.verifyTokenService **What it does**: Verifies JWT tokens for authentication. **Core modules it connects to**: AuthenticationModule, JwtModule **When to update**: When you need custom token verification logic or integration with external token validation services. **Interface**: `VerifyTokenServiceInterface` from `@concepta/nestjs-authentication` **Configuration example**: ```typescript services: { verifyTokenService: new YourCustomVerifyTokenService(), // Must implement VerifyTokenServiceInterface } ``` #### services.issueTokenService **What it does**: Issues JWT tokens for authenticated users. **Core modules it connects to**: AuthenticationModule, AuthLocalModule, AuthRefreshModule **When to update**: When you need custom token issuance logic or want to include additional claims. **Interface**: `IssueTokenServiceInterface` from `@concepta/nestjs-authentication` **Configuration example**: ```typescript services: { issueTokenService: new YourCustomIssueTokenService(), // Must implement IssueTokenServiceInterface } ``` #### services.validateTokenService **What it does**: Validates token structure and claims. **Core modules it connects to**: AuthenticationModule **When to update**: When you need custom token validation rules or security checks. **Interface**: `ValidateTokenServiceInterface` from `@concepta/nestjs-authentication` **Configuration example**: ```typescript services: { validateTokenService: new YourCustomValidateTokenService(), // Must implement ValidateTokenServiceInterface } ``` #### services.validateUserService **What it does**: Validates user credentials during local authentication. **Core modules it connects to**: AuthLocalModule **When to update**: When you need custom credential validation or integration with external authentication systems. **Interface**: `ValidateUserServiceInterface` from `@concepta/nestjs-authentication` **Configuration example**: ```typescript services: { validateUserService: new YourCustomValidateUserService(), // Must implement ValidateUserServiceInterface } ``` #### services.userPasswordService **What it does**: Handles password operations including hashing and validation. **Core modules it connects to**: UserModule, AuthRecoveryModule **When to update**: When you need custom password hashing algorithms or password policy enforcement. **Interface**: `UserPasswordServiceInterface` from `@concepta/nestjs-user` **Configuration example**: ```typescript services: { userPasswordService: new YourCustomUserPasswordService(), // Must implement UserPasswordServiceInterface } ``` #### services.userPasswordHistoryService **What it does**: Manages password history to prevent password reuse. **Core modules it connects to**: UserModule **When to update**: When you need to enforce password history policies or custom password tracking. **Interface**: `UserPasswordHistoryServiceInterface` from `@concepta/nestjs-user` **Configuration example**: ```typescript services: { userPasswordHistoryService: new YourCustomPasswordHistoryService(), // Must implement UserPasswordHistoryServiceInterface } ``` #### services.userAccessQueryService **What it does**: Handles access control and permission queries. **Core modules it connects to**: UserModule **When to update**: When you need custom access control logic or integration with external authorization systems. **Interface**: `CanAccess` from `@concepta/nestjs-common` **Configuration example**: ```typescript services: { userAccessQueryService: new YourCustomAccessQueryService(), // Must implement CanAccess } ``` #### services.mailerService (Required) **What it does**: Core email sending service used throughout the system. **Core modules it connects to**: EmailModule, AuthRecoveryModule, AuthVerifyModule, OTP system **When to update**: Always required. You must provide a working email service for production. **Interface**: `EmailServiceInterface` from `@concepta/nestjs-email` **Configuration example**: ```typescript services: { mailerService: new YourCustomMailerService(), // Must implement EmailServiceInterface } ``` --- ### crud Add a new CRUD endpoint for any entity using `@concepta/nestjs-crud` with minimal boilerplate. This section provides step-by-step instructions for AI agents to implement CRUD operations following the v7.0.0-alpha.6 patterns. **IMPORTANT FOR AI AGENTS**: Follow this exact pattern when implementing CRUD functionality. The pattern uses `ConfigurableCrudBuilder` plus a `TypeOrmCrudAdapter` and requires specific imports and structure. #### Required Imports for CRUD Implementation **Always import these modules for CRUD:** ```typescript import { CrudModule } from '@concepta/nestjs-crud'; // For CrudModule.forRoot() import { TypeOrmModule } from '@nestjs/typeorm'; // For TypeOrmModule.forFeature() import { TypeOrmCrudAdapter } from '@concepta/nestjs-crud'; // For the adapter pattern ``` **DO NOT use TypeOrmExtModule for CRUD** - this is only for model services. Use standard TypeOrmModule instead. #### Module Import Requirements **Required in your module:** ```typescript @Module({ imports: [ CrudModule.forRoot({}), // Required for CRUD functionality TypeOrmModule.forFeature([ProjectEntity]), // Required for repository injection // NOT TypeOrmExtModule - that's only for model services ], // ... rest of module }) ``` #### Complete CRUD Implementation Pattern #### 1) Define your Entity ```typescript // entities/project.entity.ts import { Entity, Column, PrimaryGeneratedColumn } from 'typeorm'; @Entity('project') export class ProjectEntity { @PrimaryGeneratedColumn('uuid') id!: string; @Column() name!: string; @Column({ nullable: true }) description?: string; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP' }) createdAt!: Date; @Column({ type: 'timestamp', default: () => 'CURRENT_TIMESTAMP', onUpdate: 'CURRENT_TIMESTAMP' }) dateUpdated!: Date; } ``` #### 2) Define your DTOs ```typescript // dto/project/project.dto.ts import { ApiProperty } from '@nestjs/swagger'; export class ProjectDto { @ApiProperty() id!: string; @ApiProperty() name!: string; @ApiProperty({ required: false }) description?: string; @ApiProperty() dateCreated!: Date; @ApiProperty() dateUpdated!: Date; } // dto/project/project-create.dto.ts import { ApiProperty } from '@nestjs/swagger'; import { IsNotEmpty, IsOptional, IsString } from 'class-validator'; export class ProjectCreateDto { @ApiProperty() @IsString() @IsNotEmpty() name!: string; @ApiProperty({ required: false }) @IsString() @IsOptional() description?: string; } // dto/project/project-update.dto.ts import { PartialType } from '@nestjs/swagger'; import { ProjectCreateDto } from './project-create.dto'; export class ProjectUpdateDto extends PartialType(ProjectCreateDto) {} // dto/project/project-paginated.dto.ts import { CrudResponsePaginatedDto } from '@concepta/nestjs-crud'; import { ProjectDto } from './project.dto'; export class ProjectPaginatedDto extends CrudResponsePaginatedDto(ProjectDto) {} ``` #### 3) Create a TypeOrmCrudAdapter (REQUIRED PATTERN) **AI AGENTS: This is the correct adapter pattern for v7.0.0-alpha.6:** ```typescript // adapters/project-typeorm-crud.adapter.ts import { Repository } from 'typeorm'; import { Injectable } from '@nestjs/common'; import { InjectRepository } from '@nestjs/typeorm'; import { TypeOrmCrudAdapter } from '@concepta/nestjs-crud'; import { ProjectEntity } from '../entities/project.entity'; /** * Project CRUD Adapter using TypeORM * * PATTERN NOTE: This follows the standard pattern where: * - Extends TypeOrmCrudAdapter<EntityType> * - Injects Repository<EntityType> via @InjectRepository * - Calls super(repo) to initialize the adapter */ @Injectable() export class ProjectTypeOrmCrudAdapter extends TypeOrmCrudAdapter<ProjectEntity> { constructor( @InjectRepository(ProjectEntity) repo: Repository<ProjectEntity>, ) { super(repo); } } ``` #### 4) Create a CRUD Builder with build() Method ```typescript // crud/project-crud.builder.ts import { ApiTags } from '@nestjs/swagger'; import { ConfigurableCrudBuilder } from '@concepta/nestjs-crud'; import { ProjectEntity } from '../entities/project.entity'; import { ProjectDto } from '../dto/project/project.dto'; import { ProjectCreateDto } from '../dto/project/project-create.dto'; import { ProjectUpdateDto } from '../dto/project/project-update.dto'; import { ProjectPaginatedDto } from '../dto/project/project-paginated.dto'; import { ProjectTypeOrmCrudAdapter } from '../adapters/project-typeorm-crud.adapter'; export const PROJECT_CRUD_SERVICE_TOKEN = Symbol('PROJECT_CRUD_SERVICE_TOKEN'); export class ProjectCrudBuilder extends ConfigurableCrudBuilder< ProjectEntity, ProjectCreateDto, ProjectUpdateDto > { constructor() { super({ service: { injectionToken: PROJECT_CRUD_SERVICE_TOKEN, adapter: ProjectTypeOrmCrudAdapter, }, controller: { path: 'projects', model: { type: ProjectDto, paginatedType: ProjectPaginatedDto, }, extraDecorators: [ApiTags('projects')], }, getMany: {}, getOne: {}, createOne: { dto: ProjectCreateDto }, updateOne: { dto: ProjectUpdateDto }, replaceOne: { dto: ProjectUpdateDto }, deleteOne: {}, }); } } ``` #### 5) Use build() Method to Get ConfigurableClasses **AI AGENTS: You must call .build() and extract the classes:** ```typescript // crud/project-crud.builder.ts (continued) // Call build() to get the configurable classes const { ConfigurableServiceClass, ConfigurableControllerClass, } = new ProjectCrudBuilder().build(); // Export the classes that extend the configurable classes export class ProjectCrudService extends ConfigurableServiceClass { // Inherits all CRUD operations: getMany, getOne, createOne, updateOne, replaceOne, deleteOne } export class ProjectController extends ConfigurableControllerClass { // Inherits all CRUD endpoints: // GET /projects (getMany) // GET /projects/:id (getOne) // POST /projects (createOne) // PATCH /projects/:id (updateOne) // PUT /projects/:id (replaceOne) // DELETE /projects/:id (deleteOne) } ``` #### 6) Register in a Module (COMPLETE PATTERN) **AI AGENTS: This is the exact module pattern you must follow:** ```typescript // modules/project.module.ts import { Module } from '@nestjs/common'; import { CrudModule } from '@concepta/nestjs-crud'; // REQUIRED import { TypeOrmModule } from '@nestjs/typeorm'; // REQUIRED (NOT TypeOrmExtModule) import { ProjectEntity } from '../entities/project.entity'; import { ProjectTypeOrmCrudAdapter } from '../adapters/project-typeorm-crud.adapter'; import { ProjectController, ProjectServiceProvider } from '../crud/project-crud.builder'; @Module({ imports: [ CrudModule.forRoot({}), // REQUIRED for CRUD functionality TypeOrmModule.forFeature([ProjectEntity]), // REQUIRED for repository injection ], providers: [ ProjectTypeOrmCrudAdapter, // The adapter with @Injectable ProjectServiceProvider, // From the builder.build() result ], controllers: [ ProjectController, // From the builder.build() result ], }) export class ProjectModule {} ``` #### 7) Wire up in Main App Module ```typescript // app.module.ts (add to imports) @Module({ imports: [ // ... other imports ProjectModule, // Your new CRUD module ], }) export class AppModule {} ``` #### Key Patterns for AI Agents **1. Adapter Pattern**: Always create a `EntityTypeOrmCrudAdapter` that extends `TypeOrmCrudAdapter<Entity>` (or any other adapter you may need) and injects `Repository<Entity>`. **2. Builder Pattern**: Use `ConfigurableCrudBuilder` and call `.build()` to get `ConfigurableServiceClass` and `ConfigurableControllerClass`. **3. Module Imports**: Always use: - `CrudModule.forRoot({})` - for CRUD functionality - `TypeOrmModule.forFeature([Entity])` - for repository injection - **NOT** `TypeOrmExtModule` - that's only for model services **4. Service Token**: Create a unique `Symbol` for each CRUD service token. **5. DTOs**: Always create separate DTOs for Create, Update, Response, and Paginated types. #### Gene