@flusys/nestjs-auth 0.1.0-beta.1 → 0.1.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. package/README.md +776 -0
  2. package/cjs/config/auth.constants.js +23 -0
  3. package/cjs/config/index.js +18 -0
  4. package/cjs/controllers/authentication.controller.js +251 -0
  5. package/cjs/controllers/branch.controller.js +59 -0
  6. package/cjs/controllers/company-selection.controller.js +168 -0
  7. package/cjs/controllers/company.controller.js +59 -0
  8. package/cjs/controllers/index.js +23 -0
  9. package/cjs/controllers/user-permission.controller.js +299 -0
  10. package/cjs/controllers/user.controller.js +177 -0
  11. package/cjs/docs/auth-swagger.config.js +409 -0
  12. package/cjs/docs/index.js +18 -0
  13. package/cjs/dtos/authentication.dto.js +676 -0
  14. package/cjs/dtos/company-branch.dto.js +199 -0
  15. package/cjs/dtos/company.dto.js +203 -0
  16. package/cjs/dtos/index.js +23 -0
  17. package/cjs/dtos/user-company-permission.dto.js +110 -0
  18. package/cjs/dtos/user-permission.dto.js +316 -0
  19. package/cjs/dtos/user.dto.js +333 -0
  20. package/cjs/entities/company-branch.entity.js +164 -0
  21. package/cjs/entities/company.entity.js +146 -0
  22. package/cjs/entities/index.js +62 -0
  23. package/cjs/entities/user-company-permission.entity.js +167 -0
  24. package/cjs/entities/user.entity.js +25 -0
  25. package/cjs/enums/index.js +18 -0
  26. package/cjs/enums/user-org-permission-type.enum.js +15 -0
  27. package/cjs/index.js +27 -187
  28. package/cjs/interceptors/clear-token.interceptor.js +86 -0
  29. package/cjs/interceptors/index.js +19 -0
  30. package/cjs/interceptors/set-token.interceptor.js +130 -0
  31. package/cjs/interfaces/auth-module-options.interface.js +4 -0
  32. package/cjs/interfaces/authentication.interface.js +7 -0
  33. package/cjs/interfaces/company-branch.interface.js +4 -0
  34. package/cjs/interfaces/company.interface.js +4 -0
  35. package/cjs/interfaces/index.js +22 -0
  36. package/cjs/interfaces/user.interface.js +4 -0
  37. package/cjs/modules/auth.module.js +270 -0
  38. package/cjs/modules/index.js +18 -0
  39. package/cjs/services/auth-config.service.js +134 -0
  40. package/cjs/services/auth-datasource.provider.js +218 -0
  41. package/cjs/services/authentication.service.js +901 -0
  42. package/cjs/services/branch.service.js +139 -0
  43. package/cjs/services/company-selection-session.service.js +102 -0
  44. package/cjs/services/company.service.js +122 -0
  45. package/cjs/services/index.js +25 -0
  46. package/cjs/services/user-permission.service.js +187 -0
  47. package/cjs/services/user.service.js +460 -0
  48. package/cjs/strategies/jwt.strategy.js +106 -0
  49. package/fesm/config/auth.constants.js +8 -0
  50. package/fesm/config/index.js +1 -0
  51. package/fesm/controllers/authentication.controller.js +241 -0
  52. package/fesm/controllers/branch.controller.js +49 -0
  53. package/fesm/controllers/company-selection.controller.js +158 -0
  54. package/fesm/controllers/company.controller.js +49 -0
  55. package/fesm/controllers/index.js +6 -0
  56. package/fesm/controllers/user-permission.controller.js +289 -0
  57. package/fesm/controllers/user.controller.js +167 -0
  58. package/fesm/docs/auth-swagger.config.js +406 -0
  59. package/fesm/docs/index.js +1 -0
  60. package/fesm/dtos/authentication.dto.js +701 -0
  61. package/fesm/dtos/company-branch.dto.js +184 -0
  62. package/fesm/dtos/company.dto.js +188 -0
  63. package/fesm/dtos/index.js +6 -0
  64. package/fesm/dtos/user-company-permission.dto.js +98 -0
  65. package/fesm/dtos/user-permission.dto.js +297 -0
  66. package/fesm/dtos/user.dto.js +314 -0
  67. package/fesm/entities/company-branch.entity.js +154 -0
  68. package/fesm/entities/company.entity.js +136 -0
  69. package/fesm/entities/index.js +23 -0
  70. package/fesm/entities/user-company-permission.entity.js +150 -0
  71. package/fesm/entities/user.entity.js +15 -0
  72. package/fesm/enums/index.js +1 -0
  73. package/fesm/enums/user-org-permission-type.enum.js +5 -0
  74. package/fesm/index.js +11 -188
  75. package/fesm/interceptors/clear-token.interceptor.js +76 -0
  76. package/fesm/interceptors/index.js +2 -0
  77. package/fesm/interceptors/set-token.interceptor.js +120 -0
  78. package/fesm/interfaces/auth-module-options.interface.js +3 -0
  79. package/fesm/interfaces/authentication.interface.js +7 -0
  80. package/fesm/interfaces/company-branch.interface.js +1 -0
  81. package/fesm/interfaces/company.interface.js +1 -0
  82. package/fesm/interfaces/index.js +5 -0
  83. package/fesm/interfaces/user.interface.js +1 -0
  84. package/fesm/modules/auth.module.js +260 -0
  85. package/fesm/modules/index.js +1 -0
  86. package/fesm/services/auth-config.service.js +124 -0
  87. package/fesm/services/auth-datasource.provider.js +167 -0
  88. package/fesm/services/authentication.service.js +850 -0
  89. package/fesm/services/branch.service.js +129 -0
  90. package/fesm/services/company-selection-session.service.js +92 -0
  91. package/fesm/services/company.service.js +112 -0
  92. package/fesm/services/index.js +8 -0
  93. package/fesm/services/user-permission.service.js +177 -0
  94. package/fesm/services/user.service.js +409 -0
  95. package/fesm/strategies/jwt.strategy.js +96 -0
  96. package/package.json +28 -23
  97. package/cjs/config-index.js +0 -1
  98. package/cjs/controllers-index.js +0 -86
  99. package/cjs/docs-index.js +0 -103
  100. package/cjs/dtos-index.js +0 -1
  101. package/cjs/entities-index.js +0 -1
  102. package/cjs/enums-index.js +0 -1
  103. package/cjs/interceptors-index.js +0 -1
  104. package/cjs/interfaces-index.js +0 -1
  105. package/cjs/modules-index.js +0 -86
  106. package/cjs/services-index.js +0 -16
  107. package/fesm/config-index.js +0 -1
  108. package/fesm/controllers-index.js +0 -86
  109. package/fesm/docs-index.js +0 -103
  110. package/fesm/dtos-index.js +0 -1
  111. package/fesm/entities-index.js +0 -1
  112. package/fesm/enums-index.js +0 -1
  113. package/fesm/interceptors-index.js +0 -1
  114. package/fesm/interfaces-index.js +0 -0
  115. package/fesm/modules-index.js +0 -86
  116. package/fesm/services-index.js +0 -16
