@nauth-toolkit/nestjs 0.1.13 → 0.1.17

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 (63) hide show
  1. package/dist/auth.module.d.ts +48 -0
  2. package/dist/auth.module.d.ts.map +1 -1
  3. package/dist/auth.module.js +161 -19
  4. package/dist/auth.module.js.map +1 -1
  5. package/dist/decorators/client-info.decorator.d.ts +39 -0
  6. package/dist/decorators/client-info.decorator.d.ts.map +1 -1
  7. package/dist/decorators/client-info.decorator.js +41 -0
  8. package/dist/decorators/client-info.decorator.js.map +1 -1
  9. package/dist/decorators/current-user.decorator.d.ts +6 -0
  10. package/dist/decorators/current-user.decorator.d.ts.map +1 -1
  11. package/dist/decorators/current-user.decorator.js +6 -0
  12. package/dist/decorators/current-user.decorator.js.map +1 -1
  13. package/dist/decorators/public.decorator.d.ts +7 -0
  14. package/dist/decorators/public.decorator.d.ts.map +1 -1
  15. package/dist/decorators/public.decorator.js +7 -0
  16. package/dist/decorators/public.decorator.js.map +1 -1
  17. package/dist/decorators/token-delivery.decorator.d.ts +20 -0
  18. package/dist/decorators/token-delivery.decorator.d.ts.map +1 -1
  19. package/dist/dto/index.d.ts +9 -0
  20. package/dist/dto/index.d.ts.map +1 -1
  21. package/dist/dto/index.js +10 -0
  22. package/dist/dto/index.js.map +1 -1
  23. package/dist/factories/storage-adapter.factory.d.ts +107 -0
  24. package/dist/factories/storage-adapter.factory.d.ts.map +1 -1
  25. package/dist/factories/storage-adapter.factory.js +129 -0
  26. package/dist/factories/storage-adapter.factory.js.map +1 -1
  27. package/dist/filters/nauth-http-exception.filter.d.ts +80 -0
  28. package/dist/filters/nauth-http-exception.filter.d.ts.map +1 -1
  29. package/dist/filters/nauth-http-exception.filter.js +96 -0
  30. package/dist/filters/nauth-http-exception.filter.js.map +1 -1
  31. package/dist/guards/auth.guard.d.ts +26 -0
  32. package/dist/guards/auth.guard.d.ts.map +1 -1
  33. package/dist/guards/auth.guard.js +44 -0
  34. package/dist/guards/auth.guard.js.map +1 -1
  35. package/dist/guards/csrf.guard.d.ts +21 -0
  36. package/dist/guards/csrf.guard.d.ts.map +1 -1
  37. package/dist/guards/csrf.guard.js +30 -1
  38. package/dist/guards/csrf.guard.js.map +1 -1
  39. package/dist/index.d.ts +34 -0
  40. package/dist/index.d.ts.map +1 -1
  41. package/dist/index.js +46 -0
  42. package/dist/index.js.map +1 -1
  43. package/dist/interceptors/client-info.interceptor.d.ts +37 -0
  44. package/dist/interceptors/client-info.interceptor.d.ts.map +1 -1
  45. package/dist/interceptors/client-info.interceptor.js +89 -1
  46. package/dist/interceptors/client-info.interceptor.js.map +1 -1
  47. package/dist/interceptors/cookie-token.interceptor.d.ts +16 -0
  48. package/dist/interceptors/cookie-token.interceptor.d.ts.map +1 -1
  49. package/dist/interceptors/cookie-token.interceptor.js +80 -16
  50. package/dist/interceptors/cookie-token.interceptor.js.map +1 -1
  51. package/dist/providers/nestjs-logger.adapter.d.ts +96 -0
  52. package/dist/providers/nestjs-logger.adapter.d.ts.map +1 -1
  53. package/dist/providers/nestjs-logger.adapter.js +105 -1
  54. package/dist/providers/nestjs-logger.adapter.js.map +1 -1
  55. package/dist/services/csrf.service.d.ts +61 -0
  56. package/dist/services/csrf.service.d.ts.map +1 -1
  57. package/dist/services/csrf.service.js +62 -1
  58. package/dist/services/csrf.service.js.map +1 -1
  59. package/dist/services/migrations-bootstrap.service.d.ts +6 -0
  60. package/dist/services/migrations-bootstrap.service.d.ts.map +1 -1
  61. package/dist/services/migrations-bootstrap.service.js +6 -0
  62. package/dist/services/migrations-bootstrap.service.js.map +1 -1
  63. package/package.json +14 -2
@@ -1,10 +1,58 @@
1
1
  import { DynamicModule } from '@nestjs/common';
2
2
  import { NAuthConfig } from '@nauth-toolkit/core';
