@lenne.tech/nest-server 11.10.2 → 11.10.4
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 +16 -133
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +4 -0
- package/dist/core/modules/auth/core-auth.module.js +8 -4
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/guards/roles-guard-registry.d.ts +9 -0
- package/dist/core/modules/auth/guards/roles-guard-registry.js +30 -0
- package/dist/core/modules/auth/guards/roles-guard-registry.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth.config.d.ts +3 -0
- package/dist/core/modules/better-auth/better-auth.config.js +176 -47
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.d.ts +5 -1
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +101 -8
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-challenge.service.d.ts +20 -0
- package/dist/core/modules/better-auth/core-better-auth-challenge.service.js +142 -0
- package/dist/core/modules/better-auth/core-better-auth-challenge.service.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-user.mapper.js +1 -1
- 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 +2 -0
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +29 -1
- 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.js +5 -13
- 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 +6 -19
- 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 +5 -1
- package/dist/core/modules/better-auth/core-better-auth.module.js +74 -27
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +7 -6
- 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 +0 -2
- package/dist/core/modules/better-auth/core-better-auth.service.js +23 -37
- package/dist/core/modules/better-auth/core-better-auth.service.js.map +1 -1
- package/dist/core.module.js +10 -1
- 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/server/modules/better-auth/better-auth.module.d.ts +4 -1
- package/dist/server/modules/better-auth/better-auth.module.js +4 -1
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/server.module.js +1 -4
- package/dist/server/server.module.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config.env.ts +24 -174
- package/src/core/common/interfaces/server-options.interface.ts +288 -35
- package/src/core/modules/auth/core-auth.module.ts +11 -5
- package/src/core/modules/auth/guards/roles-guard-registry.ts +57 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +85 -56
- package/src/core/modules/better-auth/README.md +132 -35
- package/src/core/modules/better-auth/better-auth.config.ts +402 -70
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +158 -18
- package/src/core/modules/better-auth/core-better-auth-challenge.service.ts +254 -0
- package/src/core/modules/better-auth/core-better-auth-user.mapper.ts +1 -1
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +64 -1
- package/src/core/modules/better-auth/core-better-auth.controller.ts +6 -14
- package/src/core/modules/better-auth/core-better-auth.middleware.ts +7 -20
- package/src/core/modules/better-auth/core-better-auth.module.ts +173 -38
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +7 -6
- package/src/core/modules/better-auth/core-better-auth.service.ts +27 -48
- package/src/core.module.ts +21 -3
- package/src/index.ts +1 -0
- package/src/server/modules/better-auth/better-auth.module.ts +40 -10
- package/src/server/server.module.ts +2 -4
|
@@ -132,7 +132,15 @@ export interface IAuthLegacyEndpoints {
|
|
|
132
132
|
*
|
|
133
133
|
* Check migration status via the `betterAuthMigrationStatus` query.
|
|
134
134
|
*
|
|
135
|
+
* **Environment Variable:** `LEGACY_AUTH_ENABLED`
|
|
136
|
+
*
|
|
135
137
|
* @default true
|
|
138
|
+
*
|
|
139
|
+
* @example
|
|
140
|
+
* ```typescript
|
|
141
|
+
* // Via environment variable
|
|
142
|
+
* enabled: process.env.LEGACY_AUTH_ENABLED !== 'false',
|
|
143
|
+
* ```
|
|
136
144
|
*/
|
|
137
145
|
enabled?: boolean;
|
|
138
146
|
|
|
@@ -225,11 +233,15 @@ export type IBetterAuth = IBetterAuthWithoutPasskey | IBetterAuthWithPasskey;
|
|
|
225
233
|
|
|
226
234
|
/**
|
|
227
235
|
* JWT plugin configuration for Better-Auth
|
|
236
|
+
*
|
|
237
|
+
* **Enabled by Default:** JWT is enabled by default when BetterAuth is active.
|
|
238
|
+
* This provides stateless authentication for API clients.
|
|
239
|
+
* Set `jwt: false` or `jwt: { enabled: false }` to disable.
|
|
228
240
|
*/
|
|
229
241
|
export interface IBetterAuthJwtConfig {
|
|
230
242
|
/**
|
|
231
243
|
* Whether JWT plugin is enabled.
|
|
232
|
-
* @default true (when
|
|
244
|
+
* @default true (enabled by default when BetterAuth is active)
|
|
233
245
|
*/
|
|
234
246
|
enabled?: boolean;
|
|
235
247
|
|
|
@@ -242,7 +254,36 @@ export interface IBetterAuthJwtConfig {
|
|
|
242
254
|
|
|
243
255
|
/**
|
|
244
256
|
* Passkey/WebAuthn plugin configuration for Better-Auth
|
|
257
|
+
*
|
|
258
|
+
* **Auto-Detection from baseUrl:** When `passkey: true` is set and `baseUrl` is configured,
|
|
259
|
+
* the following values are auto-detected:
|
|
260
|
+
* - `rpId`: Derived from baseUrl hostname (e.g., 'example.com')
|
|
261
|
+
* - `origin`: Derived from baseUrl (e.g., 'https://api.example.com')
|
|
262
|
+
* - `trustedOrigins`: Derived from baseUrl (e.g., ['https://api.example.com'])
|
|
263
|
+
*
|
|
264
|
+
* **Graceful Degradation:** If auto-detection fails and values are not explicitly set,
|
|
265
|
+
* Passkey is automatically disabled with a warning. Other auth methods continue to work.
|
|
266
|
+
*
|
|
245
267
|
* @see https://www.better-auth.com/docs/plugins/passkey
|
|
268
|
+
*
|
|
269
|
+
* @example
|
|
270
|
+
* ```typescript
|
|
271
|
+
* // RECOMMENDED: Use auto-detection
|
|
272
|
+
* betterAuth: {
|
|
273
|
+
* baseUrl: process.env.BASE_URL, // e.g., 'https://api.example.com'
|
|
274
|
+
* passkey: true, // Auto-detects rpId, origin, trustedOrigins
|
|
275
|
+
* }
|
|
276
|
+
*
|
|
277
|
+
* // Explicit configuration (overrides auto-detection)
|
|
278
|
+
* betterAuth: {
|
|
279
|
+
* passkey: {
|
|
280
|
+
* rpId: 'example.com',
|
|
281
|
+
* origin: 'https://app.example.com',
|
|
282
|
+
* rpName: 'My App',
|
|
283
|
+
* },
|
|
284
|
+
* trustedOrigins: ['https://app.example.com'],
|
|
285
|
+
* }
|
|
286
|
+
* ```
|
|
246
287
|
*/
|
|
247
288
|
export interface IBetterAuthPasskeyConfig {
|
|
248
289
|
/**
|
|
@@ -254,6 +295,27 @@ export interface IBetterAuthPasskeyConfig {
|
|
|
254
295
|
*/
|
|
255
296
|
authenticatorAttachment?: 'cross-platform' | 'platform';
|
|
256
297
|
|
|
298
|
+
/**
|
|
299
|
+
* Where to store WebAuthn challenges.
|
|
300
|
+
* - 'database': Store in MongoDB with TTL (default, works everywhere including cross-origin and JWT mode)
|
|
301
|
+
* - 'cookie': Store in httpOnly cookie (requires session cookies and same-origin setup)
|
|
302
|
+
*
|
|
303
|
+
* Use 'cookie' only when:
|
|
304
|
+
* - You want to avoid database writes for challenges
|
|
305
|
+
* - You have a same-origin setup (frontend and API on same domain/port)
|
|
306
|
+
* - You are NOT using JWT mode
|
|
307
|
+
*
|
|
308
|
+
* @default 'database'
|
|
309
|
+
*/
|
|
310
|
+
challengeStorage?: 'cookie' | 'database';
|
|
311
|
+
|
|
312
|
+
/**
|
|
313
|
+
* TTL in seconds for database-stored challenges.
|
|
314
|
+
* Only used when challengeStorage is 'database'.
|
|
315
|
+
* @default 300 (5 minutes)
|
|
316
|
+
*/
|
|
317
|
+
challengeTtlSeconds?: number;
|
|
318
|
+
|
|
257
319
|
/**
|
|
258
320
|
* Whether passkey authentication is enabled.
|
|
259
321
|
* @default true (when config block is present)
|
|
@@ -261,8 +323,11 @@ export interface IBetterAuthPasskeyConfig {
|
|
|
261
323
|
enabled?: boolean;
|
|
262
324
|
|
|
263
325
|
/**
|
|
264
|
-
* Origin URL for WebAuthn
|
|
265
|
-
*
|
|
326
|
+
* Origin URL for WebAuthn.
|
|
327
|
+
*
|
|
328
|
+
* **Auto-detected** from `baseUrl` if not set (e.g., 'https://api.example.com').
|
|
329
|
+
*
|
|
330
|
+
* @example 'http://localhost:3000' or 'https://api.example.com'
|
|
266
331
|
*/
|
|
267
332
|
origin?: string;
|
|
268
333
|
|
|
@@ -276,8 +341,11 @@ export interface IBetterAuthPasskeyConfig {
|
|
|
276
341
|
residentKey?: 'discouraged' | 'preferred' | 'required';
|
|
277
342
|
|
|
278
343
|
/**
|
|
279
|
-
* Relying Party ID (usually the domain)
|
|
280
|
-
*
|
|
344
|
+
* Relying Party ID (usually the domain without protocol).
|
|
345
|
+
*
|
|
346
|
+
* **Auto-detected** from `baseUrl` hostname if not set (e.g., 'example.com').
|
|
347
|
+
*
|
|
348
|
+
* @example 'localhost' or 'example.com'
|
|
281
349
|
*/
|
|
282
350
|
rpId?: string;
|
|
283
351
|
|
|
@@ -298,6 +366,7 @@ export interface IBetterAuthPasskeyConfig {
|
|
|
298
366
|
|
|
299
367
|
/**
|
|
300
368
|
* Custom cookie name for WebAuthn challenge storage.
|
|
369
|
+
* Only used when challengeStorage is 'cookie'.
|
|
301
370
|
* @default 'better-auth-passkey'
|
|
302
371
|
*/
|
|
303
372
|
webAuthnChallengeCookie?: string;
|
|
@@ -305,17 +374,30 @@ export interface IBetterAuthPasskeyConfig {
|
|
|
305
374
|
|
|
306
375
|
/**
|
|
307
376
|
* Interface for Better-Auth rate limiting configuration
|
|
377
|
+
*
|
|
378
|
+
* **Environment Variables:**
|
|
379
|
+
* - `RATE_LIMIT_ENABLED` - Set to 'false' to disable
|
|
380
|
+
* - `RATE_LIMIT_MAX` - Maximum requests per window
|
|
381
|
+
* - `RATE_LIMIT_WINDOW_SECONDS` - Window duration
|
|
382
|
+
*
|
|
383
|
+
* @example
|
|
384
|
+
* ```typescript
|
|
385
|
+
* rateLimit: {
|
|
386
|
+
* enabled: process.env.RATE_LIMIT_ENABLED !== 'false',
|
|
387
|
+
* max: parseInt(process.env.RATE_LIMIT_MAX || '10', 10),
|
|
388
|
+
* },
|
|
389
|
+
* ```
|
|
308
390
|
*/
|
|
309
391
|
export interface IBetterAuthRateLimit {
|
|
310
392
|
/**
|
|
311
393
|
* Whether rate limiting is enabled
|
|
312
|
-
* default
|
|
394
|
+
* @default false
|
|
313
395
|
*/
|
|
314
396
|
enabled?: boolean;
|
|
315
397
|
|
|
316
398
|
/**
|
|
317
399
|
* Maximum number of requests within the time window
|
|
318
|
-
* default
|
|
400
|
+
* @default 10
|
|
319
401
|
*/
|
|
320
402
|
max?: number;
|
|
321
403
|
|
|
@@ -351,10 +433,18 @@ export interface IBetterAuthRateLimit {
|
|
|
351
433
|
* both `clientId` and `clientSecret` are provided. You only need to set
|
|
352
434
|
* `enabled: false` to explicitly disable a configured provider.
|
|
353
435
|
*
|
|
436
|
+
* **Environment Variables (convention):**
|
|
437
|
+
* - `SOCIAL_GOOGLE_CLIENT_ID`, `SOCIAL_GOOGLE_CLIENT_SECRET`
|
|
438
|
+
* - `SOCIAL_GITHUB_CLIENT_ID`, `SOCIAL_GITHUB_CLIENT_SECRET`
|
|
439
|
+
* - `SOCIAL_APPLE_CLIENT_ID`, `SOCIAL_APPLE_CLIENT_SECRET`
|
|
440
|
+
*
|
|
354
441
|
* @example
|
|
355
442
|
* ```typescript
|
|
356
|
-
* //
|
|
357
|
-
* google: {
|
|
443
|
+
* // Via environment variables (recommended)
|
|
444
|
+
* google: {
|
|
445
|
+
* clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '',
|
|
446
|
+
* clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '',
|
|
447
|
+
* },
|
|
358
448
|
*
|
|
359
449
|
* // Provider is explicitly disabled despite having credentials
|
|
360
450
|
* github: { clientId: '...', clientSecret: '...', enabled: false }
|
|
@@ -382,17 +472,30 @@ export interface IBetterAuthSocialProvider {
|
|
|
382
472
|
|
|
383
473
|
/**
|
|
384
474
|
* Two-factor authentication plugin configuration for Better-Auth
|
|
475
|
+
*
|
|
476
|
+
* **Enabled by Default:** 2FA is enabled by default when BetterAuth is active.
|
|
477
|
+
* Users can optionally set up 2FA for their accounts.
|
|
478
|
+
* Set `twoFactor: false` or `twoFactor: { enabled: false }` to disable.
|
|
479
|
+
*
|
|
480
|
+
* **Environment Variables:**
|
|
481
|
+
* - `TWO_FACTOR_APP_NAME` - App name shown in authenticator apps
|
|
482
|
+
* - `TWO_FACTOR_ENABLED` - Set to 'true' to enable (default: true)
|
|
385
483
|
*/
|
|
386
484
|
export interface IBetterAuthTwoFactorConfig {
|
|
387
485
|
/**
|
|
388
|
-
* App name shown in authenticator apps
|
|
389
|
-
*
|
|
486
|
+
* App name shown in authenticator apps.
|
|
487
|
+
* This appears in Google Authenticator, Authy, etc.
|
|
488
|
+
*
|
|
489
|
+
* **Environment Variable:** `TWO_FACTOR_APP_NAME`
|
|
490
|
+
*
|
|
491
|
+
* @default 'Nest Server'
|
|
492
|
+
* @example 'My Application'
|
|
390
493
|
*/
|
|
391
494
|
appName?: string;
|
|
392
495
|
|
|
393
496
|
/**
|
|
394
497
|
* Whether 2FA is enabled.
|
|
395
|
-
* @default true (when
|
|
498
|
+
* @default true (enabled by default when BetterAuth is active)
|
|
396
499
|
*/
|
|
397
500
|
enabled?: boolean;
|
|
398
501
|
}
|
|
@@ -533,6 +636,44 @@ export interface IJwt {
|
|
|
533
636
|
* Options for the server
|
|
534
637
|
*/
|
|
535
638
|
export interface IServerOptions {
|
|
639
|
+
/**
|
|
640
|
+
* Base URL of the frontend/app application.
|
|
641
|
+
*
|
|
642
|
+
* Used for:
|
|
643
|
+
* - CORS `trustedOrigins` configuration
|
|
644
|
+
* - Passkey/WebAuthn `origin` (where the browser runs)
|
|
645
|
+
* - Frontend redirect URLs
|
|
646
|
+
*
|
|
647
|
+
* **Auto-Detection from `baseUrl`:**
|
|
648
|
+
* If not set, `appUrl` is derived from `baseUrl`:
|
|
649
|
+
* - `https://api.example.com` → `https://example.com` (removes 'api.' prefix)
|
|
650
|
+
* - `https://example.com` → `https://example.com` (unchanged)
|
|
651
|
+
*
|
|
652
|
+
* **Localhost Environment Defaults:**
|
|
653
|
+
* When `env` is 'local', 'ci', or 'e2e' and neither `baseUrl` nor `appUrl` is set:
|
|
654
|
+
* - `appUrl` defaults to `http://localhost:3001`
|
|
655
|
+
*
|
|
656
|
+
* **Environment Variable:** `APP_URL` (only needed if not auto-derivable from `BASE_URL`)
|
|
657
|
+
*
|
|
658
|
+
* @example 'https://example.com' or 'http://localhost:3001'
|
|
659
|
+
*
|
|
660
|
+
* @example
|
|
661
|
+
* ```typescript
|
|
662
|
+
* // Typical production setup (appUrl auto-derived from baseUrl)
|
|
663
|
+
* baseUrl: process.env.BASE_URL, // e.g., 'https://api.example.com'
|
|
664
|
+
* // → appUrl auto-derived: 'https://example.com'
|
|
665
|
+
*
|
|
666
|
+
* // Explicit appUrl (when frontend is on different domain)
|
|
667
|
+
* appUrl: process.env.APP_URL, // e.g., 'https://app.different-domain.com'
|
|
668
|
+
*
|
|
669
|
+
* // Local/CI/E2E (auto-defaults)
|
|
670
|
+
* env: 'local', // or 'ci' or 'e2e'
|
|
671
|
+
* // baseUrl defaults to 'http://localhost:3000'
|
|
672
|
+
* // appUrl defaults to 'http://localhost:3001'
|
|
673
|
+
* ```
|
|
674
|
+
*/
|
|
675
|
+
appUrl?: string;
|
|
676
|
+
|
|
536
677
|
/**
|
|
537
678
|
* Authentication system configuration
|
|
538
679
|
*
|
|
@@ -553,21 +694,87 @@ export interface IServerOptions {
|
|
|
553
694
|
*/
|
|
554
695
|
automaticObjectIdFiltering?: boolean;
|
|
555
696
|
|
|
697
|
+
/**
|
|
698
|
+
* Base URL of the API server.
|
|
699
|
+
*
|
|
700
|
+
* Used for:
|
|
701
|
+
* - Email links (password reset, verification)
|
|
702
|
+
* - OAuth callback URLs
|
|
703
|
+
* - Swagger/OpenAPI documentation
|
|
704
|
+
* - BetterAuth configuration
|
|
705
|
+
*
|
|
706
|
+
* **Localhost Environment Defaults:**
|
|
707
|
+
* When `env` is 'local', 'ci', or 'e2e' and `baseUrl` is not set:
|
|
708
|
+
* - `baseUrl` defaults to `http://localhost:3000`
|
|
709
|
+
*
|
|
710
|
+
* **Relationship with `appUrl`:**
|
|
711
|
+
* If `appUrl` is not set, it is auto-derived from `baseUrl`:
|
|
712
|
+
* - `https://api.example.com` → `appUrl: https://example.com`
|
|
713
|
+
* - `https://example.com` → `appUrl: https://example.com`
|
|
714
|
+
*
|
|
715
|
+
* **Environment Variable:** `BASE_URL`
|
|
716
|
+
*
|
|
717
|
+
* @example 'https://api.example.com' or 'http://localhost:3000'
|
|
718
|
+
*
|
|
719
|
+
* @example
|
|
720
|
+
* ```typescript
|
|
721
|
+
* // Production (via environment variable)
|
|
722
|
+
* baseUrl: process.env.BASE_URL,
|
|
723
|
+
*
|
|
724
|
+
* // Local/CI/E2E (auto-defaults)
|
|
725
|
+
* env: 'local', // or 'ci' or 'e2e'
|
|
726
|
+
* // baseUrl defaults to 'http://localhost:3000'
|
|
727
|
+
* ```
|
|
728
|
+
*/
|
|
729
|
+
baseUrl?: string;
|
|
730
|
+
|
|
556
731
|
/**
|
|
557
732
|
* Configuration for better-auth authentication framework.
|
|
558
733
|
* See: https://better-auth.com
|
|
559
734
|
*
|
|
735
|
+
* **Zero-Config Philosophy:** BetterAuth is enabled by default.
|
|
736
|
+
* JWT, 2FA, and Passkey are also enabled by default when BetterAuth is active.
|
|
737
|
+
*
|
|
738
|
+
* **Passkey Auto-Activation:**
|
|
739
|
+
* Passkey is automatically enabled when `baseUrl` (or `appUrl`) is configured.
|
|
740
|
+
* If URLs are not set, Passkey is disabled with a warning (Graceful Degradation).
|
|
741
|
+
*
|
|
560
742
|
* Accepts:
|
|
561
|
-
* - `
|
|
743
|
+
* - `undefined`: Enabled with defaults (zero-config)
|
|
744
|
+
* - `true`: Enable with all defaults (same as undefined)
|
|
562
745
|
* - `false`: Disable BetterAuth completely
|
|
563
746
|
* - `{ ... }`: Enable with custom configuration
|
|
564
|
-
* - `
|
|
747
|
+
* - `{ enabled: false }`: Disable BetterAuth completely
|
|
748
|
+
*
|
|
749
|
+
* | Configuration | BetterAuth | JWT | 2FA | Passkey |
|
|
750
|
+
* |---------------|:----------:|:---:|:---:|:-------:|
|
|
751
|
+
* | *not set* + no URLs | ✅ | ✅ | ✅ | ⚠️ disabled |
|
|
752
|
+
* | *not set* + `baseUrl` set | ✅ | ✅ | ✅ | ✅ auto |
|
|
753
|
+
* | `env: 'local'/'ci'/'e2e'` (auto URLs) | ✅ | ✅ | ✅ | ✅ auto |
|
|
754
|
+
* | `false` | ❌ | ❌ | ❌ | ❌ |
|
|
755
|
+
* | `{ passkey: false }` | ✅ | ✅ | ✅ | ❌ |
|
|
756
|
+
* | `{ twoFactor: false }` | ✅ | ✅ | ❌ | ✅ auto |
|
|
757
|
+
*
|
|
758
|
+
* @default undefined (enabled with defaults)
|
|
565
759
|
*
|
|
566
760
|
* @example
|
|
567
761
|
* ```typescript
|
|
568
|
-
*
|
|
569
|
-
*
|
|
570
|
-
*
|
|
762
|
+
* // Zero-config for local/ci/e2e:
|
|
763
|
+
* env: 'local', // or 'ci' or 'e2e'
|
|
764
|
+
* // → baseUrl: 'http://localhost:3000' (auto)
|
|
765
|
+
* // → appUrl: 'http://localhost:3001' (auto)
|
|
766
|
+
* // → BetterAuth + JWT + 2FA + Passkey all enabled!
|
|
767
|
+
*
|
|
768
|
+
* // Production with auto-derived appUrl:
|
|
769
|
+
* baseUrl: 'https://api.example.com',
|
|
770
|
+
* // → appUrl: 'https://example.com' (auto-derived)
|
|
771
|
+
* // → Passkey uses appUrl for origin/trustedOrigins
|
|
772
|
+
*
|
|
773
|
+
* // Disable Passkey explicitly (no warning):
|
|
774
|
+
* betterAuth: { passkey: false },
|
|
775
|
+
*
|
|
776
|
+
* // Disable BetterAuth completely:
|
|
777
|
+
* betterAuth: false,
|
|
571
778
|
* ```
|
|
572
779
|
*/
|
|
573
780
|
betterAuth?: boolean | IBetterAuth;
|
|
@@ -1293,28 +1500,31 @@ interface IBetterAuthBase {
|
|
|
1293
1500
|
|
|
1294
1501
|
/**
|
|
1295
1502
|
* Whether better-auth is enabled.
|
|
1296
|
-
*
|
|
1297
|
-
*
|
|
1298
|
-
*
|
|
1503
|
+
*
|
|
1504
|
+
* **Zero-Config Philosophy:** BetterAuth is enabled by default.
|
|
1505
|
+
* Set to `false` to explicitly disable it.
|
|
1506
|
+
*
|
|
1507
|
+
* @default true (enabled by default)
|
|
1299
1508
|
*/
|
|
1300
1509
|
enabled?: boolean;
|
|
1301
1510
|
|
|
1302
1511
|
/**
|
|
1303
1512
|
* JWT plugin configuration for API clients.
|
|
1304
1513
|
*
|
|
1305
|
-
* **
|
|
1306
|
-
* This
|
|
1514
|
+
* **Enabled by Default:** JWT is enabled by default when BetterAuth is active.
|
|
1515
|
+
* This provides stateless authentication for API clients.
|
|
1307
1516
|
*
|
|
1308
1517
|
* Accepts:
|
|
1309
|
-
* - `
|
|
1518
|
+
* - `undefined`: Enabled with defaults (zero-config)
|
|
1519
|
+
* - `true` or `{}`: Enable with defaults (same as undefined)
|
|
1310
1520
|
* - `{ expiresIn: '1h' }`: Enable with custom settings
|
|
1311
1521
|
* - `false` or `{ enabled: false }`: Explicitly disable
|
|
1312
|
-
*
|
|
1522
|
+
*
|
|
1523
|
+
* @default undefined (enabled by default)
|
|
1313
1524
|
*
|
|
1314
1525
|
* @example
|
|
1315
1526
|
* ```typescript
|
|
1316
|
-
* // JWT
|
|
1317
|
-
* betterAuth: true,
|
|
1527
|
+
* // JWT enabled by default - no config needed
|
|
1318
1528
|
*
|
|
1319
1529
|
* // Customize JWT expiry
|
|
1320
1530
|
* betterAuth: { jwt: { expiresIn: '1h' } },
|
|
@@ -1378,8 +1588,46 @@ interface IBetterAuthBase {
|
|
|
1378
1588
|
rateLimit?: IBetterAuthRateLimit;
|
|
1379
1589
|
|
|
1380
1590
|
/**
|
|
1381
|
-
* Secret for better-auth
|
|
1382
|
-
*
|
|
1591
|
+
* Secret for better-auth session cookie signing.
|
|
1592
|
+
*
|
|
1593
|
+
* **Used for:**
|
|
1594
|
+
* - Session cookie integrity (HMAC-SHA256 signature)
|
|
1595
|
+
* - Cookie encryption (when using JWE strategy)
|
|
1596
|
+
*
|
|
1597
|
+
* **NOT used for:**
|
|
1598
|
+
* - JWT signing (JWT plugin generates asymmetric keys stored in `jwks` collection)
|
|
1599
|
+
* - Refresh tokens (Better-Auth uses DB sessions, not refresh JWTs)
|
|
1600
|
+
*
|
|
1601
|
+
* **REQUIRED for Production!** Without a persistent secret:
|
|
1602
|
+
* - Session cookies become invalid on server restart
|
|
1603
|
+
* - Sessions cannot be shared across cluster instances
|
|
1604
|
+
*
|
|
1605
|
+
* **Note:** Unlike Legacy Auth, Better-Auth sessions are stored in the database.
|
|
1606
|
+
* The secret only signs the session cookie, not the session itself.
|
|
1607
|
+
*
|
|
1608
|
+
* **Minimum:** 32 characters
|
|
1609
|
+
*
|
|
1610
|
+
* **Fallback Chain (nest-server implementation):**
|
|
1611
|
+
* 1. `betterAuth.secret` (if set)
|
|
1612
|
+
* 2. `jwt.secret` (if ≥32 chars, for backwards compatibility)
|
|
1613
|
+
* 3. `jwt.refresh.secret` (if ≥32 chars, for backwards compatibility)
|
|
1614
|
+
* 4. Auto-generated (with warning, not persistent!)
|
|
1615
|
+
*
|
|
1616
|
+
* **Environment Variable:** `BETTER_AUTH_SECRET`
|
|
1617
|
+
*
|
|
1618
|
+
* **Generate a secure secret:**
|
|
1619
|
+
* ```bash
|
|
1620
|
+
* node -e "console.log(require('crypto').randomBytes(32).toString('base64'))"
|
|
1621
|
+
* ```
|
|
1622
|
+
*
|
|
1623
|
+
* @example
|
|
1624
|
+
* ```typescript
|
|
1625
|
+
* // Via environment variable (recommended)
|
|
1626
|
+
* secret: process.env.BETTER_AUTH_SECRET,
|
|
1627
|
+
*
|
|
1628
|
+
* // Or direct value (not recommended for production)
|
|
1629
|
+
* secret: 'your-32-character-minimum-secret-here',
|
|
1630
|
+
* ```
|
|
1383
1631
|
*/
|
|
1384
1632
|
secret?: string;
|
|
1385
1633
|
|
|
@@ -1407,18 +1655,23 @@ interface IBetterAuthBase {
|
|
|
1407
1655
|
/**
|
|
1408
1656
|
* Two-factor authentication configuration.
|
|
1409
1657
|
*
|
|
1658
|
+
* **Enabled by Default:** 2FA is enabled by default when BetterAuth is active.
|
|
1659
|
+
* Users can optionally set up 2FA for their accounts.
|
|
1660
|
+
*
|
|
1410
1661
|
* Accepts:
|
|
1411
|
-
* - `
|
|
1662
|
+
* - `undefined`: Enabled with defaults (zero-config)
|
|
1663
|
+
* - `true` or `{}`: Enable with defaults (same as undefined)
|
|
1412
1664
|
* - `{ appName: 'My App' }`: Enable with custom settings
|
|
1413
|
-
* - `false` or `{ enabled: false }`:
|
|
1414
|
-
*
|
|
1665
|
+
* - `false` or `{ enabled: false }`: Explicitly disable
|
|
1666
|
+
*
|
|
1667
|
+
* @default undefined (enabled by default)
|
|
1415
1668
|
*
|
|
1416
1669
|
* @example
|
|
1417
1670
|
* ```typescript
|
|
1418
|
-
*
|
|
1419
|
-
*
|
|
1420
|
-
* twoFactor: { appName: 'My App' }, //
|
|
1421
|
-
* twoFactor: false, //
|
|
1671
|
+
* // 2FA enabled by default - no config needed
|
|
1672
|
+
*
|
|
1673
|
+
* twoFactor: { appName: 'My App' }, // Customize app name in authenticator
|
|
1674
|
+
* twoFactor: false, // Explicitly disable 2FA
|
|
1422
1675
|
* ```
|
|
1423
1676
|
*/
|
|
1424
1677
|
twoFactor?: boolean | IBetterAuthTwoFactorConfig;
|
|
@@ -6,6 +6,7 @@ import { PubSub } from 'graphql-subscriptions';
|
|
|
6
6
|
|
|
7
7
|
import { AuthGuardStrategy } from './auth-guard-strategy.enum';
|
|
8
8
|
import { LegacyAuthRateLimitGuard } from './guards/legacy-auth-rate-limit.guard';
|
|
9
|
+
import { RolesGuardRegistry } from './guards/roles-guard-registry';
|
|
9
10
|
import { RolesGuard } from './guards/roles.guard';
|
|
10
11
|
import { CoreAuthUserService } from './services/core-auth-user.service';
|
|
11
12
|
import { CoreAuthService } from './services/core-auth.service';
|
|
@@ -44,12 +45,17 @@ export class CoreAuthModule {
|
|
|
44
45
|
}
|
|
45
46
|
|
|
46
47
|
// Process providers
|
|
48
|
+
// Only register RolesGuard if not already registered (prevents duplicate with CoreBetterAuthModule)
|
|
49
|
+
const rolesGuardProvider = RolesGuardRegistry.isRegistered()
|
|
50
|
+
? []
|
|
51
|
+
: (() => {
|
|
52
|
+
RolesGuardRegistry.markRegistered('CoreAuthModule');
|
|
53
|
+
return [{ provide: APP_GUARD, useClass: RolesGuard }];
|
|
54
|
+
})();
|
|
55
|
+
|
|
47
56
|
let providers = [
|
|
48
|
-
// [Global] The
|
|
49
|
-
|
|
50
|
-
provide: APP_GUARD,
|
|
51
|
-
useClass: RolesGuard,
|
|
52
|
-
},
|
|
57
|
+
// [Global] The GraphQLAuthGuard integrates the user into context
|
|
58
|
+
...rolesGuardProvider,
|
|
53
59
|
{
|
|
54
60
|
provide: CoreAuthUserService,
|
|
55
61
|
useClass: UserService,
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Logger } from '@nestjs/common';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Global registry to track RolesGuard registration across modules.
|
|
5
|
+
*
|
|
6
|
+
* This prevents duplicate registration when both CoreAuthModule (Legacy)
|
|
7
|
+
* and CoreBetterAuthModule (IAM) are used in the same application.
|
|
8
|
+
*
|
|
9
|
+
* Multiple APP_GUARD registrations of the same guard would work but cause
|
|
10
|
+
* unnecessary double validation on every request.
|
|
11
|
+
*/
|
|
12
|
+
export class RolesGuardRegistry {
|
|
13
|
+
private static readonly logger = new Logger('RolesGuardRegistry');
|
|
14
|
+
private static registered = false;
|
|
15
|
+
private static registeredBy: null | string = null;
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if RolesGuard has already been registered as a global guard.
|
|
19
|
+
*/
|
|
20
|
+
static isRegistered(): boolean {
|
|
21
|
+
return this.registered;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Get which module registered the RolesGuard.
|
|
26
|
+
*/
|
|
27
|
+
static getRegisteredBy(): null | string {
|
|
28
|
+
return this.registeredBy;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Mark RolesGuard as registered by a specific module.
|
|
33
|
+
* Call this when adding RolesGuard as APP_GUARD.
|
|
34
|
+
*
|
|
35
|
+
* @param moduleName - Name of the module registering RolesGuard (for debugging)
|
|
36
|
+
*/
|
|
37
|
+
static markRegistered(moduleName: string = 'Unknown'): void {
|
|
38
|
+
if (this.registered) {
|
|
39
|
+
this.logger.debug(
|
|
40
|
+
`RolesGuard already registered by ${this.registeredBy}, skipping registration from ${moduleName}`,
|
|
41
|
+
);
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
this.registered = true;
|
|
45
|
+
this.registeredBy = moduleName;
|
|
46
|
+
this.logger.debug(`RolesGuard registered globally by ${moduleName}`);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Reset the registry state.
|
|
51
|
+
* Used in tests to ensure clean state between test runs.
|
|
52
|
+
*/
|
|
53
|
+
static reset(): void {
|
|
54
|
+
this.registered = false;
|
|
55
|
+
this.registeredBy = null;
|
|
56
|
+
}
|
|
57
|
+
}
|