@lenne.tech/nest-server 11.13.0 → 11.13.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 +12 -3
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +3 -0
- package/dist/core/modules/auth/core-auth.module.js +5 -2
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/guards/roles.guard.d.ts +2 -0
- package/dist/core/modules/auth/guards/roles.guard.js +24 -3
- package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
- package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth-roles.guard.d.ts +10 -0
- package/dist/core/modules/better-auth/better-auth-roles.guard.js +136 -0
- package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +3 -0
- package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +3 -1
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js +8 -6
- package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.middleware.js +55 -43
- 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 +13 -1
- package/dist/core/modules/better-auth/core-better-auth.module.js +56 -10
- package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core.module.d.ts +1 -0
- package/dist/core.module.js +82 -2
- package/dist/core.module.js.map +1 -1
- package/dist/server/modules/error-code/error-codes.d.ts +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +3 -11
- package/src/config.env.ts +12 -3
- package/src/core/common/interfaces/server-options.interface.ts +39 -0
- package/src/core/modules/auth/core-auth.module.ts +9 -3
- package/src/core/modules/auth/guards/roles.guard.ts +44 -6
- package/src/core/modules/better-auth/CUSTOMIZATION.md +520 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +15 -0
- package/src/core/modules/better-auth/README.md +24 -1
- package/src/core/modules/better-auth/better-auth-roles.guard.ts +205 -0
- package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +6 -0
- package/src/core/modules/better-auth/core-better-auth-web.helper.ts +14 -6
- package/src/core/modules/better-auth/core-better-auth.middleware.ts +91 -71
- package/src/core/modules/better-auth/core-better-auth.module.ts +97 -11
- package/src/core/modules/better-auth/index.ts +1 -0
- package/src/core.module.ts +120 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lenne.tech/nest-server",
|
|
3
|
-
"version": "11.13.
|
|
3
|
+
"version": "11.13.2",
|
|
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",
|
|
@@ -41,9 +41,7 @@
|
|
|
41
41
|
"reinit:force": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i --force && npm run test:e2e",
|
|
42
42
|
"reinit:legacy": "rimraf package-lock.json && rimraf node_modules && npm cache clean --force && npm i --legacy-peer-deps && npm run test:e2e",
|
|
43
43
|
"start": "npm run start:local",
|
|
44
|
-
"
|
|
45
|
-
"start:pm2": "./node_modules/.bin/grunt",
|
|
46
|
-
"start:prod": "./node_modules/.bin/grunt production",
|
|
44
|
+
"start:prod": "NODE_ENV=production node dist/main.js",
|
|
47
45
|
"start:nodemon": "ts-node -r tsconfig-paths/register src/main.ts",
|
|
48
46
|
"start:debug": "nodemon --config nodemon-debug.json",
|
|
49
47
|
"start:dev": "nodemon",
|
|
@@ -78,7 +76,7 @@
|
|
|
78
76
|
"node": ">= 20"
|
|
79
77
|
},
|
|
80
78
|
"dependencies": {
|
|
81
|
-
"@apollo/server": "5.
|
|
79
|
+
"@apollo/server": "5.4.0",
|
|
82
80
|
"@as-integrations/express5": "1.1.2",
|
|
83
81
|
"@better-auth/passkey": "1.4.17",
|
|
84
82
|
"@getbrevo/brevo": "3.0.1",
|
|
@@ -152,16 +150,10 @@
|
|
|
152
150
|
"eslint-config-prettier": "10.1.8",
|
|
153
151
|
"eslint-plugin-unused-imports": "4.3.0",
|
|
154
152
|
"find-file-up": "2.0.1",
|
|
155
|
-
"grunt": "1.6.1",
|
|
156
|
-
"grunt-bg-shell": "2.3.3",
|
|
157
|
-
"grunt-contrib-clean": "2.0.1",
|
|
158
|
-
"grunt-contrib-watch": "1.1.0",
|
|
159
|
-
"grunt-sync": "0.8.2",
|
|
160
153
|
"husky": "9.1.7",
|
|
161
154
|
"nodemon": "3.1.11",
|
|
162
155
|
"npm-watch": "0.13.0",
|
|
163
156
|
"otpauth": "9.4.1",
|
|
164
|
-
"pm2": "6.0.14",
|
|
165
157
|
"prettier": "3.8.1",
|
|
166
158
|
"pretty-quick": "4.2.2",
|
|
167
159
|
"rimraf": "6.1.2",
|
package/src/config.env.ts
CHANGED
|
@@ -265,11 +265,20 @@ const config: { [env: string]: IServerOptions } = {
|
|
|
265
265
|
automaticObjectIdFiltering: true,
|
|
266
266
|
baseUrl: process.env.BASE_URL,
|
|
267
267
|
betterAuth: {
|
|
268
|
-
rateLimit: {
|
|
268
|
+
rateLimit: {
|
|
269
|
+
enabled: process.env.RATE_LIMIT_ENABLED !== 'false',
|
|
270
|
+
max: parseInt(process.env.RATE_LIMIT_MAX || '10', 10),
|
|
271
|
+
},
|
|
269
272
|
secret: process.env.BETTER_AUTH_SECRET,
|
|
270
273
|
socialProviders: {
|
|
271
|
-
github: {
|
|
272
|
-
|
|
274
|
+
github: {
|
|
275
|
+
clientId: process.env.SOCIAL_GITHUB_CLIENT_ID || '',
|
|
276
|
+
clientSecret: process.env.SOCIAL_GITHUB_CLIENT_SECRET || '',
|
|
277
|
+
},
|
|
278
|
+
google: {
|
|
279
|
+
clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '',
|
|
280
|
+
clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '',
|
|
281
|
+
},
|
|
273
282
|
},
|
|
274
283
|
twoFactor: { appName: process.env.TWO_FACTOR_APP_NAME || 'Nest Server' },
|
|
275
284
|
},
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { ApolloDriverConfig } from '@nestjs/apollo';
|
|
2
|
+
import { Type } from '@nestjs/common';
|
|
2
3
|
import { GqlModuleAsyncOptions } from '@nestjs/graphql';
|
|
3
4
|
import { JwtModuleOptions } from '@nestjs/jwt';
|
|
4
5
|
import { JwtSignOptions } from '@nestjs/jwt/dist/interfaces';
|
|
@@ -1660,6 +1661,25 @@ interface IBetterAuthBase {
|
|
|
1660
1661
|
*/
|
|
1661
1662
|
baseUrl?: string;
|
|
1662
1663
|
|
|
1664
|
+
/**
|
|
1665
|
+
* Custom controller class to use instead of the default CoreBetterAuthController.
|
|
1666
|
+
* The class should extend CoreBetterAuthController.
|
|
1667
|
+
*
|
|
1668
|
+
* This allows projects to customize REST endpoints via config instead of creating
|
|
1669
|
+
* a separate module. Use this with CoreModule.forRoot(envConfig) (IAM-only mode).
|
|
1670
|
+
*
|
|
1671
|
+
* @example
|
|
1672
|
+
* ```typescript
|
|
1673
|
+
* // config.env.ts
|
|
1674
|
+
* betterAuth: {
|
|
1675
|
+
* controller: IamController,
|
|
1676
|
+
* }
|
|
1677
|
+
* ```
|
|
1678
|
+
*
|
|
1679
|
+
* @since 11.14.0
|
|
1680
|
+
*/
|
|
1681
|
+
controller?: Type<any>;
|
|
1682
|
+
|
|
1663
1683
|
/**
|
|
1664
1684
|
* Email/password authentication configuration.
|
|
1665
1685
|
* Enabled by default.
|
|
@@ -1795,6 +1815,25 @@ interface IBetterAuthBase {
|
|
|
1795
1815
|
*/
|
|
1796
1816
|
rateLimit?: IBetterAuthRateLimit;
|
|
1797
1817
|
|
|
1818
|
+
/**
|
|
1819
|
+
* Custom resolver class to use instead of the default DefaultBetterAuthResolver.
|
|
1820
|
+
* The class should extend CoreBetterAuthResolver.
|
|
1821
|
+
*
|
|
1822
|
+
* This allows projects to customize GraphQL operations via config instead of creating
|
|
1823
|
+
* a separate module. Use this with CoreModule.forRoot(envConfig) (IAM-only mode).
|
|
1824
|
+
*
|
|
1825
|
+
* @example
|
|
1826
|
+
* ```typescript
|
|
1827
|
+
* // config.env.ts
|
|
1828
|
+
* betterAuth: {
|
|
1829
|
+
* resolver: IamResolver,
|
|
1830
|
+
* }
|
|
1831
|
+
* ```
|
|
1832
|
+
*
|
|
1833
|
+
* @since 11.14.0
|
|
1834
|
+
*/
|
|
1835
|
+
resolver?: Type<any>;
|
|
1836
|
+
|
|
1798
1837
|
/**
|
|
1799
1838
|
* Secret for better-auth session cookie signing.
|
|
1800
1839
|
*
|
|
@@ -46,14 +46,20 @@ export class CoreAuthModule {
|
|
|
46
46
|
|
|
47
47
|
// Process providers
|
|
48
48
|
// Only register RolesGuard if not already registered (prevents duplicate with CoreBetterAuthModule)
|
|
49
|
+
// Note: We register RolesGuard as a regular provider first, then use useExisting
|
|
50
|
+
// to reference it as APP_GUARD. This ensures proper DI resolution even when
|
|
51
|
+
// RolesGuard extends a mixin (AuthGuard), which can interfere with useClass DI.
|
|
49
52
|
const rolesGuardProvider = RolesGuardRegistry.isRegistered()
|
|
50
53
|
? []
|
|
51
54
|
: (() => {
|
|
52
55
|
RolesGuardRegistry.markRegistered('CoreAuthModule');
|
|
53
|
-
return [
|
|
56
|
+
return [
|
|
57
|
+
RolesGuard, // Register as regular provider first
|
|
58
|
+
{ provide: APP_GUARD, useExisting: RolesGuard }, // Reference the registered provider
|
|
59
|
+
];
|
|
54
60
|
})();
|
|
55
61
|
|
|
56
|
-
let providers = [
|
|
62
|
+
let providers: any[] = [
|
|
57
63
|
// [Global] The GraphQLAuthGuard integrates the user into context
|
|
58
64
|
...rolesGuardProvider,
|
|
59
65
|
{
|
|
@@ -81,7 +87,7 @@ export class CoreAuthModule {
|
|
|
81
87
|
LegacyAuthRateLimitGuard,
|
|
82
88
|
];
|
|
83
89
|
if (Array.isArray(options?.providers)) {
|
|
84
|
-
providers =
|
|
90
|
+
providers = providers.concat(options.providers);
|
|
85
91
|
}
|
|
86
92
|
|
|
87
93
|
// Return CoreAuthModule
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { ExecutionContext, ForbiddenException, Injectable, Logger, Optional, UnauthorizedException } from '@nestjs/common';
|
|
1
|
+
import { ExecutionContext, ForbiddenException, Inject, Injectable, Logger, Optional, UnauthorizedException } from '@nestjs/common';
|
|
2
2
|
import { ModuleRef, Reflector } from '@nestjs/core';
|
|
3
3
|
import { GqlExecutionContext } from '@nestjs/graphql';
|
|
4
4
|
import { firstValueFrom, isObservable } from 'rxjs';
|
|
@@ -39,17 +39,52 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
|
|
|
39
39
|
private betterAuthService: CoreBetterAuthService | null = null;
|
|
40
40
|
private tokenService: BetterAuthTokenService | null = null;
|
|
41
41
|
private servicesResolved = false;
|
|
42
|
+
private resolvedReflector: null | Reflector = null;
|
|
42
43
|
|
|
43
44
|
/**
|
|
44
45
|
* Integrate reflector and moduleRef for lazy service resolution
|
|
46
|
+
*
|
|
47
|
+
* Note: Due to mixin inheritance from AuthGuard, NestJS DI may not inject dependencies correctly
|
|
48
|
+
* because the mixin generates its own design:paramtypes metadata that can override
|
|
49
|
+
* the child class's parameter metadata. Using explicit @Inject() decorators ensures
|
|
50
|
+
* NestJS uses token-based injection rather than positional metadata lookup.
|
|
51
|
+
*
|
|
52
|
+
* The ensureReflector() method provides an additional fallback by lazily resolving
|
|
53
|
+
* Reflector from moduleRef if initial injection fails.
|
|
45
54
|
*/
|
|
46
55
|
constructor(
|
|
47
|
-
protected readonly reflector: Reflector,
|
|
48
|
-
@Optional() private readonly moduleRef?: ModuleRef,
|
|
56
|
+
@Inject(Reflector) protected readonly reflector: Reflector,
|
|
57
|
+
@Optional() @Inject(ModuleRef) private readonly moduleRef?: ModuleRef,
|
|
49
58
|
) {
|
|
50
59
|
super();
|
|
51
60
|
}
|
|
52
61
|
|
|
62
|
+
/**
|
|
63
|
+
* Ensure Reflector is available.
|
|
64
|
+
* Due to mixin inheritance from AuthGuard, NestJS DI may not inject Reflector correctly.
|
|
65
|
+
* This fallback resolves Reflector from moduleRef if not injected.
|
|
66
|
+
*/
|
|
67
|
+
private ensureReflector(): Reflector {
|
|
68
|
+
if (this.reflector) {
|
|
69
|
+
return this.reflector;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (this.resolvedReflector) {
|
|
73
|
+
return this.resolvedReflector;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (this.moduleRef) {
|
|
77
|
+
try {
|
|
78
|
+
this.resolvedReflector = this.moduleRef.get(Reflector, { strict: false });
|
|
79
|
+
return this.resolvedReflector;
|
|
80
|
+
} catch {
|
|
81
|
+
this.logger.error('Failed to resolve Reflector from moduleRef');
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
throw new Error('Reflector not available - RolesGuard cannot function without it');
|
|
86
|
+
}
|
|
87
|
+
|
|
53
88
|
/**
|
|
54
89
|
* Lazily resolve BetterAuth services
|
|
55
90
|
*/
|
|
@@ -87,7 +122,7 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
|
|
|
87
122
|
*/
|
|
88
123
|
override async canActivate(context: ExecutionContext): Promise<boolean> {
|
|
89
124
|
// Get roles FIRST to check if authentication is even needed
|
|
90
|
-
const reflectorRoles = this.
|
|
125
|
+
const reflectorRoles = this.ensureReflector().getAll<string[][]>('roles', [context.getHandler(), context.getClass()]);
|
|
91
126
|
const roles: string[] = reflectorRoles[0]
|
|
92
127
|
? reflectorRoles[1]
|
|
93
128
|
? [...reflectorRoles[0], ...reflectorRoles[1]]
|
|
@@ -244,7 +279,7 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
|
|
|
244
279
|
*/
|
|
245
280
|
override handleRequest(err: Error | null, user: any, info: any, context: ExecutionContext) {
|
|
246
281
|
// Get roles
|
|
247
|
-
const reflectorRoles = this.
|
|
282
|
+
const reflectorRoles = this.ensureReflector().getAll<string[][]>('roles', [context.getHandler(), context.getClass()]);
|
|
248
283
|
const roles: string[] = reflectorRoles[0]
|
|
249
284
|
? reflectorRoles[1]
|
|
250
285
|
? [...reflectorRoles[0], ...reflectorRoles[1]]
|
|
@@ -292,6 +327,9 @@ export class RolesGuard extends AuthGuard(AuthGuardStrategy.JWT) {
|
|
|
292
327
|
*/
|
|
293
328
|
getRequest(context: ExecutionContext) {
|
|
294
329
|
const ctx = GqlExecutionContext.create(context);
|
|
295
|
-
|
|
330
|
+
// For GraphQL: ctx.getContext() is the GQL context with `.req` property
|
|
331
|
+
// For REST/HTTP: ctx.getContext() returns the `next` function (truthy but without `.req`)
|
|
332
|
+
// Using `?.req ||` ensures we fall back to the HTTP request for REST controllers
|
|
333
|
+
return ctx.getContext()?.req || context.switchToHttp().getRequest();
|
|
296
334
|
}
|
|
297
335
|
}
|