@flusys/nestjs-auth
v5.1.1
Published
Authentication and authorization infrastructure for NestJS applications
Maintainers
Readme
@flusys/nestjs-auth
Drop-in authentication for NestJS — JWT access/refresh tokens, bcrypt passwords, multi-company hierarchy, email verification, and pluggable email + user enrichment.
Installation
npm install @flusys/nestjs-auth @flusys/nestjs-shared @flusys/nestjs-core
npm install @nestjs/jwt @nestjs/passport passport-jwt bcrypt cookie-parser1. Module Registration
Sync (forRoot)
Mode 1: Single Database
import { AuthModule } from '@flusys/nestjs-auth';
AuthModule.forRoot({
global: true,
includeController: true,
bootstrapAppConfig: {
databaseMode: 'single',
enableCompanyFeature: false,
enableEmailVerification: true,
},
config: {
defaultDatabaseConfig: {
type: 'mysql',
host: process.env.DB_HOST,
port: Number(process.env.DB_PORT ?? 3306),
username: process.env.DB_USER,
password: process.env.DB_PASSWORD,
database: process.env.DB_NAME,
},
jwtSecret: process.env.JWT_SECRET,
jwtExpiration: '15m',
refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET,
refreshTokenExpiration: '7d',
// refreshTokenCookieName: 'fsn_refresh_token', // optional, this is the default
},
});Mode 2: Multi-Tenant
In multi-tenant mode you provide:
tenantDefaultDatabaseConfig— connection template applied to all tenants; individual tenants override only the fields that differtenants— per-tenant database config (ITenantDatabaseConfig[]); tenant resolved per request viax-tenant-idheader
defaultDatabaseConfig is not needed — tenantDefaultDatabaseConfig serves as the fallback for both template building and single-datasource resolution.
AuthModule.forRoot({
global: true,
includeController: true,
bootstrapAppConfig: {
databaseMode: 'multi-tenant',
enableCompanyFeature: true,
enableEmailVerification: true,
},
config: {
// Default template applied to every tenant unless overridden
tenantDefaultDatabaseConfig: {
type: 'mysql',
host: process.env.TENANT_DB_HOST,
port: Number(process.env.TENANT_DB_PORT ?? 3306),
username: process.env.TENANT_DB_USER,
password: process.env.TENANT_DB_PASSWORD,
database: process.env.TENANT_DB_NAME,
},
// Per-tenant overrides — only fields that differ from tenantDefaultDatabaseConfig
tenants: [
{
id: 'tenant-a',
name: 'Tenant A',
database: 'tenant_a_db',
enableCompanyFeature: true,
permissionMode: 'FULL',
},
{
id: 'tenant-b',
name: 'Tenant B',
host: 'tenant-b.db.example.com',
database: 'tenant_b_db',
enableCompanyFeature: false,
permissionMode: 'RBAC',
},
],
jwtSecret: process.env.JWT_SECRET,
jwtExpiration: '15m',
refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET,
refreshTokenExpiration: '7d',
},
});Async (forRootAsync)
Use when config comes from ConfigService or another async source:
import { ConfigModule, ConfigService } from '@nestjs/config';
import { AuthModule, ITenantDatabaseConfig } from '@flusys/nestjs-auth';
// Single database
AuthModule.forRootAsync({
global: true,
includeController: true,
imports: [ConfigModule],
bootstrapAppConfig: {
databaseMode: 'single',
enableCompanyFeature: true,
enableEmailVerification: true,
},why you cannot fix it,, need to need gap bellow LAB REPORT text
useFactory: (cfg: ConfigService) => ({
defaultDatabaseConfig: {
type: 'mysql',
host: cfg.get('DB_HOST'),
port: cfg.get<number>('DB_PORT'),
username: cfg.get('DB_USER'),
password: cfg.get('DB_PASSWORD'),
database: cfg.get('DB_NAME'),
},
jwtSecret: cfg.get('JWT_SECRET'),
jwtExpiration: cfg.get('JWT_EXPIRATION', '15m'),
refreshTokenSecret: cfg.get('REFRESH_TOKEN_SECRET'),
refreshTokenExpiration: cfg.get('REFRESH_TOKEN_EXPIRATION', '7d'),
}),
inject: [ConfigService],
providers: [authEmailProvider, userEnricherProvider],
});
// Multi-tenant
AuthModule.forRootAsync({
global: true,
includeController: true,
imports: [ConfigModule],
bootstrapAppConfig: {
databaseMode: 'multi-tenant',
enableCompanyFeature: true,
enableEmailVerification: true,
},
useFactory: (cfg: ConfigService) => ({
tenantDefaultDatabaseConfig: {
type: 'mysql',
host: cfg.get('TENANT_DB_HOST'),
port: cfg.get<number>('TENANT_DB_PORT'),
username: cfg.get('TENANT_DB_USER'),
password: cfg.get('TENANT_DB_PASSWORD'),
database: cfg.get('TENANT_DB_NAME'),
},
tenants: cfg.get<ITenantDatabaseConfig[]>('TENANTS'),
jwtSecret: cfg.get('JWT_SECRET'),
jwtExpiration: cfg.get('JWT_EXPIRATION', '15m'),
refreshTokenSecret: cfg.get('REFRESH_TOKEN_SECRET'),
refreshTokenExpiration: cfg.get('REFRESH_TOKEN_EXPIRATION', '7d'),
}),
inject: [ConfigService],
providers: [authEmailProvider, userEnricherProvider],
});2. Entity Registration
Pass auth entities to your TypeORM setup using the static helper:
import { getEntitiesByConfig } from '@flusys/nestjs-auth/entities';
TypeOrmModule.forRoot({
entities: [
...getEntitiesByConfig({
enableCompanyFeature: true,
enableEmailVerification: false,
}),
],
});Keep the flags here in sync with those passed to forRoot/forRootAsync.
3. Pluggable Email — AUTH_EMAIL_PROVIDER
Auth never imports nestjs-email. Connect them via a factory provider:
import { AUTH_EMAIL_PROVIDER, IAuthEmailProvider } from '@flusys/nestjs-auth';
import { EmailSendService } from '@flusys/nestjs-email';
export const authEmailProvider = {
provide: AUTH_EMAIL_PROVIDER,
useFactory: (emailSendService: EmailSendService): IAuthEmailProvider => ({
sendPasswordResetEmail: async (email, token, resetUrl) => {
await emailSendService.sendTemplateEmail({
templateSlug: 'password-reset',
to: email,
variables: { resetUrl, token },
});
},
sendVerificationEmail: async (email, token, verifyUrl) => {
await emailSendService.sendTemplateEmail({
templateSlug: 'email-verification',
to: email,
variables: { verifyUrl, token },
});
},
// sendWelcomeEmail is optional
}),
inject: [EmailSendService],
};
// Register:
AuthModule.forRootAsync({ ..., providers: [authEmailProvider] });When AUTH_EMAIL_PROVIDER is not provided, registration still works but emails are silently skipped.
4. User Enrichment — USER_ENRICHER
Extend user list and profile responses without creating circular imports. All methods are optional — implement only what you need.
| Method | When called | Purpose |
| ---------------------------- | ---------------------- | ---------------------------------------------- |
| enrichListItems | After user list fetch | Attach computed data to list items |
| getProfileExtras | POST /users/get/:id | Add roles, actions, sections to profile |
| updateProfileExtras | Profile update (in tx) | Persist extra profile fields |
| validateProfileExtras | Before profile update | Validate extra fields |
| getProfileSections | Profile form init | Return multi-step section definitions |
| getProfileSectionData | Section fetch | Return section-specific data |
| updateProfileSection | Section save (in tx) | Persist section data |
| handleSectionFileUpload | File upload (in tx) | Handle file attach to section |
| handleSectionFileDelete | File delete (in tx) | Handle file removal from section |
| calculateProfileCompletion | Profile fetch | Return 0–100 completion score |
| onUserCreated | Registration (in tx) | Hook after user row created; throw to rollback |
import { USER_ENRICHER, IUserEnricher, IProfileExtras } from '@flusys/nestjs-auth';
import { ILoggedUserInfo } from '@flusys/nestjs-shared/interfaces';
import { Injectable, Inject } from '@nestjs/common';
@Injectable()
export class IamUserEnricher implements IUserEnricher {
constructor(@Inject(PermissionService) private readonly permissionService: PermissionService) {}
async getProfileExtras(userId: string, user: ILoggedUserInfo): Promise<IProfileExtras> {
const roles = await this.permissionService.getRolesForUser(userId);
return { roles };
}
}
export const userEnricherProvider = {
provide: USER_ENRICHER,
useClass: IamUserEnricher,
};
// Register:
AuthModule.forRootAsync({ ..., providers: [userEnricherProvider] });License
MIT © FLUSYS
