@lenne.tech/nest-server 11.11.1 → 11.13.0
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.
- package/dist/config.env.js +1 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +16 -0
- package/dist/core/modules/auth/core-auth.controller.js +1 -1
- package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.js +1 -1
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-token.service.js +1 -4
- package/dist/core/modules/better-auth/better-auth-token.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.d.ts +13 -0
- package/dist/core/modules/better-auth/better-auth.config.js +114 -17
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +7 -3
- package/dist/core/modules/better-auth/better-auth.resolver.js +16 -6
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +4 -2
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +63 -18
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-auth.model.d.ts +1 -0
- package/dist/core/modules/better-auth/core-better-auth-auth.model.js +7 -0
- package/dist/core/modules/better-auth/core-better-auth-auth.model.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-cookie.helper.d.ts +41 -0
- package/dist/core/modules/better-auth/core-better-auth-cookie.helper.js +107 -0
- package/dist/core/modules/better-auth/core-better-auth-cookie.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.d.ts +48 -0
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js +241 -0
- package/dist/core/modules/better-auth/core-better-auth-email-verification.service.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-models.d.ts +2 -1
- package/dist/core/modules/better-auth/core-better-auth-models.js +8 -4
- package/dist/core/modules/better-auth/core-better-auth-models.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-signup-validator.service.d.ts +18 -0
- package/dist/core/modules/better-auth/core-better-auth-signup-validator.service.js +82 -0
- package/dist/core/modules/better-auth/core-better-auth-signup-validator.service.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-token.helper.d.ts +16 -0
- package/dist/core/modules/better-auth/core-better-auth-token.helper.js +66 -0
- package/dist/core/modules/better-auth/core-better-auth-token.helper.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.d.ts +0 -1
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +15 -8
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +3 -3
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +64 -44
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +13 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.js +108 -49
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.d.ts +0 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.js +57 -39
- package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.module.d.ts +6 -0
- package/dist/core/modules/better-auth/core-better-auth.module.js +129 -24
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +12 -5
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +64 -17
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.service.d.ts +4 -1
- package/dist/core/modules/better-auth/core-better-auth.service.js +143 -23
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +4 -0
- package/dist/core/modules/better-auth/index.js +4 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/error-code/error-codes.d.ts +45 -0
- package/dist/core/modules/error-code/error-codes.js +40 -0
- package/dist/core/modules/error-code/error-codes.js.map +1 -1
- package/dist/core/modules/user/core-user.model.d.ts +1 -0
- package/dist/core/modules/user/core-user.model.js +11 -0
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.controller.d.ts +3 -1
- package/dist/server/modules/better-auth/better-auth.controller.js +12 -3
- package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +7 -3
- package/dist/server/modules/better-auth/better-auth.resolver.js +16 -6
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/error-code/error-codes.d.ts +5 -0
- package/dist/server/modules/user/user.model.d.ts +5 -0
- package/dist/templates/email-verification-de.ejs +78 -0
- package/dist/templates/email-verification-en.ejs +78 -0
- package/dist/test/test.helper.d.ts +4 -0
- package/dist/test/test.helper.js +54 -1
- package/dist/test/test.helper.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +10 -10
- package/src/config.env.ts +2 -0
- package/src/core/common/interfaces/server-options.interface.ts +240 -0
- package/src/core/modules/auth/core-auth.controller.ts +2 -2
- package/src/core/modules/auth/core-auth.resolver.ts +2 -2
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +113 -0
- package/src/core/modules/better-auth/README.md +72 -7
- package/src/core/modules/better-auth/better-auth-token.service.ts +5 -8
- package/src/core/modules/better-auth/better-auth.config.ts +282 -29
- package/src/core/modules/better-auth/better-auth.resolver.ts +16 -5
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +100 -22
- package/src/core/modules/better-auth/core-better-auth-auth.model.ts +10 -0
- package/src/core/modules/better-auth/core-better-auth-cookie.helper.ts +323 -0
- package/src/core/modules/better-auth/core-better-auth-email-verification.service.ts +433 -0
- package/src/core/modules/better-auth/core-better-auth-models.ts +6 -3
- package/src/core/modules/better-auth/core-better-auth-signup-validator.service.ts +178 -0
- package/src/core/modules/better-auth/core-better-auth-token.helper.ts +200 -0
- package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +18 -14
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +119 -69
- package/src/core/modules/better-auth/core-better-auth.controller.ts +197 -84
- package/src/core/modules/better-auth/core-better-auth.middleware.ts +93 -64
- package/src/core/modules/better-auth/core-better-auth.module.ts +215 -38
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +140 -20
- package/src/core/modules/better-auth/core-better-auth.service.ts +210 -32
- package/src/core/modules/better-auth/index.ts +4 -0
- package/src/core/modules/error-code/error-codes.ts +45 -0
- package/src/core/modules/user/core-user.model.ts +15 -0
- package/src/server/modules/better-auth/better-auth.controller.ts +6 -2
- package/src/server/modules/better-auth/better-auth.resolver.ts +16 -5
- package/src/templates/email-verification-de.ejs +78 -0
- package/src/templates/email-verification-en.ejs +78 -0
- package/src/test/README.md +190 -0
- package/src/test/test.helper.ts +82 -1
|
@@ -14,6 +14,7 @@ import { getConnectionToken } from '@nestjs/mongoose';
|
|
|
14
14
|
import mongoose, { Connection } from 'mongoose';
|
|
15
15
|
|
|
16
16
|
import { IBetterAuth } from '../../common/interfaces/server-options.interface';
|
|
17
|
+
import { BrevoService } from '../../common/services/brevo.service';
|
|
17
18
|
import { ConfigService } from '../../common/services/config.service';
|
|
18
19
|
import { RolesGuardRegistry } from '../auth/guards/roles-guard-registry';
|
|
19
20
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
|
@@ -22,8 +23,10 @@ import { BetterAuthInstance, createBetterAuthInstance } from './better-auth.conf
|
|
|
22
23
|
import { DefaultBetterAuthResolver } from './better-auth.resolver';
|
|
23
24
|
import { CoreBetterAuthApiMiddleware } from './core-better-auth-api.middleware';
|
|
24
25
|
import { CoreBetterAuthChallengeService } from './core-better-auth-challenge.service';
|
|
26
|
+
import { CoreBetterAuthEmailVerificationService } from './core-better-auth-email-verification.service';
|
|
25
27
|
import { CoreBetterAuthRateLimitMiddleware } from './core-better-auth-rate-limit.middleware';
|
|
26
28
|
import { CoreBetterAuthRateLimiter } from './core-better-auth-rate-limiter.service';
|
|
29
|
+
import { CoreBetterAuthSignUpValidatorService } from './core-better-auth-signup-validator.service';
|
|
27
30
|
import { CoreBetterAuthUserMapper } from './core-better-auth-user.mapper';
|
|
28
31
|
import { CoreBetterAuthController } from './core-better-auth.controller';
|
|
29
32
|
import { CoreBetterAuthMiddleware } from './core-better-auth.middleware';
|
|
@@ -222,6 +225,9 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
222
225
|
private static shouldRegisterRolesGuardGlobally = false;
|
|
223
226
|
// Track if registerRolesGuardGlobally was explicitly set to false (for warning)
|
|
224
227
|
private static rolesGuardExplicitlyDisabled = false;
|
|
228
|
+
// Static reference to email verification service for Better-Auth hooks (outside DI context)
|
|
229
|
+
private static emailVerificationService: CoreBetterAuthEmailVerificationService | null = null;
|
|
230
|
+
private static mongoConnection: Connection | null = null;
|
|
225
231
|
|
|
226
232
|
/**
|
|
227
233
|
* Gets the controller class to use (custom or default)
|
|
@@ -253,6 +259,26 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
253
259
|
this.rateLimiter.configure(CoreBetterAuthModule.currentConfig.rateLimit);
|
|
254
260
|
}
|
|
255
261
|
|
|
262
|
+
// Configuration warning: cookies: false without jwt enabled
|
|
263
|
+
// When cookies are disabled, BetterAuth needs JWT plugin to issue tokens via Authorization header
|
|
264
|
+
// JWT is enabled by default (same logic as CoreBetterAuthService.isJwtEnabled()),
|
|
265
|
+
// so only warn when explicitly disabled via `jwt: false` or `jwt: { enabled: false }`
|
|
266
|
+
if (CoreBetterAuthModule.currentConfig) {
|
|
267
|
+
const globalConfig = ConfigService.configFastButReadOnly;
|
|
268
|
+
const cookiesDisabled = globalConfig?.cookies === false;
|
|
269
|
+
const jwtExplicitlyDisabled = CoreBetterAuthModule.currentConfig.jwt === false
|
|
270
|
+
|| (typeof CoreBetterAuthModule.currentConfig.jwt === 'object' && CoreBetterAuthModule.currentConfig.jwt?.enabled === false);
|
|
271
|
+
|
|
272
|
+
if (cookiesDisabled && jwtExplicitlyDisabled) {
|
|
273
|
+
CoreBetterAuthModule.logger.warn(
|
|
274
|
+
'CONFIGURATION WARNING: cookies is set to false, but betterAuth.jwt is not enabled. ' +
|
|
275
|
+
'Without cookies, BetterAuth cannot establish sessions via Set-Cookie headers. ' +
|
|
276
|
+
'Enable betterAuth.jwt (set jwt: true in betterAuth config) to use Bearer token authentication, ' +
|
|
277
|
+
'or set cookies: true to use cookie-based sessions.',
|
|
278
|
+
);
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
|
|
256
282
|
// Security warning: Check if RolesGuard is registered when explicitly disabled
|
|
257
283
|
// This warning helps developers identify potential security misconfigurations
|
|
258
284
|
if (CoreBetterAuthModule.rolesGuardExplicitlyDisabled && !RolesGuardRegistry.isRegistered()) {
|
|
@@ -265,22 +291,27 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
265
291
|
}
|
|
266
292
|
|
|
267
293
|
/**
|
|
268
|
-
* Configure middleware for Better-Auth
|
|
294
|
+
* Configure middleware for Better-Auth session validation, API handling, and rate limiting.
|
|
269
295
|
*
|
|
270
296
|
* Middleware order (important!):
|
|
271
|
-
* 1.
|
|
297
|
+
* 1. CoreBetterAuthMiddleware - Session validation and user mapping for all routes
|
|
298
|
+
* Must run FIRST so that req.betterAuthSession is available for downstream middleware.
|
|
299
|
+
* In JWT mode, this resolves the JWT to a real DB session (via getActiveSessionForUser).
|
|
272
300
|
* 2. CoreBetterAuthRateLimitMiddleware - Rate limiting for auth endpoints
|
|
273
|
-
* 3.
|
|
301
|
+
* 3. CoreBetterAuthApiMiddleware - Forwards plugin endpoints (passkey, 2FA, etc.) to Better Auth's native handler
|
|
302
|
+
* Runs AFTER session middleware so it can use req.betterAuthSession.session.token
|
|
303
|
+
* to authenticate requests in JWT mode.
|
|
274
304
|
*/
|
|
275
305
|
configure(consumer: MiddlewareConsumer) {
|
|
276
306
|
// Only apply middleware if Better-Auth is enabled
|
|
277
307
|
if (CoreBetterAuthModule.betterAuthEnabled && this.betterAuthService?.isEnabled()) {
|
|
278
308
|
const basePath = CoreBetterAuthModule.currentConfig?.basePath || '/iam';
|
|
279
309
|
|
|
280
|
-
// Apply
|
|
281
|
-
// This
|
|
282
|
-
|
|
283
|
-
|
|
310
|
+
// Apply session middleware to all routes FIRST
|
|
311
|
+
// This resolves JWT tokens to DB sessions, making req.betterAuthSession available
|
|
312
|
+
// for the API middleware to use when forwarding to Better Auth's native handler.
|
|
313
|
+
consumer.apply(CoreBetterAuthMiddleware).forRoutes('(.*)'); // New path-to-regexp syntax for wildcard
|
|
314
|
+
CoreBetterAuthModule.logger.debug('CoreBetterAuthMiddleware registered for all routes');
|
|
284
315
|
|
|
285
316
|
// Apply rate limiting to Better-Auth endpoints only
|
|
286
317
|
if (CoreBetterAuthModule.currentConfig?.rateLimit?.enabled) {
|
|
@@ -288,9 +319,11 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
288
319
|
CoreBetterAuthModule.logger.debug(`Rate limiting middleware registered for ${basePath}/*path endpoints`);
|
|
289
320
|
}
|
|
290
321
|
|
|
291
|
-
// Apply
|
|
292
|
-
|
|
293
|
-
|
|
322
|
+
// Apply API middleware to Better-Auth endpoints LAST
|
|
323
|
+
// This handles plugin endpoints (passkey, 2FA, social login, etc.) that are not defined in the controller.
|
|
324
|
+
// It uses req.betterAuthSession (set by session middleware above) for JWT mode authentication.
|
|
325
|
+
consumer.apply(CoreBetterAuthApiMiddleware).forRoutes(`${basePath}/*path`);
|
|
326
|
+
CoreBetterAuthModule.logger.debug(`CoreBetterAuthApiMiddleware registered for ${basePath}/*path endpoints`);
|
|
294
327
|
}
|
|
295
328
|
}
|
|
296
329
|
|
|
@@ -381,6 +414,8 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
381
414
|
// If better-auth is disabled (config is null or enabled: false), return minimal module
|
|
382
415
|
// Note: We don't provide middleware classes when disabled because they depend on CoreBetterAuthService
|
|
383
416
|
// and won't be used anyway (middleware is only applied when enabled)
|
|
417
|
+
// Note: EmailVerificationService and SignUpValidatorService are not provided in disabled mode
|
|
418
|
+
// because they require ConfigService and are only useful when BetterAuth is enabled
|
|
384
419
|
if (config === null || config?.enabled === false) {
|
|
385
420
|
this.logger.debug('BetterAuth is disabled - skipping initialization');
|
|
386
421
|
this.betterAuthEnabled = false;
|
|
@@ -399,6 +434,8 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
399
434
|
CoreBetterAuthRateLimiter,
|
|
400
435
|
BetterAuthTokenService,
|
|
401
436
|
CoreBetterAuthChallengeService,
|
|
437
|
+
// Note: EmailVerificationService and SignUpValidatorService are NOT provided when disabled
|
|
438
|
+
// because they require ConfigService and have no purpose when BetterAuth is disabled
|
|
402
439
|
],
|
|
403
440
|
};
|
|
404
441
|
}
|
|
@@ -412,7 +449,8 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
412
449
|
// Always use deferred initialization to ensure MongoDB is ready
|
|
413
450
|
// This prevents timing issues during application startup
|
|
414
451
|
// Pass server-level URLs for Passkey auto-detection (using effective values from ConfigService fallback)
|
|
415
|
-
return this.createDeferredModule(config,
|
|
452
|
+
return this.createDeferredModule(config, {
|
|
453
|
+
fallbackSecrets: effectiveFallbackSecrets,
|
|
416
454
|
serverAppUrl: effectiveServerAppUrl,
|
|
417
455
|
serverBaseUrl: effectiveServerBaseUrl,
|
|
418
456
|
serverEnv: effectiveServerEnv,
|
|
@@ -428,14 +466,32 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
428
466
|
static forRootAsync(): DynamicModule {
|
|
429
467
|
return {
|
|
430
468
|
controllers: [this.getControllerClass()],
|
|
431
|
-
exports: [BETTER_AUTH_INSTANCE, CoreBetterAuthService, CoreBetterAuthUserMapper, CoreBetterAuthRateLimiter, BetterAuthTokenService, CoreBetterAuthChallengeService],
|
|
469
|
+
exports: [BETTER_AUTH_INSTANCE, CoreBetterAuthService, CoreBetterAuthUserMapper, CoreBetterAuthRateLimiter, BetterAuthTokenService, CoreBetterAuthChallengeService, CoreBetterAuthEmailVerificationService, CoreBetterAuthSignUpValidatorService],
|
|
432
470
|
imports: [],
|
|
433
471
|
module: CoreBetterAuthModule,
|
|
434
472
|
providers: [
|
|
473
|
+
// Optional BrevoService: uses factory to avoid constructor error when brevo config is missing
|
|
435
474
|
{
|
|
436
475
|
inject: [ConfigService],
|
|
476
|
+
provide: CoreBetterAuthEmailVerificationService.BREVO_SERVICE_TOKEN,
|
|
477
|
+
useFactory: (configService: ConfigService) => {
|
|
478
|
+
if (configService.configFastButReadOnly?.brevo?.apiKey) {
|
|
479
|
+
return new BrevoService(configService);
|
|
480
|
+
}
|
|
481
|
+
return null;
|
|
482
|
+
},
|
|
483
|
+
},
|
|
484
|
+
// Email verification service - must be initialized early for callbacks
|
|
485
|
+
CoreBetterAuthEmailVerificationService,
|
|
486
|
+
// Sign-up validator service
|
|
487
|
+
CoreBetterAuthSignUpValidatorService,
|
|
488
|
+
{
|
|
489
|
+
inject: [ConfigService, CoreBetterAuthEmailVerificationService],
|
|
437
490
|
provide: BETTER_AUTH_INSTANCE,
|
|
438
|
-
useFactory: async (configService: ConfigService) => {
|
|
491
|
+
useFactory: async (configService: ConfigService, emailVerificationService: CoreBetterAuthEmailVerificationService) => {
|
|
492
|
+
// Set static reference for callbacks BEFORE creating Better-Auth instance
|
|
493
|
+
this.setEmailVerificationService(emailVerificationService);
|
|
494
|
+
|
|
439
495
|
// Get raw config (can be boolean or object)
|
|
440
496
|
const rawConfig = configService.get<boolean | IBetterAuth>('betterAuth');
|
|
441
497
|
// Normalize: true → {}, false/undefined → null
|
|
@@ -463,13 +519,27 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
463
519
|
const jwtConfig = configService.get<{ refresh?: { secret?: string }; secret?: string }>('jwt');
|
|
464
520
|
const fallbackSecrets = [jwtConfig?.secret, jwtConfig?.refresh?.secret];
|
|
465
521
|
|
|
522
|
+
// Create email verification callbacks that delegate to the NestJS service
|
|
523
|
+
const { onEmailVerified, sendVerificationEmail } = this.createEmailVerificationCallbacks();
|
|
524
|
+
|
|
466
525
|
// Note: Secret validation is now handled in createBetterAuthInstance
|
|
467
526
|
// with fallback to jwt.secret, jwt.refresh.secret, or auto-generation
|
|
468
|
-
this.authInstance = createBetterAuthInstance({
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
527
|
+
this.authInstance = createBetterAuthInstance({
|
|
528
|
+
config,
|
|
529
|
+
db,
|
|
530
|
+
fallbackSecrets,
|
|
531
|
+
onEmailVerified,
|
|
532
|
+
sendVerificationEmail,
|
|
533
|
+
});
|
|
534
|
+
|
|
535
|
+
// Store a config copy with the resolved secret so that consumers
|
|
536
|
+
// (CoreBetterAuthService, CoreBetterAuthController) can sign cookies.
|
|
537
|
+
// The original config object may be frozen (from ConfigService), so we
|
|
538
|
+
// create a shallow copy with the resolved fallback secret applied.
|
|
539
|
+
const resolvedSecret = config.secret || fallbackSecrets?.find((s) => s && s.length >= 32);
|
|
540
|
+
this.currentConfig = resolvedSecret && resolvedSecret !== config.secret
|
|
541
|
+
? { ...config, secret: resolvedSecret }
|
|
542
|
+
: config;
|
|
473
543
|
|
|
474
544
|
if (this.authInstance) {
|
|
475
545
|
this.logger.log('BetterAuth initialized successfully');
|
|
@@ -480,7 +550,9 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
480
550
|
},
|
|
481
551
|
},
|
|
482
552
|
// Provide the resolved config for CoreBetterAuthService
|
|
553
|
+
// IMPORTANT: Must depend on BETTER_AUTH_INSTANCE to ensure currentConfig is set
|
|
483
554
|
{
|
|
555
|
+
inject: [BETTER_AUTH_INSTANCE],
|
|
484
556
|
provide: BETTER_AUTH_CONFIG,
|
|
485
557
|
useFactory: () => this.currentConfig,
|
|
486
558
|
},
|
|
@@ -545,33 +617,131 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
545
617
|
this.customResolver = null;
|
|
546
618
|
this.shouldRegisterRolesGuardGlobally = false;
|
|
547
619
|
this.rolesGuardExplicitlyDisabled = false;
|
|
620
|
+
this.emailVerificationService = null;
|
|
548
621
|
// Reset shared RolesGuard registry (shared with CoreAuthModule)
|
|
549
622
|
RolesGuardRegistry.reset();
|
|
550
623
|
}
|
|
551
624
|
|
|
625
|
+
/**
|
|
626
|
+
* Set the email verification service instance for Better-Auth hooks.
|
|
627
|
+
* Called internally during module initialization.
|
|
628
|
+
* @internal
|
|
629
|
+
*/
|
|
630
|
+
static setEmailVerificationService(service: CoreBetterAuthEmailVerificationService): void {
|
|
631
|
+
this.emailVerificationService = service;
|
|
632
|
+
}
|
|
633
|
+
|
|
634
|
+
/**
|
|
635
|
+
* Get the email verification service instance.
|
|
636
|
+
* @internal
|
|
637
|
+
*/
|
|
638
|
+
static getEmailVerificationService(): CoreBetterAuthEmailVerificationService | null {
|
|
639
|
+
return this.emailVerificationService;
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
/**
|
|
643
|
+
* Create email verification callbacks that delegate to the NestJS service.
|
|
644
|
+
* These callbacks are passed to Better-Auth during initialization.
|
|
645
|
+
* They access the service via static reference since Better-Auth hooks run outside DI context.
|
|
646
|
+
* @internal
|
|
647
|
+
*/
|
|
648
|
+
private static createEmailVerificationCallbacks(): {
|
|
649
|
+
onEmailVerified: (userId: string) => Promise<void>;
|
|
650
|
+
sendVerificationEmail: (options: { token: string; url: string; user: { email: string; id: string; name?: null | string } }) => Promise<void>;
|
|
651
|
+
} {
|
|
652
|
+
return {
|
|
653
|
+
onEmailVerified: async (userId: string) => {
|
|
654
|
+
// This callback is called by Better-Auth when email is verified
|
|
655
|
+
// Sync nest-server's verified/verifiedAt fields with Better-Auth's emailVerified
|
|
656
|
+
try {
|
|
657
|
+
const db = this.mongoConnection?.db;
|
|
658
|
+
if (db) {
|
|
659
|
+
const { ObjectId } = await import('mongodb');
|
|
660
|
+
await db.collection('users').updateOne(
|
|
661
|
+
{ _id: new ObjectId(userId) },
|
|
662
|
+
{ $set: { verified: true, verifiedAt: new Date() } },
|
|
663
|
+
);
|
|
664
|
+
this.logger.debug(`Email verified for user ${userId} - synced verified/verifiedAt`);
|
|
665
|
+
} else {
|
|
666
|
+
this.logger.warn(`Cannot sync verifiedAt for user ${userId} - no database connection`);
|
|
667
|
+
}
|
|
668
|
+
} catch (error) {
|
|
669
|
+
this.logger.error(`Failed to sync verifiedAt for user ${userId}: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
670
|
+
}
|
|
671
|
+
},
|
|
672
|
+
sendVerificationEmail: async (options) => {
|
|
673
|
+
// Delegate to the NestJS service
|
|
674
|
+
if (this.emailVerificationService) {
|
|
675
|
+
await this.emailVerificationService.sendVerificationEmail(options);
|
|
676
|
+
} else {
|
|
677
|
+
// Fallback: Log verification URL if service not available
|
|
678
|
+
this.logger.warn('Email verification service not available, logging URL for development');
|
|
679
|
+
this.logger.log(`[DEV] Verification URL for ${options.user.email}: ${options.url}`);
|
|
680
|
+
}
|
|
681
|
+
},
|
|
682
|
+
};
|
|
683
|
+
}
|
|
684
|
+
|
|
552
685
|
/**
|
|
553
686
|
* Creates a deferred initialization module that waits for mongoose connection
|
|
554
687
|
* By injecting the Connection token, NestJS ensures Mongoose is ready first
|
|
555
688
|
*
|
|
556
689
|
* @param config - BetterAuth configuration
|
|
557
|
-
* @param
|
|
558
|
-
* @param serverUrls - Server-level URLs for Passkey auto-detection
|
|
690
|
+
* @param options - Optional deferred module options (fallback secrets, server URLs)
|
|
559
691
|
*/
|
|
560
692
|
private static createDeferredModule(
|
|
561
693
|
config: IBetterAuth,
|
|
562
|
-
|
|
563
|
-
|
|
694
|
+
options?: {
|
|
695
|
+
fallbackSecrets?: (string | undefined)[];
|
|
696
|
+
serverAppUrl?: string;
|
|
697
|
+
serverBaseUrl?: string;
|
|
698
|
+
serverEnv?: string;
|
|
699
|
+
},
|
|
564
700
|
): DynamicModule {
|
|
565
701
|
return {
|
|
566
702
|
controllers: [this.getControllerClass()],
|
|
567
|
-
exports: [BETTER_AUTH_INSTANCE, CoreBetterAuthService, CoreBetterAuthUserMapper, CoreBetterAuthRateLimiter, BetterAuthTokenService, CoreBetterAuthChallengeService],
|
|
703
|
+
exports: [BETTER_AUTH_INSTANCE, CoreBetterAuthService, CoreBetterAuthUserMapper, CoreBetterAuthRateLimiter, BetterAuthTokenService, CoreBetterAuthChallengeService, CoreBetterAuthEmailVerificationService, CoreBetterAuthSignUpValidatorService],
|
|
568
704
|
module: CoreBetterAuthModule,
|
|
569
705
|
providers: [
|
|
706
|
+
// Optional BrevoService: uses factory to avoid constructor error when brevo config is missing
|
|
707
|
+
{
|
|
708
|
+
inject: [ConfigService],
|
|
709
|
+
provide: CoreBetterAuthEmailVerificationService.BREVO_SERVICE_TOKEN,
|
|
710
|
+
useFactory: (configService: ConfigService) => {
|
|
711
|
+
if (configService.configFastButReadOnly?.brevo?.apiKey) {
|
|
712
|
+
return new BrevoService(configService);
|
|
713
|
+
}
|
|
714
|
+
return null;
|
|
715
|
+
},
|
|
716
|
+
},
|
|
717
|
+
// Email verification service - must be initialized early for callbacks
|
|
718
|
+
CoreBetterAuthEmailVerificationService,
|
|
719
|
+
// Sign-up validator service
|
|
720
|
+
CoreBetterAuthSignUpValidatorService,
|
|
570
721
|
{
|
|
571
722
|
// Inject Mongoose Connection to ensure NestJS waits for it to be ready
|
|
572
|
-
inject
|
|
723
|
+
// Also inject EmailVerificationService to set static reference before Better-Auth init
|
|
724
|
+
inject: [getConnectionToken(), CoreBetterAuthEmailVerificationService],
|
|
573
725
|
provide: BETTER_AUTH_INSTANCE,
|
|
574
|
-
useFactory: async (connection: Connection) => {
|
|
726
|
+
useFactory: async (connection: Connection, emailVerificationService: CoreBetterAuthEmailVerificationService) => {
|
|
727
|
+
// Set static references for callbacks BEFORE creating Better-Auth instance
|
|
728
|
+
this.setEmailVerificationService(emailVerificationService);
|
|
729
|
+
this.mongoConnection = connection;
|
|
730
|
+
|
|
731
|
+
// Create email verification callbacks that delegate to the NestJS service
|
|
732
|
+
const { onEmailVerified, sendVerificationEmail } = this.createEmailVerificationCallbacks();
|
|
733
|
+
|
|
734
|
+
// Build shared instance options
|
|
735
|
+
const sharedInstanceOptions = {
|
|
736
|
+
config,
|
|
737
|
+
fallbackSecrets: options?.fallbackSecrets,
|
|
738
|
+
onEmailVerified,
|
|
739
|
+
sendVerificationEmail,
|
|
740
|
+
serverAppUrl: options?.serverAppUrl,
|
|
741
|
+
serverBaseUrl: options?.serverBaseUrl,
|
|
742
|
+
serverEnv: options?.serverEnv,
|
|
743
|
+
};
|
|
744
|
+
|
|
575
745
|
// Connection is now guaranteed to be established
|
|
576
746
|
const db = connection.db;
|
|
577
747
|
if (!db) {
|
|
@@ -582,27 +752,22 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
582
752
|
throw new Error('MongoDB database not available');
|
|
583
753
|
}
|
|
584
754
|
this.authInstance = createBetterAuthInstance({
|
|
585
|
-
|
|
755
|
+
...sharedInstanceOptions,
|
|
586
756
|
db: globalDb,
|
|
587
|
-
fallbackSecrets,
|
|
588
|
-
serverAppUrl: serverUrls?.serverAppUrl,
|
|
589
|
-
serverBaseUrl: serverUrls?.serverBaseUrl,
|
|
590
|
-
serverEnv: serverUrls?.serverEnv,
|
|
591
757
|
});
|
|
592
758
|
} else {
|
|
593
759
|
this.authInstance = createBetterAuthInstance({
|
|
594
|
-
|
|
760
|
+
...sharedInstanceOptions,
|
|
595
761
|
db,
|
|
596
|
-
fallbackSecrets,
|
|
597
|
-
serverAppUrl: serverUrls?.serverAppUrl,
|
|
598
|
-
serverBaseUrl: serverUrls?.serverBaseUrl,
|
|
599
|
-
serverEnv: serverUrls?.serverEnv,
|
|
600
762
|
});
|
|
601
763
|
}
|
|
602
764
|
|
|
603
|
-
//
|
|
604
|
-
|
|
605
|
-
|
|
765
|
+
// Store a config copy with the resolved secret (same as first forRoot variant)
|
|
766
|
+
const fallbacks = options?.fallbackSecrets;
|
|
767
|
+
const resolvedSecret2 = config.secret || fallbacks?.find((s) => s && s.length >= 32);
|
|
768
|
+
this.currentConfig = resolvedSecret2 && resolvedSecret2 !== config.secret
|
|
769
|
+
? { ...config, secret: resolvedSecret2 }
|
|
770
|
+
: config;
|
|
606
771
|
|
|
607
772
|
if (this.authInstance && !this.initLogged) {
|
|
608
773
|
this.initLogged = true;
|
|
@@ -614,7 +779,9 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
614
779
|
},
|
|
615
780
|
},
|
|
616
781
|
// Provide the resolved config for CoreBetterAuthService
|
|
782
|
+
// IMPORTANT: Must depend on BETTER_AUTH_INSTANCE to ensure currentConfig is set
|
|
617
783
|
{
|
|
784
|
+
inject: [BETTER_AUTH_INSTANCE],
|
|
618
785
|
provide: BETTER_AUTH_CONFIG,
|
|
619
786
|
useFactory: () => this.currentConfig,
|
|
620
787
|
},
|
|
@@ -716,6 +883,16 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
716
883
|
features.push(`Rate Limiting (${config.rateLimit.max || 10}/${config.rateLimit.windowSeconds || 60}s)`);
|
|
717
884
|
}
|
|
718
885
|
|
|
886
|
+
// Email verification is enabled by default unless explicitly disabled
|
|
887
|
+
if (!isExplicitlyDisabled(config.emailVerification)) {
|
|
888
|
+
features.push('Email Verification');
|
|
889
|
+
}
|
|
890
|
+
|
|
891
|
+
// Sign-up checks are enabled by default unless explicitly disabled
|
|
892
|
+
if (!isExplicitlyDisabled(config.signUpChecks)) {
|
|
893
|
+
features.push('Sign-Up Checks');
|
|
894
|
+
}
|
|
895
|
+
|
|
719
896
|
if (features.length > 0) {
|
|
720
897
|
this.logger.log(`Enabled features: ${features.join(', ')}`);
|
|
721
898
|
}
|