3
+ /**
4
+ * Extended NAuth Configuration (includes optional entities)
5
+ *
6
+ * Entities are optional - AuthModule will auto-discover them from DataSource if not provided.
7
+ * Consumer apps should NOT need to configure entities if they're already in TypeORM.forRoot().
8
+ */
3
9
  export interface NAuthModuleConfig extends NAuthConfig {
10
+ /**
11
+ * Optional: TypeORM entities from database package
12
+ *
13
+ * **Note:** Entities are optional if already registered in `TypeOrmModule.forRoot()`.
14
+ * AuthModule will auto-discover entities from DataSource metadata.
15
+ *
16
+ * Only provide if you're not using `TypeOrmModule.forRoot()` with entities.
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * // PREFERRED - Auto-discovery (entities in TypeORM.forRoot())
21
+ * TypeOrmModule.forRoot({ entities: getNAuthEntities(), ... })
22
+ * AuthModule.forRoot({ jwt: {...} }) // entities not needed
23
+ *
24
+ * // ALSO VALID - Explicit entities (if not using TypeORM.forRoot())
25
+ * AuthModule.forRoot({ entities: getNAuthEntities(), jwt: {...} })
26
+ * ```
27
+ */
4
28
  entities?: Function[];
5
29
  }
30
+ /**
31
+ * Main Authentication Module (v2.0 - Modular)
32
+ *
33
+ * Core provides base authentication services.
34
+ * Features like MFA, social auth, phone verification are in separate packages.
35
+ */
6
36
  export declare class AuthModule {
37
+ /**
38
+ * Configure module with static configuration
39
+ *
40
+ * @param config - NAuthModuleConfig with entities from database package
41
+ */
7
42
  static forRoot(config: NAuthModuleConfig): DynamicModule;
43
+ /**
44
+ * Validate configuration using Zod schema
45
+ *
46
+ * Validates all configuration sections and cross-dependencies:
47
+ * - Email/phone verification requires respective providers
48
+ * - MFA enforcement modes require specific configurations
49
+ * - Social providers require credentials when enabled
50
+ * - JWT algorithm requires appropriate keys
51
+ * - MaxMind geolocation requires credentials for downloads
52
+ *
53
+ * @param config - Configuration to validate
54
+ * @throws {NAuthException} If validation fails with detailed error messages
55
+ */
8
56
  private static validateConfig;
9
57
  }
10
58
  //# sourceMappingURL=auth.module.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAK/D,OAAO,EAYL,WAAW,EAoBZ,MAAM,qBAAqB,CAAC;AAsD7B,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IAmBpD,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAQD,qBAEa,UAAU;IAMrB,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa;IAqqCxD,OAAO,CAAC,MAAM,CAAC,cAAc;CAuB9B"}
1
+ {"version":3,"file":"auth.module.d.ts","sourceRoot":"","sources":["../src/auth.module.ts"],"names":[],"mappings":"AAAA,OAAO,EAAU,aAAa,EAAU,MAAM,gBAAgB,CAAC;AAK/D,OAAO,EAYL,WAAW,EAoBZ,MAAM,qBAAqB,CAAC;AAgD7B;;;;;GAKG;AACH,MAAM,WAAW,iBAAkB,SAAQ,WAAW;IACpD;;;;;;;;;;;;;;;;;OAiBG;IACH,QAAQ,CAAC,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED;;;;;GAKG;AACH,qBAEa,UAAU;IACrB;;;;OAIG;IACH,MAAM,CAAC,OAAO,CAAC,MAAM,EAAE,iBAAiB,GAAG,aAAa;IAwpCxD;;;;;;;;;;;;OAYG;IACH,OAAO,CAAC,MAAM,CAAC,cAAc;CAuB9B"}
@@ -45,7 +45,9 @@ const common_1 = require("@nestjs/common");
45
45
  const core_1 = require("@nestjs/core");
46
46
  const typeorm_1 = require("@nestjs/typeorm");
47
47
  const typeorm_2 = require("typeorm");
48
+ // Public API imports
48
49
  const core_2 = require("@nauth-toolkit/core");
50
+ // Internal API imports (for framework adapter use only)
49
51
  const internal_1 = require("@nauth-toolkit/core/internal");
50
52
  const client_info_interceptor_1 = require("./interceptors/client-info.interceptor");
51
53
  const cookie_token_interceptor_1 = require("./interceptors/cookie-token.interceptor");
@@ -53,27 +55,48 @@ const auth_guard_1 = require("./guards/auth.guard");
53
55
  const csrf_guard_1 = require("./guards/csrf.guard");
54
56
  const csrf_service_1 = require("./services/csrf.service");
55
57
  const migrations_bootstrap_service_1 = require("./services/migrations-bootstrap.service");
