@lenne.tech/nest-server 11.10.3 → 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/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/core-better-auth.module.d.ts +1 -0
- package/dist/core/modules/better-auth/core-better-auth.module.js +21 -8
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core.module.js +7 -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/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- 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 +3 -0
- package/src/core/modules/better-auth/core-better-auth.module.ts +38 -13
- package/src/core.module.ts +16 -3
- package/src/index.ts +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.10.
|
|
3
|
+
"version": "11.10.4",
|
|
4
4
|
"description": "Modern, fast, powerful Node.js web framework in TypeScript based on Nest with a GraphQL API and a connection to MongoDB (or other databases).",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"node",
|
|
@@ -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
|
+
}
|
|
@@ -101,6 +101,7 @@ The `CoreBetterAuthUserMapper` enables bidirectional password synchronization:
|
|
|
101
101
|
BetterAuthModule.forRoot({
|
|
102
102
|
config: envConfig.betterAuth,
|
|
103
103
|
fallbackSecrets: [envConfig.jwt?.secret],
|
|
104
|
+
// registerRolesGuardGlobally defaults to true - @Roles() decorators work automatically!
|
|
104
105
|
}),
|
|
105
106
|
// ... other modules
|
|
106
107
|
],
|
|
@@ -108,6 +109,8 @@ The `CoreBetterAuthUserMapper` enables bidirectional password synchronization:
|
|
|
108
109
|
export class ServerModule {}
|
|
109
110
|
```
|
|
110
111
|
|
|
112
|
+
**Note:** Since 11.10.3, `registerRolesGuardGlobally` defaults to `true`. You don't need to set it explicitly.
|
|
113
|
+
|
|
111
114
|
#### For Existing Projects (Migration):
|
|
112
115
|
```typescript
|
|
113
116
|
@Module({
|
|
@@ -15,6 +15,7 @@ import mongoose, { Connection } from 'mongoose';
|
|
|
15
15
|
|
|
16
16
|
import { IBetterAuth } from '../../common/interfaces/server-options.interface';
|
|
17
17
|
import { ConfigService } from '../../common/services/config.service';
|
|
18
|
+
import { RolesGuardRegistry } from '../auth/guards/roles-guard-registry';
|
|
18
19
|
import { RolesGuard } from '../auth/guards/roles.guard';
|
|
19
20
|
import { BetterAuthTokenService } from './better-auth-token.service';
|
|
20
21
|
import { BetterAuthInstance, createBetterAuthInstance } from './better-auth.config';
|
|
@@ -87,13 +88,14 @@ export interface CoreBetterAuthModuleOptions {
|
|
|
87
88
|
/**
|
|
88
89
|
* Register RolesGuard as a global guard.
|
|
89
90
|
*
|
|
90
|
-
* This should be set to `true` for IAM-only setups (CoreModule.forRoot with 1 parameter)
|
|
91
|
-
* where CoreAuthModule is not imported (which normally registers RolesGuard globally).
|
|
92
|
-
*
|
|
93
91
|
* When `true`, all `@Roles()` decorators will be enforced automatically without
|
|
94
92
|
* needing explicit `@UseGuards(RolesGuard)` on each endpoint.
|
|
95
93
|
*
|
|
96
|
-
*
|
|
94
|
+
* **Important:** This should be `false` in Legacy mode (3-parameter CoreModule.forRoot)
|
|
95
|
+
* because CoreAuthModule already registers RolesGuard globally. Setting it to `true`
|
|
96
|
+
* in Legacy mode would cause duplicate guard registration.
|
|
97
|
+
*
|
|
98
|
+
* @default true (secure by default - ensures @Roles() decorators are enforced)
|
|
97
99
|
*/
|
|
98
100
|
registerRolesGuardGlobally?: boolean;
|
|
99
101
|
|
|
@@ -218,6 +220,8 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
218
220
|
private static customController: null | Type<CoreBetterAuthController> = null;
|
|
219
221
|
private static customResolver: null | Type<CoreBetterAuthResolver> = null;
|
|
220
222
|
private static shouldRegisterRolesGuardGlobally = false;
|
|
223
|
+
// Track if registerRolesGuardGlobally was explicitly set to false (for warning)
|
|
224
|
+
private static rolesGuardExplicitlyDisabled = false;
|
|
221
225
|
|
|
222
226
|
/**
|
|
223
227
|
* Gets the controller class to use (custom or default)
|
|
@@ -248,6 +252,16 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
248
252
|
if (this.rateLimiter && CoreBetterAuthModule.currentConfig?.rateLimit) {
|
|
249
253
|
this.rateLimiter.configure(CoreBetterAuthModule.currentConfig.rateLimit);
|
|
250
254
|
}
|
|
255
|
+
|
|
256
|
+
// Security warning: Check if RolesGuard is registered when explicitly disabled
|
|
257
|
+
// This warning helps developers identify potential security misconfigurations
|
|
258
|
+
if (CoreBetterAuthModule.rolesGuardExplicitlyDisabled && !RolesGuardRegistry.isRegistered()) {
|
|
259
|
+
CoreBetterAuthModule.logger.warn(
|
|
260
|
+
'⚠️ SECURITY WARNING: registerRolesGuardGlobally is explicitly set to false, ' +
|
|
261
|
+
'but no RolesGuard is registered globally. @Roles() decorators will NOT enforce access control! ' +
|
|
262
|
+
'Either set registerRolesGuardGlobally: true, or ensure CoreAuthModule (Legacy) is imported.',
|
|
263
|
+
);
|
|
264
|
+
}
|
|
251
265
|
}
|
|
252
266
|
|
|
253
267
|
/**
|
|
@@ -357,8 +371,12 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
357
371
|
this.customController = controller || null;
|
|
358
372
|
// Store custom resolver if provided
|
|
359
373
|
this.customResolver = resolver || null;
|
|
360
|
-
// Store whether to register RolesGuard globally
|
|
361
|
-
|
|
374
|
+
// Store whether to register RolesGuard globally
|
|
375
|
+
// Default is true (secure by default) - ensures @Roles() decorators are enforced
|
|
376
|
+
// CoreModule.forRoot sets this to false in Legacy mode (where CoreAuthModule handles it)
|
|
377
|
+
this.shouldRegisterRolesGuardGlobally = registerRolesGuardGlobally ?? true;
|
|
378
|
+
// Track if explicitly disabled (for security warning in onModuleInit)
|
|
379
|
+
this.rolesGuardExplicitlyDisabled = registerRolesGuardGlobally === false;
|
|
362
380
|
|
|
363
381
|
// If better-auth is disabled (config is null or enabled: false), return minimal module
|
|
364
382
|
// Note: We don't provide middleware classes when disabled because they depend on CoreBetterAuthService
|
|
@@ -526,6 +544,9 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
526
544
|
this.customController = null;
|
|
527
545
|
this.customResolver = null;
|
|
528
546
|
this.shouldRegisterRolesGuardGlobally = false;
|
|
547
|
+
this.rolesGuardExplicitlyDisabled = false;
|
|
548
|
+
// Reset shared RolesGuard registry (shared with CoreAuthModule)
|
|
549
|
+
RolesGuardRegistry.reset();
|
|
529
550
|
}
|
|
530
551
|
|
|
531
552
|
/**
|
|
@@ -627,13 +648,17 @@ export class CoreBetterAuthModule implements NestModule, OnModuleInit {
|
|
|
627
648
|
this.getResolverClass(),
|
|
628
649
|
// Conditionally register RolesGuard globally for IAM-only setups
|
|
629
650
|
// In Legacy mode, RolesGuard is already registered globally via CoreAuthModule
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
651
|
+
// Uses shared RolesGuardRegistry to prevent duplicate registration across modules
|
|
652
|
+
...(this.shouldRegisterRolesGuardGlobally && !RolesGuardRegistry.isRegistered()
|
|
653
|
+
? (() => {
|
|
654
|
+
RolesGuardRegistry.markRegistered('CoreBetterAuthModule');
|
|
655
|
+
return [
|
|
656
|
+
{
|
|
657
|
+
provide: APP_GUARD,
|
|
658
|
+
useClass: RolesGuard,
|
|
659
|
+
},
|
|
660
|
+
];
|
|
661
|
+
})()
|
|
637
662
|
: []),
|
|
638
663
|
],
|
|
639
664
|
};
|
package/src/core.module.ts
CHANGED
|
@@ -247,12 +247,25 @@ export class CoreModule implements NestModule {
|
|
|
247
247
|
}
|
|
248
248
|
|
|
249
249
|
// Add CoreBetterAuthModule based on mode
|
|
250
|
-
// IAM-only mode:
|
|
250
|
+
// IAM-only mode: BetterAuth is enabled by default (it's the only auth option)
|
|
251
251
|
// Legacy mode: Only register if autoRegister is explicitly true
|
|
252
252
|
// betterAuth can be: boolean | IBetterAuth | undefined
|
|
253
253
|
const betterAuthConfig = config.betterAuth;
|
|
254
|
-
|
|
255
|
-
|
|
254
|
+
|
|
255
|
+
// Determine if BetterAuth is explicitly disabled
|
|
256
|
+
// In IAM-only mode: enabled by default (undefined = true), only false or { enabled: false } disables
|
|
257
|
+
// In Legacy mode: disabled by default (undefined = false), must be explicitly enabled
|
|
258
|
+
const isExplicitlyDisabled = betterAuthConfig === false ||
|
|
259
|
+
(typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled === false);
|
|
260
|
+
const isExplicitlyEnabled = betterAuthConfig === true ||
|
|
261
|
+
(typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled !== false);
|
|
262
|
+
|
|
263
|
+
// IAM-only mode: enabled unless explicitly disabled
|
|
264
|
+
// Legacy mode: enabled only if explicitly enabled
|
|
265
|
+
const isBetterAuthEnabled = isIamOnlyMode
|
|
266
|
+
? !isExplicitlyDisabled
|
|
267
|
+
: isExplicitlyEnabled;
|
|
268
|
+
|
|
256
269
|
const isAutoRegister = typeof betterAuthConfig === 'object' && betterAuthConfig?.autoRegister === true;
|
|
257
270
|
|
|
258
271
|
if (isBetterAuthEnabled) {
|
package/src/index.ts
CHANGED
|
@@ -112,6 +112,7 @@ export * from './core/modules/auth/exceptions/invalid-token.exception';
|
|
|
112
112
|
export * from './core/modules/auth/exceptions/legacy-auth-disabled.exception';
|
|
113
113
|
export * from './core/modules/auth/guards/auth.guard';
|
|
114
114
|
export * from './core/modules/auth/guards/legacy-auth-rate-limit.guard';
|
|
115
|
+
export * from './core/modules/auth/guards/roles-guard-registry';
|
|
115
116
|
export * from './core/modules/auth/guards/roles.guard';
|
|
116
117
|
export * from './core/modules/auth/inputs/core-auth-sign-in.input';
|
|
117
118
|
export * from './core/modules/auth/inputs/core-auth-sign-up.input';
|