package/README.md ADDED
@@ -0,0 +1,776 @@
1
+ # Authentication Package Guide
2
+
3
+ > **Package:** `@flusys/nestjs-auth`
4
+ > **Type:** Authentication system with JWT, multi-tenant, and company/branch support
5
+
6
+ This comprehensive guide covers the authentication package - flexible auth system for NestJS applications.
7
+
8
+ ## Table of Contents
9
+
10
+ - [Installation](#installation)
11
+ - [Quick Start](#quick-start)
12
+ - [Configuration](#configuration)
13
+ - [API Endpoints](#api-endpoints)
14
+ - [Architecture](#architecture)
15
+ - [Login Flows](#login-flows)
16
+ - [User Management](#user-management)
17
+ - [Environment Variables](#environment-variables)
18
+ - [Migrations](#migrations)
19
+ - [Configuration Modes](#configuration-modes)
20
+ - [IAM Integration](#iam-integration)
21
+
22
+ ---
23
+
24
+ ## Installation
25
+
26
+ ```bash
27
+ npm install @flusys/nestjs-auth @flusys/nestjs-shared @flusys/nestjs-core
28
+ ```
29
+
30
+ ## Quick Start
31
+
32
+ ### Basic Setup (Single Database, No Company Feature)
33
+
34
+ ```typescript
35
+ import { Module } from '@nestjs/common';
36
+ import { AuthModule } from '@flusys/nestjs-auth';
37
+
38
+ @Module({
39
+ imports: [
40
+ AuthModule.forRoot({
41
+ global: true,
42
+ includeController: true,
43
+ includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
44
+ bootstrapAppConfig: {
45
+ databaseMode: 'single',
46
+ enableCompanyFeature: false,
47
+ },
48
+ config: {
49
+ defaultDatabaseConfig: {
50
+ type: 'mysql',
51
+ host: 'localhost',
52
+ port: 3306,
53
+ username: 'root',
54
+ password: 'password',
55
+ database: 'myapp',
56
+ },
57
+ jwtSecret: process.env.JWT_SECRET,
58
+ jwtExpiration: '1h',
59
+ },
60
+ }),
61
+ ],
62
+ })
63
+ export class AppModule {}
64
+ ```
65
+
66
+ ### With Company Feature
67
+
68
+ ```typescript
69
+ AuthModule.forRoot({
70
+ global: true,
71
+ includeController: true,
72
+ includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
73
+ bootstrapAppConfig: {
74
+ databaseMode: 'single',
75
+ enableCompanyFeature: true, // Enable company/branch support
76
+ },
77
+ config: {
78
+ defaultDatabaseConfig: {
79
+ type: 'mysql',
80
+ host: 'localhost',
81
+ port: 3306,
82
+ username: 'root',
83
+ password: 'password',
84
+ database: 'myapp',
85
+ },
86
+ jwtSecret: process.env.JWT_SECRET,
87
+ refreshTokenSecret: process.env.REFRESH_TOKEN_SECRET,
88
+ refreshTokenExpiration: '7d',
89
+ refreshTokenCookieName: 'fsn_refresh_token',
90
+ },
91
+ })
92
+ ```
93
+
94
+ ### Multi-Tenant Mode
95
+
96
+ ```typescript
97
+ AuthModule.forRoot({
98
+ global: true,
99
+ includeController: true,
100
+ includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
101
+ bootstrapAppConfig: {
102
+ databaseMode: 'multi-tenant',
103
+ enableCompanyFeature: true,
104
+ },
105
+ config: {
106
+ tenantDefaultDatabaseConfig: {
107
+ type: 'mysql',
108
+ host: 'localhost',
109
+ port: 3306,
110
+ username: 'root',
111
+ password: 'password',
112
+ },
113
+ tenants: [
114
+ { id: 'tenant1', database: 'tenant1_db', name: 'Tenant 1', isActive: true },
115
+ { id: 'tenant2', database: 'tenant2_db', name: 'Tenant 2', isActive: true },
116
+ ],
117
+ jwtSecret: process.env.JWT_SECRET,
118
+ },
119
+ })
120
+ ```
121
+
122
+ ### Async Configuration
123
+
124
+ ```typescript
125
+ AuthModule.forRootAsync({
126
+ global: true,
127
+ includeController: true,
128
+ includeIAM: true, // Include IAM entities if using @flusys/nestjs-iam
129
+ bootstrapAppConfig: {
130
+ databaseMode: 'single',
131
+ enableCompanyFeature: false,
132
+ },
133
+ imports: [HttpModule],
134
+ useClass: AuthConfigFactory, // Implements AuthOptionsFactory
135
+ })
136
+ ```
137
+
138
+ ## Configuration
139
+
140
+ ### Configuration Options
141
+
142
+ ```typescript
143
+ interface AuthModuleOptions {
144
+ global?: boolean; // Make module global
145
+ includeController?: boolean; // Include default controllers
146
+ includeIAM?: boolean; // Include IAM entities in TypeORM.forRoot() (required for IAM module)
147
+ bootstrapAppConfig: {
148
+ databaseMode: 'single' | 'multi-tenant';
149
+ enableCompanyFeature: boolean; // Enable company/branch feature
150
+ };
151
+ config: {
152
+ defaultDatabaseConfig?: IDatabaseConfig;
153
+ tenantDefaultDatabaseConfig?: IDatabaseConfig;
154
+ tenants?: ITenantDatabaseConfig[];
155
+ jwtSecret: string;
156
+ jwtExpiration?: string;
157
+ refreshTokenSecret?: string;
158
+ refreshTokenExpiration?: string;
159
+ refreshTokenCookieName?: string; // Default: 'fsn_refresh_token'
160
+ };
161
+ }
162
+ ```
163
+
164
+ **Important Notes:**
165
+
166
+ 1. **includeIAM Flag**: When using `@flusys/nestjs-iam` in single-tenant mode, set `includeIAM: true` in Auth module options. This registers IAM entities with TypeORM during initialization. Not required in multi-tenant mode.
167
+
168
+ 2. **Module Separation**: Auth module handles authentication (login, JWT, user management). For authorization and permissions, use the separate `@flusys/nestjs-iam` package.
169
+
170
+ ## API Endpoints
171
+
172
+ ### Authentication Endpoints
173
+
174
+ | Endpoint | Method | Description |
175
+ |----------|--------|-------------|
176
+ | `/auth/login` | POST | Login with email/password |
177
+ | `/auth/register` | POST | Register new user |
178
+ | `/auth/refresh` | POST | Refresh access token |
179
+ | `/auth/logout` | POST | Logout (clears refresh token cookie) |
180
+ | `/auth/change-password` | POST | Change user password |
181
+ | `/auth/me` | GET | Get current user info |
182
+
183
+ #### GET /auth/me - Get Current User Info
184
+
185
+ **Purpose:** Retrieve current authenticated user information with optional company/branch context
186
+
187
+ **Authentication:** Requires valid access token (Bearer token in Authorization header)
188
+
189
+ **Response Behavior:**
190
+
191
+ When `enableCompanyFeature: false`:
192
+ ```json
193
+ {
194
+ "user": {
195
+ "id": "550e8400-e29b-41d4-a716-446655440000",
196
+ "email": "john@example.com",
197
+ "name": "John Doe",
198
+ "profilePictureId": null
199
+ }
200
+ }
201
+ ```
202
+
203
+ When `enableCompanyFeature: true`:
204
+ ```json
205
+ {
206
+ "user": {
207
+ "id": "550e8400-e29b-41d4-a716-446655440000",
208
+ "email": "john@example.com",
209
+ "name": "John Doe",
210
+ "profilePictureId": null
211
+ },
212
+ "company": {
213
+ "id": "550e8400-e29b-41d4-a716-446655440001",
214
+ "name": "Acme Corporation",
215
+ "slug": "acme-corp",
216
+ "logoId": null
217
+ },
218
+ "branch": {
219
+ "id": "550e8400-e29b-41d4-a716-446655440002",
220
+ "name": "Main Branch",
221
+ "slug": "main-branch",
222
+ "logoId": null
223
+ }
224
+ }
225
+ ```
226
+
227
+ **Key Characteristics:**
228
+ - Uses HTTP GET (not POST like other auth endpoints)
229
+ - Returns only user context data (no tokens)
230
+ - `company` and `branch` fields are automatically excluded from Swagger docs when company feature is disabled
231
+ - Used for session restoration on page refresh (frontend calls this after token refresh)
232
+
233
+ **Related Service:** `AuthenticationService.getUserInfo()` in `authentication.service.ts`
234
+
235
+ ### Company Selection Endpoints (when company feature enabled)
236
+
237
+ | Endpoint | Method | Description |
238
+ |----------|--------|-------------|
239
+ | `/auth/select` | POST | Select company and branch after login |
240
+ | `/auth/switch-company` | POST | Switch to different company/branch |
241
+ | `/auth/companies` | GET | Get user's available companies |
242
+ | `/auth/companies/:companyId/branches` | GET | Get branches for a company |
243
+
244
+ > **Note:** These endpoints are automatically hidden from Swagger when `enableCompanyFeature: false`
245
+
246
+ ### Swagger Schema Behavior
247
+
248
+ When `enableCompanyFeature: false`, the following are automatically hidden from Swagger:
249
+
250
+ **Excluded Tags:**
251
+ - Companies
252
+ - Branches
253
+ - User Permissions (NEW - v4.1.3+)
254
+ - Company Selection
255
+
256
+ **Excluded DTO Properties:**
257
+
258
+ | DTO | Hidden Fields |
259
+ |-----|---------------|
260
+ | `RegistrationDto` | `companySlug`, `branchSlug`, `newCompanyName`, `newCompanyPhone`, `newCompanyAddress` |
261
+ | `RegistrationResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
262
+ | `LoginResponseDto` | `company`, `branch`, `companyFeatureEnabled` |
263
+ | `MeResponseDto` | `company`, `branch` |
264
+
265
+ ### User Management Endpoints (`/administration/users`)
266
+
267
+ | Endpoint | Method | Description |
268
+ |----------|--------|-------------|
269
+ | `/users/insert` | POST | Create user |
270
+ | `/users/insert-many` | POST | Bulk create users |
271
+ | `/users/get/:id` | POST | Get user by ID |
272
+ | `/users/get-all` | POST | Get paginated user list |
273
+ | `/users/update` | POST | Update user |
274
+ | `/users/update-many` | POST | Bulk update users |
275
+ | `/users/delete` | POST | Delete/restore/permanent delete |
276
+ | `/users/profile` | POST | Update own profile |
277
+ | `/users/:id/verify-email` | POST | Mark email as verified |
278
+ | `/users/:id/verify-phone` | POST | Mark phone as verified |
279
+ | `/users/:id/status` | PUT | Update user active status |
280
+
281
+ ### User Permission Endpoints (NEW - when company feature enabled)
282
+
283
+ | Endpoint | Method | Description |
284
+ |----------|--------|-------------|
285
+ | `/administration/permissions/user-company/assign` | POST | Batch assign/revoke user-company permissions |
286
+ | `/administration/permissions/user-company` | GET | Get user's assigned companies |
287
+ | `/administration/permissions/user-branch/assign` | POST | Batch assign/revoke user-branch permissions |
288
+ | `/administration/permissions/user-branch` | GET | Get user's assigned branches |
289
+
290
+ **Example: Assign companies to user**
291
+ ```json
292
+ POST /administration/permissions/user-company/assign
293
+ {
294
+ "userId": "user-123",
295
+ "items": [
296
+ { "targetId": "company-1", "isAdd": true },
297
+ { "targetId": "company-2", "isAdd": true }
298
+ ]
299
+ }
300
+ ```
301
+
302
+ **Example: Revoke branch access**
303
+ ```json
304
+ POST /administration/permissions/user-branch/assign
305
+ {
306
+ "userId": "user-123",
307
+ "items": [
308
+ { "targetId": "branch-5", "isAdd": false }
309
+ ]
310
+ }
311
+ ```
312
+
313
+ > **Note:** These endpoints are only available when `enableCompanyFeature: true`
314
+
315
+ ### Company Management Endpoints (`/administration/company`) - When `enableCompanyFeature: true`
316
+
317
+ | Endpoint | Method | Description |
318
+ |----------|--------|-------------|
319
+ | `/company/insert` | POST | Create company |
320
+ | `/company/insert-many` | POST | Bulk create companies |
321
+ | `/company/get/:id` | POST | Get company by ID |
322
+ | `/company/get-all` | POST | Get paginated company list |
323
+ | `/company/update` | POST | Update company |
324
+ | `/company/update-many` | POST | Bulk update companies |
325
+ | `/company/delete` | POST | Delete/restore company |
326
+
327
+ ### Branch Management Endpoints (`/administration/branch`) - When `enableCompanyFeature: true`
328
+
329
+ | Endpoint | Method | Description |
330
+ |----------|--------|-------------|
331
+ | `/branch/insert` | POST | Create branch |
332
+ | `/branch/insert-many` | POST | Bulk create branches |
333
+ | `/branch/get/:id` | POST | Get branch by ID |
334
+ | `/branch/get-all` | POST | Get paginated branch list (auto-filtered by company) |
335
+ | `/branch/update` | POST | Update branch |
336
+ | `/branch/update-many` | POST | Bulk update branches |
337
+ | `/branch/delete` | POST | Delete/restore branch |
338
+
339
+ ## Architecture
340
+
341
+ ### Module Independence
342
+
343
+ ```
344
+ @flusys/nestjs-shared
345
+ ├── JwtAuthGuard (validates tokens using Passport)
346
+ └── PermissionGuard (checks permissions from cache)
347
+
348
+ @flusys/nestjs-auth
349
+ ├── AuthConfigService (JWT settings via AUTH_MODULE_OPTIONS)
350
+ ├── JwtStrategy (registers with Passport, validates users)
351
+ ├── AuthenticationService (generates tokens)
352
+ ├── User management
353
+ └── Company/Branch management (optional)
354
+ ```
355
+
356
+ **Key Points:**
357
+ - ✅ **JwtAuthGuard** in shared → All modules can use without depending on Auth
358
+ - ✅ **JwtStrategy** in auth → Registers JWT validation with Passport
359
+ - ✅ **AuthConfigService** in auth → Provides JWT settings (secret, expiration)
360
+ - ✅ **Token generation** handled by AuthenticationService
361
+ - ✅ **Token validation** uses JwtStrategy registered by Auth module
362
+
363
+ **Result:** IAM and Storage modules only depend on Shared, not Auth.
364
+
365
+ ### JwtStrategy Architecture
366
+
367
+ The `JwtStrategy` is a **singleton** (required by Passport), but needs to access the **request-scoped** `AuthDataSourceProvider`. This is solved using `ModuleRef`:
368
+
369
+ ```typescript
370
+ @Injectable()
371
+ export class JwtStrategy extends PassportStrategy(Strategy, 'jwt') {
372
+ constructor(
373
+ private readonly authConfig: AuthConfigService,
374
+ private readonly moduleRef: ModuleRef, // Used to resolve request-scoped providers
375
+ ) {
376
+ super({
377
+ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
378
+ secretOrKey: authConfig.getJwtSecret(),
379
+ passReqToCallback: true, // Pass request to validate method
380
+ });
381
+ }
382
+
383
+ async validate(_req: Request, payload: TokenPayload) {
384
+ // Resolve request-scoped AuthDataSourceProvider per request
385
+ const dataSourceProvider = await this.moduleRef.resolve(
386
+ AuthDataSourceProvider,
387
+ undefined,
388
+ { strict: false },
389
+ );
390
+
391
+ const userRepository = await dataSourceProvider.getRepository(User);
392
+ // ... validate user
393
+ }
394
+ }
395
+ ```
396
+
397
+ **Why this pattern:**
398
+ - ✅ Works for both **single-tenant** and **multi-tenant** modes
399
+ - ✅ `AuthDataSourceProvider` handles tenant detection from request headers
400
+ - ✅ Connection pools are cached internally (no reconnect per request)
401
+ - ✅ Singleton strategy + request-scoped provider = best of both worlds
402
+
403
+ ### Package Structure
404
+
405
+ ```
406
+ ┌─────────────────────────────────────────────────────────┐
407
+ │ @flusys/nestjs-auth v4.1.2+ │
408
+ │ │
409
+ │ ┌─────────────────────────────────────────────────┐ │
410
+ │ │ CORE AUTH (Always Loaded) │ │
411
+ │ │ │ │
412
+ │ │ ┌──────────┐ │ │
413
+ │ │ │ User │ │ │
414
+ │ │ └──────────┘ │ │
415
+ │ │ │ │
416
+ │ │ • User login/registration │ │
417
+ │ │ • Password hashing (bcrypt) │ │
418
+ │ │ • JWT token generation │ │
419
+ │ │ • JwtStrategy (Passport integration) │ │
420
+ │ └─────────────────────────────────────────────────┘ │
421
+ │ │
422
+ │ ┌─────────────────────────────────────────────────┐ │
423
+ │ │ COMPANY FEATURE (Optional - Flag Enabled) │ │
424
+ │ │ │ │
425
+ │ │ ┌─────────┐ ┌──────────────┐ ┌────────────┐ │ │
426
+ │ │ │ Company │ │CompanyBranch │ │UserCompany │ │ │
427
+ │ │ │ │ │ │ │Permission │ │ │
428
+ │ │ └─────────┘ └──────────────┘ └────────────┘ │ │
429
+ │ │ │ │
430
+ │ │ • Multi-company support │ │
431
+ │ │ • Hierarchical branches │ │
432
+ │ │ • User-company-branch assignments │ │
433
+ │ │ • Company-scoped permissions │ │
434
+ │ │ • Enhanced login with selection │ │
435
+ │ └─────────────────────────────────────────────────┘ │
436
+ └─────────────────────────────────────────────────────────┘
437
+ ```
438
+
439
+ ### Entity Relationships
440
+
441
+ #### Without Company Feature (Simple)
442
+
443
+ ```
444
+ ┌──────────────┐
445
+ │ User │
446
+ ├──────────────┤
447
+ │ id │
448
+ │ name │
449
+ │ email │◄────── Authentication
450
+ │ password │
451
+ │ phone │
452
+ └──────────────┘
453
+ ```
454
+
455
+ #### With Company Feature (Polymorphic Design)
456
+
457
+ ```
458
+ ┌──────────────┐ ┌───────────────────┐ ┌──────────────────┐
459
+ │ User │ │ UserCompany │ │ Company │
460
+ ├──────────────┤ │ Permission │ ├──────────────────┤
461
+ │ id │◄──────┤ userId │ │ id │
462
+ │ name │ │ permissionType │──────►│ name │
463
+ │ email │ │ targetId (UUID) │ │ slug │
464
+ │ password │ │ isActive │ │ isActive │
465
+ └──────────────┘ │ metadata │ └──────────────────┘
466
+ └───────────────────┘ │
467
+ │ │
468
+ permissionType: 'company'|'branch' ▼
469
+ targetId: company.id OR branch.id ┌──────────────────┐
470
+ │ │ CompanyBranch │
471
+ └──────────────────►├──────────────────┤
472
+ │ id │
473
+ │ name, companyId │
474
+ │ parentId ◄──┐ │
475
+ │ isActive │ │
476
+ └─────────────┴────┘
477
+ Hierarchical
478
+ ```
479
+
480
+ **Polymorphic Permission Design:**
481
+ - `permissionType`: Enum ('company' | 'branch') determines target type
482
+ - `targetId`: UUID of the company OR branch (single field, not both)
483
+ - One table handles both company and branch permissions
484
+
485
+ ### Services
486
+
487
+ | Service | Scope | Description |
488
+ |---------|-------|-------------|
489
+ | `AuthenticationService` | REQUEST | Login, register, token management, company selection |
490
+ | `UserService` | REQUEST | User CRUD, extends `RequestScopedApiService` |
491
+ | `CompanyService` | REQUEST | Company CRUD (when company feature enabled) |
492
+ | `BranchService` | REQUEST | Branch CRUD with company filtering |
493
+ | `UserPermissionService` | REQUEST | User-company/branch permission management |
494
+ | `AuthDataSourceProvider` | REQUEST | Dynamic datasource for single/multi-tenant |
495
+ | `AuthConfigService` | SINGLETON | JWT and module configuration access |
496
+
497
+ ## Login Flows
498
+
499
+ ### Simple Mode (Company Feature Disabled)
500
+
501
+ ```
502
+ POST /auth/login { email, password }
503
+
504
+
505
+ ┌─────────────────────────┐
506
+ │ AuthenticationService │
507
+ │ .login() │
508
+ └────────────┬────────────┘
509
+
510
+ │ 1. Verify credentials
511
+ │ 2. Generate JWT token
512
+
513
+ { accessToken, refreshToken, user }
514
+ ```
515
+
516
+ ### Company Mode - Auto-Select (Single Company/Branch)
517
+
518
+ ```
519
+ POST /auth/login { email, password }
520
+
521
+
522
+ ┌─────────────────────────┐
523
+ │ AuthenticationService │
524
+ │ .login() │
525
+ └────────────┬────────────┘
526
+
527
+ │ 1. Verify credentials
528
+ │ 2. Check UserCompanyPermission
529
+ │ 3. Found: 1 company, 1 branch → Auto-select
530
+
531
+ { accessToken, refreshToken, user, company, branch }
532
+ ```
533
+
534
+ ### Company Mode - Selection Required (Multiple Options)
535
+
536
+ ```
537
+ POST /auth/login { email, password }
538
+
539
+
540
+ ┌─────────────────────────┐
541
+ │ AuthenticationService │
542
+ │ .login() │
543
+ └────────────┬────────────┘
544
+
545
+ │ Found: Multiple companies/branches
546
+
547
+ { requiresSelection: true, sessionId, companies: [...] }
548
+
549
+ │ User selects company & branch
550
+
551
+ POST /auth/select { sessionId, companyId, branchId }
552
+
553
+
554
+ ┌─────────────────────────┐
555
+ │ AuthenticationService │
556
+ │ .select() │
557
+ └────────────┬────────────┘
558
+
559
+ │ 1. Validate access via UserCompanyPermission
560
+ │ 2. Generate token with company context
561
+
562
+ { accessToken, refreshToken, user, company, branch }
563
+ ```
564
+
565
+ ## User Management
566
+
567
+ ### User Active Status
568
+
569
+ The `PUT /users/:id/status` endpoint handles user active status based on company feature setting:
570
+
571
+ **When company feature is disabled:**
572
+ - Updates `User.isActive` directly
573
+ - Simple on/off for the user globally
574
+
575
+ **When company feature is enabled:**
576
+ - Updates `UserCompanyPermission.isActive` for the logged user's company
577
+ - User can be active in Company A but inactive in Company B
578
+ - `getAll` users filters by company permissions
579
+ - `isActive` in responses reflects company permission status
580
+
581
+ ### Registration Flow
582
+
583
+ **Simple registration:**
584
+ ```json
585
+ {
586
+ "name": "John Doe",
587
+ "email": "john@example.com",
588
+ "password": "password123"
589
+ }
590
+ ```
591
+
592
+ **Join existing company:**
593
+ ```json
594
+ {
595
+ "name": "John Doe",
596
+ "email": "john@example.com",
597
+ "password": "password123",
598
+ "companySlug": "acme-corp",
599
+ "branchSlug": "main-branch"
600
+ }
601
+ ```
602
+
603
+ **Create new company:**
604
+ ```json
605
+ {
606
+ "name": "John Doe",
607
+ "email": "john@example.com",
608
+ "password": "password123",
609
+ "newCompanyName": "My Company",
610
+ "newBranchName": "Headquarters"
611
+ }
612
+ ```
613
+
614
+ ### Hierarchical Branch Structure
615
+
616
+ ```
617
+ Company: "Acme Corporation"
618
+
619
+ ├── Main Office (Branch)
620
+ │ ├── Sales Department (Sub-Branch)
621
+ │ │ ├── Inside Sales (Sub-Sub-Branch)
622
+ │ │ └── Outside Sales
623
+ │ └── Support Department
624
+ │ ├── Tier 1 Support
625
+ │ └── Tier 2 Support
626
+
627
+ └── Regional Office (Branch)
628
+ ├── East Branch
629
+ └── West Branch
630
+ ```
631
+
632
+ ## Environment Variables
633
+
634
+ ```env
635
+ # Database
636
+ DB_HOST=localhost
637
+ DB_PORT=3306
638
+ DB_USERNAME=root
639
+ DB_PASSWORD=password
640
+ DB_DATABASE=myapp
641
+
642
+ # JWT
643
+ JWT_SECRET=your-secret-key-change-in-production
644
+ JWT_EXPIRATION=1h
645
+ REFRESH_TOKEN_SECRET=your-refresh-secret
646
+ REFRESH_TOKEN_EXPIRATION=7d
647
+ REFRESH_TOKEN_COOKIE_NAME=fsn_refresh_token
648
+ ```
649
+
650
+ ## Migrations
651
+
652
+ See [MIGRATION.md](./MIGRATION.md) for detailed migration commands.
653
+
654
+ ### Quick Commands
655
+
656
+ ```bash
657
+ # Generate migration
658
+ npm run migration:generate --name=InitialSetup
659
+
660
+ # Run migrations
661
+ npm run migration:run
662
+
663
+ # Check status
664
+ npm run migration:status
665
+ ```
666
+
667
+ ## Configuration Modes
668
+
669
+ ### Mode 1: Minimal (No Companies)
670
+
671
+ ```typescript
672
+ AuthModule.forRoot({
673
+ bootstrapAppConfig: {
674
+ enableCompanyFeature: false,
675
+ },
676
+ })
677
+
678
+ Entities: User
679
+ Tables: 1
680
+ Features: Basic login, registration
681
+ Size: Minimal
682
+ Performance: Fast
683
+ ```
684
+
685
+ ### Mode 2: Full Featured (With Companies)
686
+
687
+ ```typescript
688
+ AuthModule.forRoot({
689
+ includeIAM: true, // If using IAM module
690
+ bootstrapAppConfig: {
691
+ enableCompanyFeature: true,
692
+ },
693
+ })
694
+
695
+ Entities: User, Company, CompanyBranch, UserCompanyPermission
696
+ (+ IAM entities if includeIAM: true)
697
+ Tables: 4 (or more with IAM)
698
+ Features: Multi-tenant, hierarchical branches
699
+ Size: Complete
700
+ Performance: Scalable
701
+ ```
702
+
703
+ ## IAM Integration
704
+
705
+ ### Why includeIAM Flag?
706
+
707
+ When using `@flusys/nestjs-iam` with Auth module in **single-tenant mode**, TypeORM needs to know about all entities during initialization.
708
+
709
+ **The Problem:**
710
+ ```typescript
711
+ // Auth module calls TypeOrmModule.forRoot() with auth entities
712
+ TypeOrmModule.forRoot({ entities: [User, Company, ...] })
713
+
714
+ // IAM module tries to use forFeature() but entities not registered
715
+ TypeOrmModule.forFeature([Action, Role, Permission]) // ❌ Error!
716
+ ```
717
+
718
+ **The Solution:**
719
+ ```typescript
720
+ // Auth module dynamically includes IAM entities when includeIAM: true
721
+ AuthModule.forRoot({
722
+ includeIAM: true, // Auth module loads IAM entities
723
+ // ...
724
+ })
725
+
726
+ // Now TypeORM knows about all entities
727
+ TypeOrmModule.forRoot({
728
+ entities: [User, Company, Action, Role, Permission, ...]
729
+ }) // ✅ Works!
730
+ ```
731
+
732
+ **When to use:**
733
+ - ✅ Single-tenant mode + IAM module → `includeIAM: true`
734
+ - ❌ Multi-tenant mode → Not needed (each tenant has own connection)
735
+ - ❌ Not using IAM → `includeIAM: false` (default)
736
+
737
+ ### Architecture
738
+
739
+ ```
740
+ App Module
741
+ ├── AuthModule.forRoot({ includeIAM: true })
742
+ │ └── TypeOrmModule.forRoot([Auth entities + IAM entities])
743
+
744
+ └── IAMModule.forRoot()
745
+ └── TypeOrmModule.forFeature([IAM entities])
746
+ ```
747
+
748
+ Auth module is responsible for database connection, but dynamically includes IAM entities when requested. This maintains separation while solving the TypeORM registration requirement.
749
+
750
+ ---
751
+
752
+ ## Summary
753
+
754
+ The authentication package provides:
755
+
756
+ - **Flexibility** - Use simple or complex mode
757
+ - **Scalability** - Grows with your needs
758
+ - **Type Safety** - Full TypeScript support
759
+ - **Clean Design** - Clear separation of concerns
760
+ - **IAM Integration** - Seamless permission management
761
+ - **Well Organized** - Logical module structure
762
+ - **Performance** - Only load what you need
763
+
764
+ ---
765
+
766
+ **Last Updated:** 2026-02-09
767
+ **Version:** 4.1.6
768
+
769
+ **Recent Improvements:**
770
+ - 2026-02-09: Added explicit `@Inject()` decorators to all services for esbuild bundling compatibility
771
+ - 2026-02-09: All services now use REQUEST scope with DataSource Provider pattern
772
+ - 2026-02-07: Fixed entity diagram to show polymorphic design (permissionType/targetId)
773
+ - 2026-02-07: Updated service names to match actual implementation
774
+ - 2026-02-07: Simplified login flow diagrams
775
+ - 2026-01-15: Fixed JwtStrategy to use `ModuleRef` for request-scoped `AuthDataSourceProvider` resolution
776
+ - 2026-01-14: Added `UserPermissionController` for batch permission management