58
+ /**
59
+ * Main Authentication Module (v2.0 - Modular)
60
+ *
61
+ * Core provides base authentication services.
62
+ * Features like MFA, social auth, phone verification are in separate packages.
63
+ */
56
64
  let AuthModule = AuthModule_1 = class AuthModule {
65
+ /**
66
+ * Configure module with static configuration
67
+ *
68
+ * @param config - NAuthModuleConfig with entities from database package
69
+ */
57
70
  static forRoot(config) {
71
+ // Validate configuration
58
72
  this.validateConfig(config);
73
+ // Initialize logger wrapper (silent by default if no logger provided)
59
74
  const nauthLogger = new core_2.NAuthLogger(config.logger);
75
+ // Log initialization
60
76
  if (nauthLogger.isEnabled()) {
61
77
  nauthLogger.log('Initializing nauth-toolkit...');
62
78
  nauthLogger.debug(`Table prefix: ${config.tablePrefix || 'nauth_'}`);
63
79
  nauthLogger.debug(`JWT algorithm: ${config.jwt.algorithm || 'HS256'}`);
64
80
  }
81
+ // Storage adapter will be initialized in useFactory below
82
+ // Use config.storageAdapter if provided, otherwise default to MemoryStorageAdapter
83
+ // Determine entities - use provided or discover from DataSource
65
84
  const entities = config.entities || [];
66
85
  return {
67
86
  module: AuthModule_1,
68
87
  imports: [
88
+ // TypeORM entities - only if provided (otherwise entities from forRoot() are used)
69
89
  ...(entities.length > 0 ? [typeorm_1.TypeOrmModule.forFeature(entities)] : []),
70
90
  ],
71
91
  providers: [
92
+ // Auto-run nauth-toolkit migrations on startup (no consumer burden)
72
93
  migrations_bootstrap_service_1.nauthMigrationsBootstrapProvider,
94
+ // Global interceptor for automatic client info extraction
73
95
  {
74
96
  provide: core_1.APP_INTERCEPTOR,
75
97
  useClass: client_info_interceptor_1.ClientInfoInterceptor,
76
98
  },
99
+ // Global interceptor for cookie token delivery (no-op in JSON mode)
77
100
  {
78
101
  provide: core_1.APP_INTERCEPTOR,
79
102
  useFactory: (config, jwtService, reflector, csrfService) => {
@@ -83,9 +106,10 @@ let AuthModule = AuthModule_1 = class AuthModule {
83
106
  'NAUTH_CONFIG',
84
107
  internal_1.JwtService,
85
108
  core_1.Reflector,
86
- { token: csrf_service_1.CsrfService, optional: true },
109
+ { token: csrf_service_1.CsrfService, optional: true }, // Optional - only available when CSRF is enabled
87
110
  ],
88
111
  },
112
+ // CSRF Service (always provided, but only used when tokenDelivery.method === 'cookies' or 'hybrid')
89
113
  {
90
114
  provide: csrf_service_1.CsrfService,
91
115
  useFactory: (config) => {
@@ -93,6 +117,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
93
117
  },
94
118
  inject: ['NAUTH_CONFIG'],
95
119
  },
120
+ // CSRF Guard (conditionally applied when using cookie-based token delivery)
96
121
  ...(config.tokenDelivery?.method === 'cookies' || config.tokenDelivery?.method === 'hybrid'
97
122
  ? [
98
123
  {
@@ -101,23 +126,34 @@ let AuthModule = AuthModule_1 = class AuthModule {
101
126
  },
102
127
  ]
103
128
  : []),
129
+ // Configuration
104
130
  {
105
131
  provide: 'NAUTH_CONFIG',
106
132
  useValue: config,
107
133
  },
134
+ // Logger
108
135
  {
109
136
  provide: 'NAUTH_LOGGER',
110
137
  useValue: nauthLogger,
111
138
  },
139
+ // Storage adapter - use config or default to DatabaseStorageAdapter if repositories available
140
+ // WARNING: PRODUCTION REQUIREMENT - MemoryStorageAdapter is NOT safe for production
141
+ // - Data lost on server restart
142
+ // - NOT shared across multiple server instances
143
+ // - Rate limiting per-container (not global in multi-container setups)
144
+ // - Token reuse detection fails in multi-server deployments
112
145
  {
113
146
  provide: 'STORAGE_ADAPTER',
114
147
  useFactory: async (config, logger, rateLimitRepo, storageLockRepo) => {
148
+ // If storage adapter is explicitly provided, use it
115
149
  if (config.storageAdapter) {
116
150
  const adapter = config.storageAdapter;
151
+ // Inject logger into adapter if it supports setLogger (for factory-created adapters)
117
152
  if (adapter &&
118
153
  typeof adapter.setLogger === 'function') {
119
154
  adapter.setLogger(logger);
120
155
  }
156
+ // Inject repositories into DatabaseStorageAdapter if it supports setRepositories
121
157
  if (adapter &&
122
158
  typeof adapter.setRepositories === 'function') {
123
159
  if (rateLimitRepo && storageLockRepo) {
@@ -127,8 +163,11 @@ let AuthModule = AuthModule_1 = class AuthModule {
127
163
  await adapter.initialize();
128
164
  return adapter;
129
165
  }
166
+ // No storage adapter provided - try to use DatabaseStorageAdapter if repositories available
130
167
  if (rateLimitRepo && storageLockRepo) {
168
+ // Default to DatabaseStorageAdapter when repositories are available (most apps have a database)
131
169
  try {
170
+ // Lazy import to avoid bundling if not used
132
171
  const { DatabaseStorageAdapter } = await Promise.resolve().then(() => __importStar(require('@nauth-toolkit/storage-database')));
133
172
  const adapter = new DatabaseStorageAdapter(null, null, logger);
134
173
  adapter.setRepositories(rateLimitRepo, storageLockRepo);
@@ -138,9 +177,11 @@ let AuthModule = AuthModule_1 = class AuthModule {
138
177
  return adapter;
139
178
  }
140
179
  catch (error) {
180
+ // If DatabaseStorageAdapter import fails, fall through to error
141
181
  logger?.error?.('Failed to create DatabaseStorageAdapter. Please explicitly configure storageAdapter in your config.', { error });
142
182
  }
143
183
  }
184
+ // No storage adapter provided and no repositories available - REQUIRE explicit configuration
144
185
  throw new Error('Storage adapter is REQUIRED for production deployments. ' +
145
186
  'MemoryStorageAdapter is NOT safe for production (data lost on restart, not shared across instances). ' +
146
187
  'Please configure storageAdapter in your NAuthConfig:\n\n' +
@@ -156,51 +197,65 @@ let AuthModule = AuthModule_1 = class AuthModule {
156
197
  },
157
198
  inject: ['NAUTH_CONFIG', 'NAUTH_LOGGER', 'RateLimitRepository', 'StorageLockRepository'],
158
199
  },
200
+ // Rate Limit Repository (optional - only needed for DatabaseStorageAdapter)
159
201
  {
160
202
  provide: 'RateLimitRepository',
161
203
  useFactory: (dataSource) => {
204
+ // Try to find entity from config first
162
205
  const entityFromConfig = entities.find((e) => e.name === 'RateLimit');
163
206
  if (entityFromConfig) {
164
207
  return dataSource.getRepository(entityFromConfig);
165
208
  }
209
+ // Try to find by table name in DataSource metadata
166
210
  const metadata = dataSource.entityMetadatas.find((m) => m.tableName === 'nauth_rate_limits');
167
211
  if (metadata) {
168
212
  return dataSource.getRepository(metadata.target);
169
213
  }
214
+ // Try to find by class name in metadata
170
215
  const metadataByName = dataSource.entityMetadatas.find((m) => typeof m.target === 'function' && m.target.name === 'RateLimit');
171
216
  if (metadataByName && typeof metadataByName.target === 'function') {
172
217
  return dataSource.getRepository(metadataByName.target);
173
218
  }
219
+ // Return null if not found (storage adapter might not be DatabaseStorageAdapter)
174
220
  return null;
175
221
  },
176
222
  inject: [typeorm_2.DataSource],
177
223
  },
224
+ // Storage Lock Repository (optional - only needed for DatabaseStorageAdapter)
178
225
  {
179
226
  provide: 'StorageLockRepository',
180
227
  useFactory: (dataSource) => {
228
+ // Try to find entity from config first
181
229
  const entityFromConfig = entities.find((e) => e.name === 'StorageLock');
182
230
  if (entityFromConfig) {
183
231
  return dataSource.getRepository(entityFromConfig);
184
232
  }
233
+ // Try to find by table name in DataSource metadata
185
234
  const metadata = dataSource.entityMetadatas.find((m) => m.tableName === 'nauth_storage_locks');
186
235
  if (metadata) {
187
236
  return dataSource.getRepository(metadata.target);
188
237
  }
238
+ // Try to find by class name in metadata
189
239
  const metadataByName = dataSource.entityMetadatas.find((m) => typeof m.target === 'function' && m.target.name === 'StorageLock');
190
240
  if (metadataByName && typeof metadataByName.target === 'function') {
191
241
  return dataSource.getRepository(metadataByName.target);
192
242
  }
243
+ // Return null if not found (storage adapter might not be DatabaseStorageAdapter)
193
244
  return null;
194
245
  },
195
246
  inject: [typeorm_2.DataSource],
196
247
  },
248
+ // Repository Tokens - discover entities from DataSource metadata
249
+ // This allows entities to be auto-discovered if registered in TypeORM.forRoot()
197
250
  {
198
251
  provide: 'UserRepository',
199
252
  useFactory: (dataSource) => {
253
+ // Try to find entity from provided config first
200
254
  const entityFromConfig = entities.find((e) => e.name === 'User');
201
255
  if (entityFromConfig) {
202
256
  return dataSource.getRepository(entityFromConfig);
203
257
  }
258
+ // Otherwise, find by table name from DataSource metadata
204
259
  const metadata = dataSource.entityMetadatas.find((m) => m.tableName === 'nauth_users');
205
260
  if (!metadata) {
206
261
  throw new core_2.NAuthException(core_2.AuthErrorCode.VALIDATION_FAILED, 'User entity not found. Register entities in TypeORM.forRoot() or provide in config.entities');
@@ -323,12 +378,14 @@ let AuthModule = AuthModule_1 = class AuthModule {
323
378
  }
324
379
  const metadata = dataSource.entityMetadatas.find((m) => m.tableName === 'nauth_trusted_devices');
325
380
  if (!metadata) {
381
+ // Return null if not found (rememberDevice might be disabled)
326
382
  return null;
327
383
  }
328
384
  return dataSource.getRepository(metadata.target);
329
385
  },
330
386
  inject: [typeorm_2.DataSource],
331
387
  },
388
+ // Services
332
389
  {
333
390
  provide: internal_1.PasswordService,
334
391
  useFactory: () => {
@@ -358,7 +415,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
358
415
  core_2.ClientInfoService,
359
416
  'NAUTH_CONFIG',
360
417
  'NAUTH_LOGGER',
361
- { token: internal_1.AuthAuditService, optional: true },
418
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
362
419
  ],
363
420
  },
364
421
  {
@@ -370,9 +427,10 @@ let AuthModule = AuthModule_1 = class AuthModule {
370
427
  'ChallengeSessionRepository',
371
428
  core_2.ClientInfoService,
372
429
  'NAUTH_LOGGER',
373
- { token: internal_1.AuthAuditService, optional: true },
430
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
374
431
  ],
375
432
  },
433
+ // AuthFlowContextBuilder - builds context with pre-computed values
376
434
  {
377
435
  provide: internal_1.AuthFlowContextBuilder,
378
436
  useFactory: (trustedDeviceService, adaptiveMFADecisionService, clientInfoService, logger) => {
@@ -385,6 +443,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
385
443
  { token: 'NAUTH_LOGGER', optional: true },
386
444
  ],
387
445
  },
446
+ // AuthFlowStateMachineService - evaluates authentication flow states
388
447
  {
389
448
  provide: internal_1.AuthFlowStateMachineService,
390
449
  useFactory: (contextBuilder, logger) => {
@@ -412,7 +471,8 @@ let AuthModule = AuthModule_1 = class AuthModule {
412
471
  },
413
472
  {
414
473
  provide: core_2.AuthService,
415
- useFactory: (userRepository, loginAttemptRepository, passwordService, jwtService, sessionService, challengeService, challengeHelper, emailVerificationService, clientInfoService, accountLockoutStorage, nauthConfig, logger, auditService, phoneVerificationService, mfaService, mfaDeviceRepository, trustedDeviceService, passwordResetService) => {
474
+ useFactory: (userRepository, loginAttemptRepository, passwordService, jwtService, sessionService, challengeService, challengeHelper, emailVerificationService, clientInfoService, accountLockoutStorage, nauthConfig, logger, auditService, // Optional - only available when auditLogs.enabled is true
475
+ phoneVerificationService, mfaService, mfaDeviceRepository, trustedDeviceService, passwordResetService) => {
416
476
  return new core_2.AuthService(userRepository, loginAttemptRepository, passwordService, jwtService, sessionService, challengeService, challengeHelper, emailVerificationService, clientInfoService, accountLockoutStorage, nauthConfig, logger, auditService, phoneVerificationService, mfaService, mfaDeviceRepository, trustedDeviceService, passwordResetService);
417
477
  },
418
478
  inject: [
@@ -428,9 +488,9 @@ let AuthModule = AuthModule_1 = class AuthModule {
428
488
  core_2.AccountLockoutStorageService,
429
489
  'NAUTH_CONFIG',
430
490
  'NAUTH_LOGGER',
431
- { token: internal_1.AuthAuditService, optional: true },
491
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
432
492
  { token: core_2.PhoneVerificationService, optional: true },
433
- { token: core_2.MFAService, optional: true },
493
+ { token: core_2.MFAService, optional: true }, // No circular dependency - MFAService no longer depends on AuthService
434
494
  { token: 'MFADeviceRepository', optional: true },
435
495
  { token: internal_1.TrustedDeviceService, optional: true },
436
496
  { token: internal_1.PasswordResetService, optional: true },
@@ -440,17 +500,25 @@ let AuthModule = AuthModule_1 = class AuthModule {
440
500
  provide: internal_1.TrustedDeviceService,
441
501
  useFactory: (config, logger, trustedDeviceRepository) => {
442
502
  if (!trustedDeviceRepository) {
503
+ // Return null if repository not available (rememberDevice disabled)
443
504
  return null;
444
505
  }
445
506
  return new internal_1.TrustedDeviceService(config, logger, trustedDeviceRepository);
446
507
  },
447
508
  inject: ['NAUTH_CONFIG', 'NAUTH_LOGGER', { token: 'TrustedDeviceRepository', optional: true }],
448
509
  },
510
+ // Social Auth State Store - shared Map for CSRF state validation across all providers
449
511
  {
450
512
  provide: 'SOCIAL_AUTH_STATE_STORE',
451
513
  useValue: new Map(),
452
514
  },
515
+ // Social Provider Registry - internal registry for social providers
516
+ // Provider modules (GoogleSocialAuthModule, AppleSocialAuthModule, etc.)
517
+ // will automatically register themselves with this registry via OnModuleInit
453
518
  internal_1.SocialProviderRegistry,
519
+ // MFA Service - registry for auto-registered MFA providers
520
+ // Provider modules (TOTPMFAModule, SMSMFAModule, PasskeyMFAModule, etc.)
521
+ // will automatically register themselves with this service via OnModuleInit
454
522
  {
455
523
  provide: core_2.MFAService,
456
524
  useFactory: (mfaDeviceRepository, userRepository, challengeService, nauthConfig, logger, auditService, clientInfoService) => {
@@ -477,12 +545,21 @@ let AuthModule = AuthModule_1 = class AuthModule {
477
545
  'SocialAccountRepository',
478
546
  core_2.AuthService,
479
547
  'NAUTH_LOGGER',
480
- { token: internal_1.AuthAuditService, optional: true },
548
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
481
549
  ],
482
550
  },
483
551
  core_2.ClientInfoService,
552
+ // Conditionally provide AuthAuditService based on config.auditLogs.enabled
553
+ // Default to enabled if not specified (backward compatibility)
554
+ //
555
+ // Architecture:
556
+ // - Create ONE instance of InternalAuthAuditService
557
+ // - Export as InternalAuthAuditService for internal packages (social auth, MFA providers)
558
+ // - Alias as AuthAuditService for consumer apps (public API with fetch methods only)
559
+ // This ensures a single instance handles both recording and fetching
484
560
  ...(config.auditLogs?.enabled !== false
485
561
  ? [
562
+ // Primary instance: InternalAuthAuditService (has recordEvent + fetch methods)
486
563
  {
487
564
  provide: internal_1.AuthAuditService,
488
565
  useFactory: (auditRepository, userRepository, logger, clientInfoService) => {
@@ -490,6 +567,9 @@ let AuthModule = AuthModule_1 = class AuthModule {
490
567
  },
491
568
  inject: ['AuthAuditRepository', 'UserRepository', 'NAUTH_LOGGER', core_2.ClientInfoService],
492
569
  },
570
+ // Alias: AuthAuditService points to the same InternalAuthAuditService instance
571
+ // Consumer apps inject AuthAuditService but get the full instance
572
+ // TypeScript types prevent them from calling recordEvent() (not in public interface)
493
573
  {
494
574
  provide: core_2.AuthAuditService,
495
575
  useExisting: internal_1.AuthAuditService,
@@ -528,19 +608,25 @@ let AuthModule = AuthModule_1 = class AuthModule {
528
608
  core_2.ClientInfoService,
529
609
  'NAUTH_CONFIG',
530
610
  'NAUTH_LOGGER',
531
- { token: internal_1.AuthAuditService, optional: true },
611
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
532
612
  ],
533
613
  },
614
+ // MaxMind Module (optional - only provided if package is installed and config is present)
615
+ // Uses dynamic import pattern (no require() - follows NestJS best practices)
534
616
  ...(config.geoLocation?.maxMind
535
617
  ? [
536
618
  {
537
619
  provide: 'MAXMIND_MODULE',
538
620
  useFactory: async () => {
539
621
  try {
622
+ // Use dynamic import (ES modules) instead of require()
623
+ // This is the proper NestJS way to handle optional peer dependencies
624
+ // Note: Module may not be installed - catch block handles gracefully
540
625
  const maxMindModule = await Promise.resolve().then(() => __importStar(require('@maxmind/geoip2-node')));
541
626
  return maxMindModule;
542
627
  }
543
628
  catch {
629
+ // Package not installed - return null (service will handle gracefully)
544
630
  return null;
545
631
  }
546
632
  },
@@ -552,6 +638,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
552
638
  useValue: null,
553
639
  },
554
640
  ]),
641
+ // GeoLocation Service (optional - only if MaxMind config provided)
555
642
  ...(config.geoLocation?.maxMind
556
643
  ? [
557
644
  {
@@ -563,6 +650,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
563
650
  },
564
651
  ]
565
652
  : []),
653
+ // Email Provider (required - must be provided in config or from email package)
566
654
  {
567
655
  provide: 'EMAIL_PROVIDER',
568
656
  useFactory: () => {
@@ -572,21 +660,25 @@ let AuthModule = AuthModule_1 = class AuthModule {
572
660
  ' yarn add @nauth-toolkit/email-nodemailer (for production)');
573
661
  }
574
662
  const provider = config.emailProvider;
663
+ // Validate required methods (core contract)
575
664
  if (typeof provider.sendVerificationEmail !== 'function' ||
576
665
  typeof provider.sendPasswordResetEmail !== 'function' ||
577
666
  typeof provider.sendWelcomeEmail !== 'function') {
578
667
  throw new core_2.NAuthException(core_2.AuthErrorCode.VALIDATION_FAILED, 'emailProvider must implement sendVerificationEmail, sendPasswordResetEmail, and sendWelcomeEmail');
579
668
  }
669
+ // Inject logger into provider if it has setLogger method
580
670
  {
581
671
  const maybeLoggerAware = provider;
582
672
  if (typeof maybeLoggerAware.setLogger === 'function') {
583
673
  maybeLoggerAware.setLogger(nauthLogger);
584
674
  }
585
675
  }
676
+ // Inject global variables from email config if provider supports it
586
677
  if (typeof provider
587
678
  .setGlobalVariables === 'function' &&
588
679
  config.email) {
589
680
  const globalVars = {};
681
+ // Extract top-level branding fields
590
682
  if (config.email.appName)
591
683
  globalVars.appName = config.email.appName;
592
684
  if (config.email.companyName)
@@ -601,6 +693,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
601
693
  globalVars.brandColor = config.email.brandColor;
602
694
  if (config.email.footerDisclaimer)
603
695
  globalVars.footerDisclaimer = config.email.footerDisclaimer;
696
+ // Merge with templates.globalVariables (templates.globalVariables takes precedence)
604
697
  const mergedVars = {
605
698
  ...globalVars,
606
699
  ...(config.email.templates?.globalVariables || {}),
@@ -623,9 +716,10 @@ let AuthModule = AuthModule_1 = class AuthModule {
623
716
  'NAUTH_CONFIG',
624
717
  core_2.ClientInfoService,
625
718
  'NAUTH_LOGGER',
626
- { token: internal_1.AuthAuditService, optional: true },
719
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
627
720
  ],
628
721
  },
722
+ // SMS Provider (optional - only needed if using phone verification)
629
723
  ...(config.smsProvider
630
724
  ? [
631
725
  {
@@ -635,26 +729,36 @@ let AuthModule = AuthModule_1 = class AuthModule {
635
729
  if (!provider || typeof provider.sendOTP !== 'function') {
636
730
  throw new core_2.NAuthException(core_2.AuthErrorCode.VALIDATION_FAILED, 'smsProvider must implement sendOTP');
637
731
  }
732
+ // Inject logger into provider if it has setLogger method
638
733
  {
639
734
  const maybeLoggerAware = provider;
640
735
  if (typeof maybeLoggerAware.setLogger === 'function') {
641
736
  maybeLoggerAware.setLogger(nauthLogger);
642
737
  }
643
738
  }
739
+ // ============================================================================
740
+ // Initialize SMS Template Engine
741
+ // ============================================================================
644
742
  const smsTemplates = config.sms?.templates;
645
743
  if (smsTemplates) {
744
+ // If user configured templates, the provider must support the template hooks.
745
+ // This avoids silent fallback to hard-coded messages when templates are expected.
646
746
  if (typeof provider.setTemplateEngine !== 'function') {
647
747
  throw new core_2.NAuthException(core_2.AuthErrorCode.VALIDATION_FAILED, 'sms.templates is configured, but smsProvider does not support templates. ' +
648
748
  'Please upgrade your SMS provider package to a version that implements setTemplateEngine().');
649
749
  }
750
+ // Use provided engine or create new one
650
751
  const templateEngine = smsTemplates.engine ?? new core_2.SMSTemplateEngineImpl(process.cwd(), nauthLogger);
752
+ // Register custom templates
651
753
  if (smsTemplates.customTemplates) {
652
754
  for (const [type, templateDef] of Object.entries(smsTemplates.customTemplates)) {
653
755
  try {
654
756
  if (templateDef.content) {
757
+ // Inline template
655
758
  templateEngine.registerTemplate(type, { content: templateDef.content });
656
759
  }
657
760
  else if (templateDef.contentPath) {
761
+ // File-based template
658
762
  await templateEngine.registerTemplateFromSources(type, {
659
763
  content: { filePath: templateDef.contentPath },
660
764
  });
@@ -666,8 +770,11 @@ let AuthModule = AuthModule_1 = class AuthModule {
666
770
  }
667
771
  }
668
772
  }
773
+ // Inject template engine into provider
669
774
  provider.setTemplateEngine(templateEngine);
775
+ // Set global variables
670
776
  if (typeof provider.setGlobalVariables === 'function' && smsTemplates.globalVariables) {
777
+ // Extract top-level branding fields from email config (if available)
671
778
  const globalVars = {};
672
779
  if (config.email?.appName)
673
780
  globalVars.appName = config.email.appName;
@@ -675,6 +782,8 @@ let AuthModule = AuthModule_1 = class AuthModule {
675
782
  globalVars.companyName = config.email.companyName;
676
783
  if (config.email?.supportEmail)
677
784
  globalVars.supportEmail = config.email.supportEmail;
785
+ // Merge with sms.templates.globalVariables (sms.templates.globalVariables takes precedence)
786
+ // Filter out non-compatible types from globalVariables
678
787
  const smsGlobalVars = {};
679
788
  for (const [key, value] of Object.entries(smsTemplates.globalVariables)) {
680
789
  if (typeof value === 'string' ||
@@ -706,7 +815,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
706
815
  'STORAGE_ADAPTER',
707
816
  'NAUTH_CONFIG',
708
817
  core_2.ClientInfoService,
709
- { token: internal_1.AuthAuditService, optional: true },
818
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
710
819
  'NAUTH_LOGGER',
711
820
  ],
712
821
  },
@@ -714,7 +823,8 @@ let AuthModule = AuthModule_1 = class AuthModule {
714
823
  : []),
715
824
  {
716
825
  provide: internal_1.PasswordResetService,
717
- useFactory: (verificationTokenRepo, emailProvider, storageAdapter, nauthConfig, clientInfoService, logger, auditService, smsProvider) => {
826
+ useFactory: (verificationTokenRepo, emailProvider, storageAdapter, nauthConfig, clientInfoService, logger, auditService, // Optional - only available when auditLogs.enabled is true
827
+ smsProvider) => {
718
828
  return new internal_1.PasswordResetService(verificationTokenRepo, emailProvider, storageAdapter, nauthConfig, clientInfoService, logger, auditService, smsProvider);
719
829
  },
720
830
  inject: [
@@ -724,8 +834,8 @@ let AuthModule = AuthModule_1 = class AuthModule {
724
834
  'NAUTH_CONFIG',
725
835
  core_2.ClientInfoService,
726
836
  'NAUTH_LOGGER',
727
- { token: internal_1.AuthAuditService, optional: true },
728
- { token: 'SMS_PROVIDER', optional: true },
837
+ { token: internal_1.AuthAuditService, optional: true }, // Optional - only available when auditLogs.enabled is true
838
+ { token: 'SMS_PROVIDER', optional: true }, // Optional - only available when smsProvider is configured
729
839
  ],
730
840
  },
731
841
  {
@@ -742,6 +852,7 @@ let AuthModule = AuthModule_1 = class AuthModule {
742
852
  },
743
853
  inject: ['STORAGE_ADAPTER'],
744
854
  },
855
+ // Guards
745
856
  auth_guard_1.AuthGuard,
746
857
  ...(config.tokenDelivery?.method === 'cookies' || config.tokenDelivery?.method === 'hybrid' ? [csrf_guard_1.CsrfGuard] : []),
747
858
  ],
@@ -751,9 +862,14 @@ let AuthModule = AuthModule_1 = class AuthModule {
751
862
  internal_1.JwtService,
752
863
  internal_1.SessionService,
753
864
  internal_1.ChallengeService,
754
- internal_1.AuthChallengeHelperService,
755
- internal_1.SocialProviderRegistry,
865
+ internal_1.AuthChallengeHelperService, // Needed by social auth providers
866
+ internal_1.SocialProviderRegistry, // Needed by social auth provider modules for auto-registration
756
867
  core_2.ClientInfoService,
868
+ // Audit Services (conditional - only if enabled)
869
+ // Single instance, exported under two tokens:
870
+ // - AuthAuditService (public API) - For consumer apps to fetch audit logs (TypeScript prevents recordEvent)
871
+ // - InternalAuthAuditService - For INTERNAL toolkit packages ONLY (social auth, MFA providers)
872
+ // Consumer apps should inject AuthAuditService, internal packages inject InternalAuthAuditService
757
873
  ...(config.auditLogs?.enabled !== false ? [core_2.AuthAuditService, internal_1.AuthAuditService] : []),
758
874
  auth_guard_1.AuthGuard,
759
875
  ...(config.tokenDelivery?.method === 'cookies' || config.tokenDelivery?.method === 'hybrid'
@@ -765,19 +881,42 @@ let AuthModule = AuthModule_1 = class AuthModule {
765
881
  'EMAIL_PROVIDER',
766
882
  core_2.EmailVerificationService,
767
883
  ...(config.smsProvider ? ['SMS_PROVIDER', core_2.PhoneVerificationService] : []),
768
- core_2.SocialAuthService,
769
- core_2.MFAService,
884
+ core_2.SocialAuthService, // Always export - providers register themselves when modules are imported
885
+ core_2.MFAService, // Always export - MFA providers register themselves when modules are imported
886
+ // TrustedDeviceService is provided but not exported (used internally by AuthService)
770
887
  'NAUTH_LOGGER',
771
- 'NAUTH_CONFIG',
772
- 'SOCIAL_AUTH_STATE_STORE',
888
+ 'NAUTH_CONFIG', // Export config so other modules can access it
889
+ 'SOCIAL_AUTH_STATE_STORE', // Needed by social auth providers for CSRF protection
890
+ // Repository tokens exported for internal toolkit packages (MFA providers, etc.)
891
+ // WARNING: These are for INTERNAL toolkit packages ONLY, not consumer apps
892
+ // Consumer apps should use service methods (AuthService, MFAService, etc.) instead of direct repository access
773
893
  'UserRepository',
774
894
  'MFADeviceRepository',
895
+ // Note: TypeOrmModule not exported to prevent consumer apps from accessing entities directly
896
+ // Note: MFA provider services (TOTPMFAProviderService, SMSMFAProviderService, PasskeyMFAProviderService)
897
+ // are in @nauth-toolkit/mfa-* packages and auto-register with MFAService
898
+ // These providers need repository access but are internal toolkit packages, not consumer apps
899
+ // Note: PhoneVerificationService is provided from core when an SMS provider is configured
775
900
  ],
776
901
  };
777
902
  }
903
+ /**
904
+ * Validate configuration using Zod schema
905
+ *
906
+ * Validates all configuration sections and cross-dependencies:
907
+ * - Email/phone verification requires respective providers
908
+ * - MFA enforcement modes require specific configurations
909
+ * - Social providers require credentials when enabled
910
+ * - JWT algorithm requires appropriate keys
911
+ * - MaxMind geolocation requires credentials for downloads
912
+ *
913
+ * @param config - Configuration to validate
914
+ * @throws {NAuthException} If validation fails with detailed error messages
915
+ */
778
916
  static validateConfig(config) {
779
917
  const result = core_2.authConfigSchema.safeParse(config);
780
918
  if (!result.success) {
919
+ // Format Zod errors into readable messages
781
920
  const errors = result.error.errors
782
921
  .map((err) => {
783
922
  const path = err.path.length > 0 ? err.path.join('.') : 'root';
@@ -787,6 +926,9 @@ let AuthModule = AuthModule_1 = class AuthModule {
787
926
  throw new core_2.NAuthException(core_2.AuthErrorCode.VALIDATION_FAILED, `Configuration validation failed:\n\n${errors}\n\n` +
788
927
  'Please check your auth configuration and ensure all required fields are provided.');
789
928
  }
929
+ // Configuration is valid - no need to return anything
930
+ // TypeScript types ensure type safety at compile time
931
+ // Zod ensures runtime validation for JavaScript/Express apps
790
932
  }
791
933
  };
792
934
  exports.AuthModule = AuthModule;