@lenne.tech/nest-server 11.6.1 → 11.6.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.
- package/dist/config.env.js +141 -0
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/decorators/graphql-populate.decorator.d.ts +2 -2
- package/dist/core/common/decorators/restricted.decorator.d.ts +1 -0
- package/dist/core/common/decorators/restricted.decorator.js +1 -1
- package/dist/core/common/decorators/restricted.decorator.js.map +1 -1
- package/dist/core/common/helpers/input.helper.d.ts +1 -0
- package/dist/core/common/helpers/input.helper.js +1 -1
- package/dist/core/common/helpers/input.helper.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +50 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.d.ts +1 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.js +1 -0
- package/dist/core/modules/auth/auth-guard-strategy.enum.js.map +1 -1
- package/dist/core/modules/auth/guards/auth.guard.js +11 -5
- package/dist/core/modules/auth/guards/auth.guard.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth-auth.model.d.ts +9 -0
- package/dist/core/modules/better-auth/better-auth-auth.model.js +63 -0
- package/dist/core/modules/better-auth/better-auth-auth.model.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-models.d.ts +44 -0
- package/dist/core/modules/better-auth/better-auth-models.js +185 -0
- package/dist/core/modules/better-auth/better-auth-models.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.d.ts +12 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js +70 -0
- package/dist/core/modules/better-auth/better-auth-rate-limit.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.d.ts +32 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +173 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +43 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js +159 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.config.d.ts +9 -0
- package/dist/core/modules/better-auth/better-auth.config.js +251 -0
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +20 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js +79 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.module.d.ts +30 -0
- package/dist/core/modules/better-auth/better-auth.module.js +265 -0
- package/dist/core/modules/better-auth/better-auth.module.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.resolver.d.ts +49 -0
- package/dist/core/modules/better-auth/better-auth.resolver.js +539 -0
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.service.d.ts +38 -0
- package/dist/core/modules/better-auth/better-auth.service.js +151 -0
- package/dist/core/modules/better-auth/better-auth.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.types.d.ts +38 -0
- package/dist/core/modules/better-auth/better-auth.types.js +15 -0
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -0
- package/dist/core/modules/better-auth/index.d.ts +11 -0
- package/dist/core/modules/better-auth/index.js +28 -0
- package/dist/core/modules/better-auth/index.js.map +1 -0
- package/dist/core/modules/user/core-user.model.d.ts +2 -0
- package/dist/core/modules/user/core-user.model.js +21 -0
- package/dist/core/modules/user/core-user.model.js.map +1 -1
- package/dist/core.module.js +7 -0
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +9 -1
- package/src/config.env.ts +148 -1
- package/src/core/common/decorators/restricted.decorator.ts +2 -2
- package/src/core/common/helpers/input.helper.ts +2 -2
- package/src/core/common/interfaces/server-options.interface.ts +344 -20
- package/src/core/modules/auth/auth-guard-strategy.enum.ts +1 -0
- package/src/core/modules/auth/guards/auth.guard.ts +20 -6
- package/src/core/modules/better-auth/README.md +1096 -0
- package/src/core/modules/better-auth/better-auth-auth.model.ts +69 -0
- package/src/core/modules/better-auth/better-auth-models.ts +143 -0
- package/src/core/modules/better-auth/better-auth-rate-limit.middleware.ts +113 -0
- package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +326 -0
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +269 -0
- package/src/core/modules/better-auth/better-auth.config.ts +483 -0
- package/src/core/modules/better-auth/better-auth.middleware.ts +111 -0
- package/src/core/modules/better-auth/better-auth.module.ts +433 -0
- package/src/core/modules/better-auth/better-auth.resolver.ts +678 -0
- package/src/core/modules/better-auth/better-auth.service.ts +323 -0
- package/src/core/modules/better-auth/better-auth.types.ts +75 -0
- package/src/core/modules/better-auth/index.ts +25 -0
- package/src/core/modules/user/core-user.model.ts +29 -0
- package/src/core.module.ts +12 -0
- package/src/index.ts +6 -0
|
@@ -0,0 +1,433 @@
|
|
|
1
|
+
import {
|
|
2
|
+
DynamicModule,
|
|
3
|
+
Global,
|
|
4
|
+
Logger,
|
|
5
|
+
MiddlewareConsumer,
|
|
6
|
+
Module,
|
|
7
|
+
NestModule,
|
|
8
|
+
OnModuleInit,
|
|
9
|
+
Optional,
|
|
10
|
+
} from '@nestjs/common';
|
|
11
|
+
import { getConnectionToken } from '@nestjs/mongoose';
|
|
12
|
+
import { AuthModule, AuthService } from '@thallesp/nestjs-better-auth';
|
|
13
|
+
import mongoose, { Connection } from 'mongoose';
|
|
14
|
+
|
|
15
|
+
import { IBetterAuth } from '../../common/interfaces/server-options.interface';
|
|
16
|
+
import { ConfigService } from '../../common/services/config.service';
|
|
17
|
+
import { BetterAuthRateLimitMiddleware } from './better-auth-rate-limit.middleware';
|
|
18
|
+
import { BetterAuthRateLimiter } from './better-auth-rate-limiter.service';
|
|
19
|
+
import { BetterAuthUserMapper } from './better-auth-user.mapper';
|
|
20
|
+
import { BetterAuthInstance, createBetterAuthInstance } from './better-auth.config';
|
|
21
|
+
import { BetterAuthMiddleware } from './better-auth.middleware';
|
|
22
|
+
import { BetterAuthResolver } from './better-auth.resolver';
|
|
23
|
+
import { BetterAuthService } from './better-auth.service';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Token for injecting the better-auth instance
|
|
27
|
+
*/
|
|
28
|
+
export const BETTER_AUTH_INSTANCE = 'BETTER_AUTH_INSTANCE';
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Options for BetterAuthModule.forRoot()
|
|
32
|
+
*/
|
|
33
|
+
export interface BetterAuthModuleOptions {
|
|
34
|
+
/**
|
|
35
|
+
* Better-auth configuration
|
|
36
|
+
*/
|
|
37
|
+
config: IBetterAuth;
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* Fallback secrets to try if no betterAuth.secret is configured.
|
|
41
|
+
* The array is iterated and the first valid secret (≥32 chars) is used.
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret]
|
|
46
|
+
* ```
|
|
47
|
+
*/
|
|
48
|
+
fallbackSecrets?: (string | undefined)[];
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* BetterAuthModule provides integration with the better-auth authentication framework.
|
|
53
|
+
*
|
|
54
|
+
* This module:
|
|
55
|
+
* - Creates and configures a better-auth instance based on server configuration
|
|
56
|
+
* - Integrates with @thallesp/nestjs-better-auth for NestJS support
|
|
57
|
+
* - Supports JWT, 2FA, Passkey, and Social Login based on configuration
|
|
58
|
+
* - Enabled by default (zero-config) - set `enabled: false` to disable explicitly
|
|
59
|
+
* - Uses the global mongoose connection for MongoDB access
|
|
60
|
+
*
|
|
61
|
+
* @example
|
|
62
|
+
* ```typescript
|
|
63
|
+
* // In your AppModule - import after CoreModule so mongoose is connected
|
|
64
|
+
* @Module({
|
|
65
|
+
* imports: [
|
|
66
|
+
* CoreModule.forRoot(...),
|
|
67
|
+
* BetterAuthModule.forRoot({ config: environment.betterAuth }),
|
|
68
|
+
* ],
|
|
69
|
+
* })
|
|
70
|
+
* export class AppModule {}
|
|
71
|
+
* ```
|
|
72
|
+
*/
|
|
73
|
+
@Global()
|
|
74
|
+
@Module({})
|
|
75
|
+
export class BetterAuthModule implements NestModule, OnModuleInit {
|
|
76
|
+
private static logger = new Logger(BetterAuthModule.name);
|
|
77
|
+
private static authInstance: BetterAuthInstance | null = null;
|
|
78
|
+
private static initialized = false;
|
|
79
|
+
private static betterAuthEnabled = false;
|
|
80
|
+
private static currentConfig: IBetterAuth | null = null;
|
|
81
|
+
|
|
82
|
+
constructor(
|
|
83
|
+
@Optional() private readonly betterAuthService?: BetterAuthService,
|
|
84
|
+
@Optional() private readonly rateLimiter?: BetterAuthRateLimiter,
|
|
85
|
+
) {}
|
|
86
|
+
|
|
87
|
+
onModuleInit() {
|
|
88
|
+
if (BetterAuthModule.authInstance && !BetterAuthModule.initialized) {
|
|
89
|
+
BetterAuthModule.initialized = true;
|
|
90
|
+
BetterAuthModule.logger.log('BetterAuthModule ready');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Configure rate limiter with stored config
|
|
94
|
+
if (this.rateLimiter && BetterAuthModule.currentConfig?.rateLimit) {
|
|
95
|
+
this.rateLimiter.configure(BetterAuthModule.currentConfig.rateLimit);
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Configure middleware for Better-Auth session handling and rate limiting
|
|
101
|
+
* The session middleware runs on all routes and maps Better-Auth sessions to users
|
|
102
|
+
* The rate limit middleware runs only on Better-Auth endpoints
|
|
103
|
+
*/
|
|
104
|
+
configure(consumer: MiddlewareConsumer) {
|
|
105
|
+
// Only apply middleware if Better-Auth is enabled
|
|
106
|
+
if (BetterAuthModule.betterAuthEnabled && this.betterAuthService?.isEnabled()) {
|
|
107
|
+
const basePath = BetterAuthModule.currentConfig?.basePath || '/iam';
|
|
108
|
+
|
|
109
|
+
// Apply rate limiting to Better-Auth endpoints only
|
|
110
|
+
if (BetterAuthModule.currentConfig?.rateLimit?.enabled) {
|
|
111
|
+
consumer.apply(BetterAuthRateLimitMiddleware).forRoutes(`${basePath}/*`);
|
|
112
|
+
BetterAuthModule.logger.debug(`Rate limiting enabled for ${basePath}/* endpoints`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// Apply session middleware to all routes
|
|
116
|
+
consumer.apply(BetterAuthMiddleware).forRoutes('*');
|
|
117
|
+
BetterAuthModule.logger.debug('BetterAuthMiddleware registered for all routes');
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Waits for mongoose connection to be ready using polling
|
|
123
|
+
* This is more reliable than event-based waiting in test environments
|
|
124
|
+
* @throws Error if connection times out or fails
|
|
125
|
+
*/
|
|
126
|
+
private static async waitForMongoConnection(): Promise<void> {
|
|
127
|
+
const maxAttempts = 60; // 60 attempts * 500ms = 30 seconds max
|
|
128
|
+
const pollInterval = 500;
|
|
129
|
+
|
|
130
|
+
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
131
|
+
// Check if already connected
|
|
132
|
+
if (mongoose.connection.readyState === 1 && mongoose.connection.db) {
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// Check for error state
|
|
137
|
+
if (mongoose.connection.readyState === 0) {
|
|
138
|
+
// Disconnected - wait for reconnection
|
|
139
|
+
this.logger.debug(`MongoDB not connected (attempt ${attempt + 1}/${maxAttempts})`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// Wait before next check
|
|
143
|
+
await new Promise((resolve) => setTimeout(resolve, pollInterval));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
throw new Error('MongoDB connection timeout - ensure MongoDB is running and accessible');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Creates a dynamic module for BetterAuth (synchronous)
|
|
151
|
+
* Note: This requires mongoose connection to be already established.
|
|
152
|
+
* For async initialization, use forRootAsync.
|
|
153
|
+
*
|
|
154
|
+
* @param options - Configuration options
|
|
155
|
+
* @returns Dynamic module configuration
|
|
156
|
+
*/
|
|
157
|
+
static forRoot(options: BetterAuthModuleOptions): DynamicModule {
|
|
158
|
+
const { config, fallbackSecrets } = options;
|
|
159
|
+
|
|
160
|
+
// Store config for middleware configuration
|
|
161
|
+
this.currentConfig = config;
|
|
162
|
+
|
|
163
|
+
// If better-auth is explicitly disabled, return minimal module
|
|
164
|
+
// Note: We don't provide middleware classes when disabled because they depend on BetterAuthService
|
|
165
|
+
// and won't be used anyway (middleware is only applied when enabled)
|
|
166
|
+
// BetterAuth is enabled by default unless explicitly set to false
|
|
167
|
+
if (config?.enabled === false) {
|
|
168
|
+
this.logger.debug('BetterAuth is explicitly disabled - skipping initialization');
|
|
169
|
+
this.betterAuthEnabled = false;
|
|
170
|
+
return {
|
|
171
|
+
exports: [BETTER_AUTH_INSTANCE, BetterAuthService, BetterAuthUserMapper, BetterAuthRateLimiter],
|
|
172
|
+
module: BetterAuthModule,
|
|
173
|
+
providers: [
|
|
174
|
+
{
|
|
175
|
+
provide: BETTER_AUTH_INSTANCE,
|
|
176
|
+
useValue: null,
|
|
177
|
+
},
|
|
178
|
+
// Note: ConfigService is provided globally by CoreModule
|
|
179
|
+
// Tests need to provide their own ConfigService
|
|
180
|
+
BetterAuthService,
|
|
181
|
+
BetterAuthUserMapper,
|
|
182
|
+
BetterAuthRateLimiter,
|
|
183
|
+
],
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
// Enable middleware registration
|
|
188
|
+
this.betterAuthEnabled = true;
|
|
189
|
+
|
|
190
|
+
// Note: Secret validation is now handled in createBetterAuthInstance
|
|
191
|
+
// with fallback to jwt.secret, jwt.refresh.secret, or auto-generation
|
|
192
|
+
|
|
193
|
+
// Always use deferred initialization to ensure MongoDB is ready
|
|
194
|
+
// This prevents timing issues during application startup
|
|
195
|
+
return this.createDeferredModule(config, fallbackSecrets);
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Creates an async dynamic module for BetterAuth
|
|
200
|
+
* This is the preferred method as it properly waits for mongoose connection.
|
|
201
|
+
*
|
|
202
|
+
* @returns Dynamic module configuration
|
|
203
|
+
*/
|
|
204
|
+
static forRootAsync(): DynamicModule {
|
|
205
|
+
return {
|
|
206
|
+
exports: [BETTER_AUTH_INSTANCE, BetterAuthService, BetterAuthUserMapper, BetterAuthRateLimiter],
|
|
207
|
+
imports: [],
|
|
208
|
+
module: BetterAuthModule,
|
|
209
|
+
providers: [
|
|
210
|
+
{
|
|
211
|
+
inject: [ConfigService],
|
|
212
|
+
provide: BETTER_AUTH_INSTANCE,
|
|
213
|
+
useFactory: async (configService: ConfigService) => {
|
|
214
|
+
const config = configService.get<IBetterAuth>('betterAuth');
|
|
215
|
+
|
|
216
|
+
// Store config for middleware configuration
|
|
217
|
+
this.currentConfig = config || null;
|
|
218
|
+
|
|
219
|
+
// BetterAuth is enabled by default unless explicitly set to false
|
|
220
|
+
if (config?.enabled === false) {
|
|
221
|
+
this.logger.debug('BetterAuth is explicitly disabled');
|
|
222
|
+
this.betterAuthEnabled = false;
|
|
223
|
+
return null;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// Enable middleware registration
|
|
227
|
+
this.betterAuthEnabled = true;
|
|
228
|
+
|
|
229
|
+
await this.waitForMongoConnection();
|
|
230
|
+
|
|
231
|
+
const db = mongoose.connection.db;
|
|
232
|
+
if (!db) {
|
|
233
|
+
throw new Error('MongoDB database not available');
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
// Get JWT secrets from config for backwards compatibility fallback
|
|
237
|
+
const jwtConfig = configService.get<{ refresh?: { secret?: string }; secret?: string }>('jwt');
|
|
238
|
+
const fallbackSecrets = [jwtConfig?.secret, jwtConfig?.refresh?.secret];
|
|
239
|
+
|
|
240
|
+
// Note: Secret validation is now handled in createBetterAuthInstance
|
|
241
|
+
// with fallback to jwt.secret, jwt.refresh.secret, or auto-generation
|
|
242
|
+
this.authInstance = createBetterAuthInstance({ config, db, fallbackSecrets });
|
|
243
|
+
|
|
244
|
+
if (this.authInstance) {
|
|
245
|
+
this.logger.log('BetterAuth initialized successfully');
|
|
246
|
+
this.logEnabledFeatures(config);
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
return this.authInstance;
|
|
250
|
+
},
|
|
251
|
+
},
|
|
252
|
+
// Note: ConfigService is provided globally by CoreModule
|
|
253
|
+
BetterAuthService,
|
|
254
|
+
BetterAuthUserMapper,
|
|
255
|
+
BetterAuthMiddleware,
|
|
256
|
+
BetterAuthRateLimiter,
|
|
257
|
+
BetterAuthRateLimitMiddleware,
|
|
258
|
+
BetterAuthResolver,
|
|
259
|
+
],
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Gets the current better-auth instance
|
|
265
|
+
* Useful for accessing the auth API directly
|
|
266
|
+
*/
|
|
267
|
+
static getInstance(): BetterAuthInstance | null {
|
|
268
|
+
return this.authInstance;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Resets the static state of BetterAuthModule
|
|
273
|
+
* This is primarily useful for testing to ensure clean state between tests
|
|
274
|
+
*
|
|
275
|
+
* @example
|
|
276
|
+
* ```typescript
|
|
277
|
+
* afterEach(() => {
|
|
278
|
+
* BetterAuthModule.reset();
|
|
279
|
+
* });
|
|
280
|
+
* ```
|
|
281
|
+
*/
|
|
282
|
+
static reset(): void {
|
|
283
|
+
this.authInstance = null;
|
|
284
|
+
this.initialized = false;
|
|
285
|
+
this.betterAuthEnabled = false;
|
|
286
|
+
this.currentConfig = null;
|
|
287
|
+
this.logger.debug('BetterAuthModule state reset');
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/**
|
|
291
|
+
* Creates the actual module with better-auth
|
|
292
|
+
*/
|
|
293
|
+
private static createModule(config: IBetterAuth, db: any, fallbackSecrets?: (string | undefined)[]): DynamicModule {
|
|
294
|
+
// Create the better-auth instance
|
|
295
|
+
this.authInstance = createBetterAuthInstance({ config, db, fallbackSecrets });
|
|
296
|
+
|
|
297
|
+
if (!this.authInstance) {
|
|
298
|
+
throw new Error('Failed to create better-auth instance');
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
this.logger.log('BetterAuth initialized successfully');
|
|
302
|
+
this.logEnabledFeatures(config);
|
|
303
|
+
|
|
304
|
+
// Use @thallesp/nestjs-better-auth's AuthModule
|
|
305
|
+
const authModule = AuthModule.forRoot({
|
|
306
|
+
auth: this.authInstance,
|
|
307
|
+
disableControllers: false, // Enable REST endpoints
|
|
308
|
+
disableGlobalAuthGuard: true, // We use our own RolesGuard
|
|
309
|
+
});
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
exports: [
|
|
313
|
+
BETTER_AUTH_INSTANCE,
|
|
314
|
+
AuthModule,
|
|
315
|
+
AuthService,
|
|
316
|
+
BetterAuthService,
|
|
317
|
+
BetterAuthUserMapper,
|
|
318
|
+
BetterAuthRateLimiter,
|
|
319
|
+
],
|
|
320
|
+
imports: [authModule],
|
|
321
|
+
module: BetterAuthModule,
|
|
322
|
+
providers: [
|
|
323
|
+
{
|
|
324
|
+
provide: BETTER_AUTH_INSTANCE,
|
|
325
|
+
useValue: this.authInstance,
|
|
326
|
+
},
|
|
327
|
+
// Note: ConfigService is provided globally by CoreModule
|
|
328
|
+
BetterAuthService,
|
|
329
|
+
BetterAuthUserMapper,
|
|
330
|
+
BetterAuthMiddleware,
|
|
331
|
+
BetterAuthRateLimiter,
|
|
332
|
+
BetterAuthRateLimitMiddleware,
|
|
333
|
+
BetterAuthResolver,
|
|
334
|
+
],
|
|
335
|
+
};
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* Creates a deferred initialization module that waits for mongoose connection
|
|
340
|
+
* By injecting the Connection token, NestJS ensures Mongoose is ready first
|
|
341
|
+
*/
|
|
342
|
+
private static createDeferredModule(config: IBetterAuth, fallbackSecrets?: (string | undefined)[]): DynamicModule {
|
|
343
|
+
return {
|
|
344
|
+
exports: [BETTER_AUTH_INSTANCE, BetterAuthService, BetterAuthUserMapper, BetterAuthRateLimiter],
|
|
345
|
+
module: BetterAuthModule,
|
|
346
|
+
providers: [
|
|
347
|
+
{
|
|
348
|
+
// Inject Mongoose Connection to ensure NestJS waits for it to be ready
|
|
349
|
+
inject: [getConnectionToken()],
|
|
350
|
+
provide: BETTER_AUTH_INSTANCE,
|
|
351
|
+
useFactory: async (connection: Connection) => {
|
|
352
|
+
// Connection is now guaranteed to be established
|
|
353
|
+
const db = connection.db;
|
|
354
|
+
if (!db) {
|
|
355
|
+
// Fallback to global mongoose if connection.db is not yet available
|
|
356
|
+
await this.waitForMongoConnection();
|
|
357
|
+
const globalDb = mongoose.connection.db;
|
|
358
|
+
if (!globalDb) {
|
|
359
|
+
throw new Error('MongoDB database not available');
|
|
360
|
+
}
|
|
361
|
+
this.authInstance = createBetterAuthInstance({ config, db: globalDb, fallbackSecrets });
|
|
362
|
+
} else {
|
|
363
|
+
this.authInstance = createBetterAuthInstance({ config, db, fallbackSecrets });
|
|
364
|
+
}
|
|
365
|
+
|
|
366
|
+
if (this.authInstance) {
|
|
367
|
+
this.logger.log('BetterAuth initialized');
|
|
368
|
+
this.logEnabledFeatures(config);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
return this.authInstance;
|
|
372
|
+
},
|
|
373
|
+
},
|
|
374
|
+
// Note: ConfigService is provided globally by CoreModule
|
|
375
|
+
BetterAuthService,
|
|
376
|
+
BetterAuthUserMapper,
|
|
377
|
+
BetterAuthMiddleware,
|
|
378
|
+
BetterAuthRateLimiter,
|
|
379
|
+
BetterAuthRateLimitMiddleware,
|
|
380
|
+
BetterAuthResolver,
|
|
381
|
+
],
|
|
382
|
+
};
|
|
383
|
+
}
|
|
384
|
+
|
|
385
|
+
/**
|
|
386
|
+
* Logs which features are enabled.
|
|
387
|
+
* Features are enabled by default when their config block is present,
|
|
388
|
+
* unless explicitly disabled with enabled: false.
|
|
389
|
+
*/
|
|
390
|
+
private static logEnabledFeatures(config: IBetterAuth): void {
|
|
391
|
+
const features: string[] = [];
|
|
392
|
+
|
|
393
|
+
// Plugins are enabled by default when config block is present
|
|
394
|
+
if (config.jwt && config.jwt.enabled !== false) {
|
|
395
|
+
features.push('JWT');
|
|
396
|
+
}
|
|
397
|
+
if (config.twoFactor && config.twoFactor.enabled !== false) {
|
|
398
|
+
features.push('2FA/TOTP');
|
|
399
|
+
}
|
|
400
|
+
if (config.passkey && config.passkey.enabled !== false) {
|
|
401
|
+
features.push('Passkey/WebAuthn');
|
|
402
|
+
}
|
|
403
|
+
if (config.legacyPassword && config.legacyPassword.enabled !== false) {
|
|
404
|
+
features.push('Legacy Password Handling');
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
// Dynamically collect enabled social providers
|
|
408
|
+
// Providers are enabled by default if they have credentials configured
|
|
409
|
+
// Only disabled when explicitly set to enabled: false
|
|
410
|
+
const socialProviders: string[] = [];
|
|
411
|
+
if (config.socialProviders) {
|
|
412
|
+
for (const [name, provider] of Object.entries(config.socialProviders)) {
|
|
413
|
+
if (provider?.clientId && provider?.clientSecret && provider?.enabled !== false) {
|
|
414
|
+
// Capitalize first letter for display
|
|
415
|
+
socialProviders.push(name.charAt(0).toUpperCase() + name.slice(1));
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
if (socialProviders.length > 0) {
|
|
421
|
+
features.push(`Social Login (${socialProviders.join(', ')})`);
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
// Rate limiting still requires explicit enabled: true
|
|
425
|
+
if (config.rateLimit?.enabled) {
|
|
426
|
+
features.push(`Rate Limiting (${config.rateLimit.max || 10}/${config.rateLimit.windowSeconds || 60}s)`);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (features.length > 0) {
|
|
430
|
+
this.logger.log(`Enabled features: ${features.join(', ')}`);
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
}
|