@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.
Files changed (45) hide show
  1. package/dist/config.env.js +12 -3
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/interfaces/server-options.interface.d.ts +3 -0
  4. package/dist/core/modules/auth/core-auth.module.js +5 -2
  5. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  6. package/dist/core/modules/auth/guards/roles.guard.d.ts +2 -0
  7. package/dist/core/modules/auth/guards/roles.guard.js +24 -3
  8. package/dist/core/modules/auth/guards/roles.guard.js.map +1 -1
  9. package/dist/core/modules/auth/tokens.decorator.d.ts +1 -1
  10. package/dist/core/modules/better-auth/better-auth-roles.guard.d.ts +10 -0
  11. package/dist/core/modules/better-auth/better-auth-roles.guard.js +136 -0
  12. package/dist/core/modules/better-auth/better-auth-roles.guard.js.map +1 -0
  13. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js +3 -0
  14. package/dist/core/modules/better-auth/core-better-auth-api.middleware.js.map +1 -1
  15. package/dist/core/modules/better-auth/core-better-auth-web.helper.d.ts +3 -1
  16. package/dist/core/modules/better-auth/core-better-auth-web.helper.js +8 -6
  17. package/dist/core/modules/better-auth/core-better-auth-web.helper.js.map +1 -1
  18. package/dist/core/modules/better-auth/core-better-auth.middleware.js +55 -43
  19. package/dist/core/modules/better-auth/core-better-auth.middleware.js.map +1 -1
  20. package/dist/core/modules/better-auth/core-better-auth.module.d.ts +13 -1
  21. package/dist/core/modules/better-auth/core-better-auth.module.js +56 -10
  22. package/dist/core/modules/better-auth/core-better-auth.module.js.map +1 -1
  23. package/dist/core/modules/better-auth/index.d.ts +1 -0
  24. package/dist/core/modules/better-auth/index.js +1 -0
  25. package/dist/core/modules/better-auth/index.js.map +1 -1
  26. package/dist/core.module.d.ts +1 -0
  27. package/dist/core.module.js +82 -2
  28. package/dist/core.module.js.map +1 -1
  29. package/dist/server/modules/error-code/error-codes.d.ts +1 -1
  30. package/dist/tsconfig.build.tsbuildinfo +1 -1
  31. package/package.json +3 -11
  32. package/src/config.env.ts +12 -3
  33. package/src/core/common/interfaces/server-options.interface.ts +39 -0
  34. package/src/core/modules/auth/core-auth.module.ts +9 -3
  35. package/src/core/modules/auth/guards/roles.guard.ts +44 -6
  36. package/src/core/modules/better-auth/CUSTOMIZATION.md +520 -0
  37. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +15 -0
  38. package/src/core/modules/better-auth/README.md +24 -1
  39. package/src/core/modules/better-auth/better-auth-roles.guard.ts +205 -0
  40. package/src/core/modules/better-auth/core-better-auth-api.middleware.ts +6 -0
  41. package/src/core/modules/better-auth/core-better-auth-web.helper.ts +14 -6
  42. package/src/core/modules/better-auth/core-better-auth.middleware.ts +91 -71
  43. package/src/core/modules/better-auth/core-better-auth.module.ts +97 -11
  44. package/src/core/modules/better-auth/index.ts +1 -0
  45. 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.0",
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
- "stop": "./node_modules/.bin/pm2 delete nest",
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.3.0",
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: { enabled: process.env.RATE_LIMIT_ENABLED !== 'false', max: parseInt(process.env.RATE_LIMIT_MAX || '10', 10) },
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: { clientId: process.env.SOCIAL_GITHUB_CLIENT_ID || '', clientSecret: process.env.SOCIAL_GITHUB_CLIENT_SECRET || '' },
272
- google: { clientId: process.env.SOCIAL_GOOGLE_CLIENT_ID || '', clientSecret: process.env.SOCIAL_GOOGLE_CLIENT_SECRET || '' },
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 [{ provide: APP_GUARD, useClass: RolesGuard }];
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 = imports.concat(options.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.reflector.getAll<string[][]>('roles', [context.getHandler(), context.getClass()]);
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.reflector.getAll<string[][]>('roles', [context.getHandler(), context.getClass()]);
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
- return ctx.getContext() ? ctx.getContext().req : context.switchToHttp().getRequest();
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
  }