@lenne.tech/nest-server 11.7.0 → 11.7.1

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 (89) hide show
  1. package/dist/config.env.js +17 -1
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/interfaces/server-options.interface.d.ts +17 -0
  4. package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
  5. package/dist/core/modules/auth/core-auth.controller.js +28 -2
  6. package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
  7. package/dist/core/modules/auth/core-auth.module.js +14 -1
  8. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  9. package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -0
  10. package/dist/core/modules/auth/core-auth.resolver.js +20 -2
  11. package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
  12. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.d.ts +4 -0
  13. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js +17 -0
  14. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js.map +1 -0
  15. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.d.ts +9 -0
  16. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +74 -0
  17. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -0
  18. package/dist/core/modules/auth/interfaces/auth-provider.interface.d.ts +7 -0
  19. package/dist/core/modules/auth/interfaces/auth-provider.interface.js +5 -0
  20. package/dist/core/modules/auth/interfaces/auth-provider.interface.js.map +1 -0
  21. package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +1 -0
  22. package/dist/core/modules/auth/services/core-auth.service.d.ts +10 -1
  23. package/dist/core/modules/auth/services/core-auth.service.js +141 -9
  24. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  25. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.d.ts +31 -0
  26. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js +153 -0
  27. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js.map +1 -0
  28. package/dist/core/modules/better-auth/better-auth-migration-status.model.d.ts +10 -0
  29. package/dist/core/modules/better-auth/better-auth-migration-status.model.js +57 -0
  30. package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +1 -0
  31. package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +33 -0
  32. package/dist/core/modules/better-auth/better-auth-user.mapper.js +443 -0
  33. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -1
  34. package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +1 -0
  35. package/dist/core/modules/better-auth/core-better-auth.controller.js +15 -2
  36. package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
  37. package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +2 -0
  38. package/dist/core/modules/better-auth/core-better-auth.resolver.js +14 -0
  39. package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
  40. package/dist/core/modules/better-auth/index.d.ts +1 -0
  41. package/dist/core/modules/better-auth/index.js +1 -0
  42. package/dist/core/modules/better-auth/index.js.map +1 -1
  43. package/dist/core/modules/user/core-user.service.d.ts +7 -1
  44. package/dist/core/modules/user/core-user.service.js +57 -3
  45. package/dist/core/modules/user/core-user.service.js.map +1 -1
  46. package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +4 -0
  47. package/dist/core/modules/user/interfaces/core-user-service-options.interface.js +3 -0
  48. package/dist/core/modules/user/interfaces/core-user-service-options.interface.js.map +1 -0
  49. package/dist/core.module.d.ts +3 -0
  50. package/dist/core.module.js +133 -55
  51. package/dist/core.module.js.map +1 -1
  52. package/dist/index.d.ts +5 -0
  53. package/dist/index.js +5 -0
  54. package/dist/index.js.map +1 -1
  55. package/dist/server/modules/auth/auth.resolver.js +2 -0
  56. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  57. package/dist/server/modules/better-auth/better-auth.resolver.d.ts +2 -0
  58. package/dist/server/modules/better-auth/better-auth.resolver.js +13 -0
  59. package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
  60. package/dist/server/modules/user/user.service.d.ts +3 -1
  61. package/dist/server/modules/user/user.service.js +7 -3
  62. package/dist/server/modules/user/user.service.js.map +1 -1
  63. package/dist/tsconfig.build.tsbuildinfo +1 -1
  64. package/package.json +1 -1
  65. package/src/config.env.ts +32 -2
  66. package/src/core/common/interfaces/server-options.interface.ts +175 -0
  67. package/src/core/modules/auth/core-auth.controller.ts +93 -5
  68. package/src/core/modules/auth/core-auth.module.ts +15 -1
  69. package/src/core/modules/auth/core-auth.resolver.ts +70 -2
  70. package/src/core/modules/auth/exceptions/legacy-auth-disabled.exception.ts +35 -0
  71. package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +109 -0
  72. package/src/core/modules/auth/interfaces/auth-provider.interface.ts +86 -0
  73. package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +6 -0
  74. package/src/core/modules/auth/services/core-auth.service.ts +245 -6
  75. package/src/core/modules/auth/services/legacy-auth-rate-limiter.service.ts +283 -0
  76. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +254 -0
  77. package/src/core/modules/better-auth/README.md +487 -169
  78. package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
  79. package/src/core/modules/better-auth/better-auth-user.mapper.ts +805 -0
  80. package/src/core/modules/better-auth/core-better-auth.controller.ts +44 -3
  81. package/src/core/modules/better-auth/core-better-auth.resolver.ts +25 -0
  82. package/src/core/modules/better-auth/index.ts +1 -0
  83. package/src/core/modules/user/core-user.service.ts +131 -4
  84. package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +15 -0
  85. package/src/core.module.ts +258 -76
  86. package/src/index.ts +5 -0
  87. package/src/server/modules/auth/auth.resolver.ts +8 -0
  88. package/src/server/modules/better-auth/better-auth.resolver.ts +9 -0
  89. package/src/server/modules/user/user.service.ts +4 -2
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lenne.tech/nest-server",
3
- "version": "11.7.0",
3
+ "version": "11.7.1",
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",
package/src/config.env.ts CHANGED
@@ -14,6 +14,16 @@ const config: { [env: string]: IServerOptions } = {
14
14
  // Development environment
15
15
  // ===========================================================================
16
16
  development: {
17
+ // Legacy Auth endpoint controls (for migration to BetterAuth)
18
+ // Set to false after all users have migrated to BetterAuth (IAM)
19
+ // See: .claude/rules/module-deprecation.md
20
+ auth: {
21
+ legacyEndpoints: {
22
+ enabled: true, // Set to false to disable legacy auth endpoints (returns HTTP 410)
23
+ // graphql: true, // Optionally disable only GraphQL endpoints
24
+ // rest: true, // Optionally disable only REST endpoints
25
+ },
26
+ },
17
27
  automaticObjectIdFiltering: true,
18
28
  betterAuth: {
19
29
  basePath: '/iam',
@@ -162,11 +172,21 @@ const config: { [env: string]: IServerOptions } = {
162
172
  // Local environment
163
173
  // ===========================================================================
164
174
  local: {
175
+ // Legacy Auth endpoint controls (for migration to BetterAuth)
176
+ // Set to false after all users have migrated to BetterAuth (IAM)
177
+ // See: .claude/rules/module-deprecation.md
178
+ auth: {
179
+ legacyEndpoints: {
180
+ enabled: true, // Set to false to disable legacy auth endpoints (returns HTTP 410)
181
+ // graphql: true, // Optionally disable only GraphQL endpoints
182
+ // rest: true, // Optionally disable only REST endpoints
183
+ },
184
+ },
165
185
  automaticObjectIdFiltering: true,
166
186
  betterAuth: {
167
187
  basePath: '/iam',
168
188
  baseUrl: 'http://localhost:3000',
169
- // enabled: true by default - set false to explicitly disable
189
+ enabled: true, // Enable for Scenario 2 (Legacy + IAM) testing
170
190
  jwt: {
171
191
  enabled: true,
172
192
  expiresIn: '15m',
@@ -179,7 +199,7 @@ const config: { [env: string]: IServerOptions } = {
179
199
  },
180
200
  rateLimit: {
181
201
  enabled: true,
182
- max: 20,
202
+ max: 100, // Higher limit for local testing
183
203
  message: 'Too many requests, please try again later.',
184
204
  skipEndpoints: ['/session', '/callback'],
185
205
  strictEndpoints: ['/sign-in', '/sign-up', '/forgot-password', '/reset-password'],
@@ -321,6 +341,16 @@ const config: { [env: string]: IServerOptions } = {
321
341
  // Production environment
322
342
  // ===========================================================================
323
343
  production: {
344
+ // Legacy Auth endpoint controls (for migration to BetterAuth)
345
+ // Set to false after all users have migrated to BetterAuth (IAM)
346
+ // See: .claude/rules/module-deprecation.md
347
+ auth: {
348
+ legacyEndpoints: {
349
+ enabled: process.env.LEGACY_AUTH_ENABLED !== 'false', // Disable via env var
350
+ // graphql: true, // Optionally disable only GraphQL endpoints
351
+ // rest: true, // Optionally disable only REST endpoints
352
+ },
353
+ },
324
354
  automaticObjectIdFiltering: true,
325
355
  betterAuth: {
326
356
  basePath: '/iam',
@@ -22,6 +22,170 @@ import { MailjetOptions } from './mailjet-options.interface';
22
22
  */
23
23
  export type BetterAuthFieldType = 'boolean' | 'date' | 'json' | 'number' | 'number[]' | 'string' | 'string[]';
24
24
 
25
+ /**
26
+ * Interface for Auth configuration
27
+ *
28
+ * This configuration controls the authentication system behavior.
29
+ * In v11.x, Legacy Auth (CoreAuthService) is the default.
30
+ * In a future version, BetterAuth (IAM) will become the default.
31
+ *
32
+ * @since 11.7.1
33
+ *
34
+ * ## Migration Roadmap
35
+ *
36
+ * ### v11.x (Current)
37
+ * - Legacy Auth is the default and required for GraphQL Subscriptions
38
+ * - BetterAuth can be used alongside Legacy Auth
39
+ * - Use `legacyEndpoints.enabled: false` after all users migrated to IAM
40
+ *
41
+ * ### Future Version (Planned)
42
+ * - BetterAuth becomes the default
43
+ * - Legacy Auth becomes optional (must be explicitly enabled)
44
+ * - CoreModule.forRoot signature simplifies to `CoreModule.forRoot(options)`
45
+ *
46
+ * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
47
+ */
48
+ export interface IAuth {
49
+ /**
50
+ * Configuration for legacy auth endpoints
51
+ *
52
+ * Legacy endpoints include:
53
+ * - GraphQL: signIn, signUp, signOut, refreshToken mutations
54
+ * - REST: /api/auth/* endpoints
55
+ *
56
+ * These can be disabled once all users have migrated to BetterAuth (IAM).
57
+ *
58
+ * @example
59
+ * ```typescript
60
+ * auth: {
61
+ * legacyEndpoints: {
62
+ * enabled: false // Disable all legacy endpoints after migration
63
+ * }
64
+ * }
65
+ * ```
66
+ */
67
+ legacyEndpoints?: IAuthLegacyEndpoints;
68
+
69
+ /**
70
+ * Prevent user enumeration via unified error messages
71
+ *
72
+ * When enabled, authentication errors return a generic "Invalid credentials"
73
+ * message instead of specific messages like "Unknown email" or "Wrong password".
74
+ *
75
+ * This prevents attackers from determining whether an email address exists
76
+ * in the system, but reduces UX clarity for legitimate users.
77
+ *
78
+ * @since 11.7.x
79
+ * @default false (backward compatible - specific error messages)
80
+ *
81
+ * @example
82
+ * ```typescript
83
+ * auth: {
84
+ * preventUserEnumeration: true // Returns "Invalid credentials" for all auth errors
85
+ * }
86
+ * ```
87
+ */
88
+ preventUserEnumeration?: boolean;
89
+
90
+ /**
91
+ * Rate limiting configuration for Legacy Auth endpoints
92
+ *
93
+ * Protects against brute-force attacks on signIn, signUp, and other
94
+ * authentication endpoints.
95
+ *
96
+ * Follows the same pattern as `betterAuth.rateLimit`.
97
+ *
98
+ * @since 11.7.x
99
+ * @default { enabled: false }
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * auth: {
104
+ * rateLimit: {
105
+ * enabled: true,
106
+ * max: 10,
107
+ * windowSeconds: 60,
108
+ * message: 'Too many login attempts, please try again later.',
109
+ * }
110
+ * }
111
+ * ```
112
+ */
113
+ rateLimit?: IAuthRateLimit;
114
+ }
115
+
116
+ /**
117
+ * Interface for Legacy Auth endpoints configuration
118
+ *
119
+ * These endpoints are part of the Legacy Auth system (CoreAuthService).
120
+ * In a future version, BetterAuth (IAM) will become the default and these endpoints
121
+ * can be disabled once all users have migrated.
122
+ *
123
+ * @since 11.7.1
124
+ * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
125
+ */
126
+ export interface IAuthLegacyEndpoints {
127
+ /**
128
+ * Whether legacy auth endpoints are enabled.
129
+ *
130
+ * Set to false to disable all legacy auth endpoints (GraphQL and REST).
131
+ * Use this after all users have migrated to BetterAuth (IAM).
132
+ *
133
+ * Check migration status via the `betterAuthMigrationStatus` query.
134
+ *
135
+ * @default true
136
+ */
137
+ enabled?: boolean;
138
+
139
+ /**
140
+ * Whether legacy GraphQL auth endpoints are enabled.
141
+ * Affects: signIn, signUp, signOut, refreshToken mutations
142
+ *
143
+ * @default true (inherits from `enabled`)
144
+ */
145
+ graphql?: boolean;
146
+
147
+ /**
148
+ * Whether legacy REST auth endpoints are enabled.
149
+ * Affects: /api/auth/sign-in, /api/auth/sign-up, etc.
150
+ *
151
+ * @default true (inherits from `enabled`)
152
+ */
153
+ rest?: boolean;
154
+ }
155
+
156
+ /**
157
+ * Interface for Legacy Auth rate limiting configuration
158
+ *
159
+ * Same structure as IBetterAuthRateLimit for consistency.
160
+ *
161
+ * @since 11.7.x
162
+ */
163
+ export interface IAuthRateLimit {
164
+ /**
165
+ * Whether rate limiting is enabled
166
+ * @default false
167
+ */
168
+ enabled?: boolean;
169
+
170
+ /**
171
+ * Maximum number of requests within the time window
172
+ * @default 10
173
+ */
174
+ max?: number;
175
+
176
+ /**
177
+ * Custom message when rate limit is exceeded
178
+ * @default 'Too many requests, please try again later.'
179
+ */
180
+ message?: string;
181
+
182
+ /**
183
+ * Time window in seconds
184
+ * @default 60
185
+ */
186
+ windowSeconds?: number;
187
+ }
188
+
25
189
  /**
26
190
  * Interface for better-auth configuration
27
191
  */
@@ -413,6 +577,17 @@ export interface IJwt {
413
577
  * Options for the server
414
578
  */
415
579
  export interface IServerOptions {
580
+ /**
581
+ * Authentication system configuration
582
+ *
583
+ * Controls Legacy Auth endpoints and behavior.
584
+ * In a future version, this will also control BetterAuth as the default system.
585
+ *
586
+ * @since 11.7.1
587
+ * @see IAuth
588
+ */
589
+ auth?: IAuth;
590
+
416
591
  /**
417
592
  * Automatically detect ObjectIds in string values in FilterQueries
418
593
  * and expand them as OR query with string and ObjectId.
@@ -1,9 +1,12 @@
1
1
  import { Body, Controller, Get, ParseBoolPipe, Post, Query, Res, UseGuards } from '@nestjs/common';
2
2
  import {
3
- ApiBody, ApiCreatedResponse,
3
+ ApiBody,
4
+ ApiCreatedResponse,
5
+ ApiGoneResponse,
4
6
  ApiOkResponse,
5
7
  ApiOperation,
6
8
  ApiQuery,
9
+ ApiTooManyRequestsResponse,
7
10
  } from '@nestjs/swagger';
8
11
  import { Response as ResponseType } from 'express';
9
12
 
@@ -14,13 +17,38 @@ import { RoleEnum } from '../../common/enums/role.enum';
14
17
  import { ConfigService } from '../../common/services/config.service';
15
18
  import { AuthGuardStrategy } from './auth-guard-strategy.enum';
16
19
  import { CoreAuthModel } from './core-auth.model';
20
+ import { LegacyAuthDisabledException } from './exceptions/legacy-auth-disabled.exception';
17
21
  import { AuthGuard } from './guards/auth.guard';
22
+ import { LegacyAuthRateLimitGuard } from './guards/legacy-auth-rate-limit.guard';
18
23
  import { CoreAuthSignInInput } from './inputs/core-auth-sign-in.input';
19
24
  import { CoreAuthSignUpInput } from './inputs/core-auth-sign-up.input';
20
25
  import { ICoreAuthUser } from './interfaces/core-auth-user.interface';
21
26
  import { CoreAuthService } from './services/core-auth.service';
22
27
  import { Tokens } from './tokens.decorator';
23
28
 
29
+ /**
30
+ * Authentication controller for REST endpoints
31
+ *
32
+ * This controller provides Legacy Auth endpoints via REST.
33
+ * In a future version, BetterAuth (IAM) will become the default.
34
+ *
35
+ * ## Disabling Legacy Endpoints
36
+ *
37
+ * After all users have migrated to BetterAuth (IAM), these endpoints
38
+ * can be disabled via configuration:
39
+ *
40
+ * ```typescript
41
+ * auth: {
42
+ * legacyEndpoints: {
43
+ * enabled: false, // Disable all legacy endpoints
44
+ * // or
45
+ * rest: false // Disable only REST endpoints
46
+ * }
47
+ * }
48
+ * ```
49
+ *
50
+ * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
51
+ */
24
52
  @ApiCommonErrorResponses()
25
53
  @Controller('auth')
26
54
  @Roles(RoleEnum.ADMIN)
@@ -33,63 +61,123 @@ export class CoreAuthController {
33
61
  protected readonly configService: ConfigService,
34
62
  ) {}
35
63
 
64
+ // ===========================================================================
65
+ // Helper - Legacy Endpoint Check
66
+ // ===========================================================================
67
+
68
+ /**
69
+ * Check if legacy REST endpoints are enabled
70
+ *
71
+ * Throws LegacyAuthDisabledException if:
72
+ * - config.auth.legacyEndpoints.enabled is false
73
+ * - config.auth.legacyEndpoints.rest is false
74
+ *
75
+ * @throws LegacyAuthDisabledException
76
+ */
77
+ protected checkLegacyRESTEnabled(endpointName: string): void {
78
+ const authConfig = this.configService.getFastButReadOnly('auth');
79
+ const legacyConfig = authConfig?.legacyEndpoints;
80
+
81
+ // Check if legacy endpoints are globally disabled
82
+ if (legacyConfig?.enabled === false) {
83
+ throw new LegacyAuthDisabledException(endpointName);
84
+ }
85
+
86
+ // Check if REST endpoints specifically are disabled
87
+ if (legacyConfig?.rest === false) {
88
+ throw new LegacyAuthDisabledException(endpointName);
89
+ }
90
+ }
91
+
36
92
  /**
37
93
  * Logout user (from specific device)
94
+ *
95
+ * @deprecated Will be replaced by BetterAuth signOut in a future version
96
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
38
97
  */
98
+ @ApiGoneResponse({ description: 'Legacy Auth endpoints are disabled' })
39
99
  @ApiOkResponse({ type: Boolean })
40
100
  @ApiOperation({ description: 'Logs a user out from a specific device' })
41
101
  @ApiQuery({ description: 'If all devices should be logged out,', name: 'allDevices', required: false, type: Boolean })
102
+ @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' })
42
103
  @Get('logout')
43
104
  @Roles(RoleEnum.S_EVERYONE)
44
- @UseGuards(AuthGuard(AuthGuardStrategy.JWT))
105
+ @UseGuards(LegacyAuthRateLimitGuard, AuthGuard(AuthGuardStrategy.JWT))
45
106
  async logout(
46
107
  @CurrentUser() currentUser: ICoreAuthUser,
47
108
  @Tokens('token') token: string,
48
109
  @Res({ passthrough: true }) res: ResponseType,
49
110
  @Query('allDevices', new ParseBoolPipe({ optional: true })) allDevices?: boolean,
50
111
  ): Promise<boolean> {
112
+ this.checkLegacyRESTEnabled('logout');
51
113
  const result = await this.authService.logout(token, { allDevices, currentUser });
52
114
  return this.processCookies(res, result);
53
115
  }
54
116
 
55
117
  /**
56
118
  * Refresh token (for specific device)
119
+ *
120
+ * @deprecated Will be replaced by BetterAuth session refresh in a future version
121
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
57
122
  */
123
+ @ApiGoneResponse({ description: 'Legacy Auth endpoints are disabled' })
58
124
  @ApiOkResponse({ type: CoreAuthModel })
59
125
  @ApiOperation({ description: 'Refresh token (for specific device)' })
126
+ @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' })
60
127
  @Get('refresh-token')
61
128
  @Roles(RoleEnum.S_EVERYONE)
62
- @UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH))
129
+ @UseGuards(LegacyAuthRateLimitGuard, AuthGuard(AuthGuardStrategy.JWT_REFRESH))
63
130
  async refreshToken(
64
131
  @CurrentUser() user: ICoreAuthUser,
65
132
  @Tokens('refreshToken') refreshToken: string,
66
133
  @Res({ passthrough: true }) res: ResponseType,
67
134
  ): Promise<CoreAuthModel> {
135
+ this.checkLegacyRESTEnabled('refresh-token');
68
136
  const result = await this.authService.refreshTokens(user, refreshToken);
69
137
  return this.processCookies(res, result);
70
138
  }
71
139
 
72
140
  /**
73
141
  * Sign in user via email and password (on specific device)
142
+ *
143
+ * @deprecated Will be replaced by BetterAuth signIn in a future version
144
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
74
145
  */
75
146
  @ApiCreatedResponse({ description: 'Signed in successfully', type: CoreAuthModel })
147
+ @ApiGoneResponse({ description: 'Legacy Auth endpoints are disabled' })
76
148
  @ApiOperation({ description: 'Sign in via email and password' })
149
+ @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' })
77
150
  @Post('signin')
78
151
  @Roles(RoleEnum.S_EVERYONE)
79
- async signIn(@Res({ passthrough: true }) res: ResponseType, @Body() input: CoreAuthSignInInput): Promise<CoreAuthModel> {
152
+ @UseGuards(LegacyAuthRateLimitGuard)
153
+ async signIn(
154
+ @Res({ passthrough: true }) res: ResponseType,
155
+ @Body() input: CoreAuthSignInInput,
156
+ ): Promise<CoreAuthModel> {
157
+ this.checkLegacyRESTEnabled('signin');
80
158
  const result = await this.authService.signIn(input);
81
159
  return this.processCookies(res, result);
82
160
  }
83
161
 
84
162
  /**
85
163
  * Register a new user account (on specific device)
164
+ *
165
+ * @deprecated Will be replaced by BetterAuth signUp in a future version
166
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
86
167
  */
87
168
  @ApiBody({ type: CoreAuthSignUpInput })
88
169
  @ApiCreatedResponse({ type: CoreAuthSignUpInput })
170
+ @ApiGoneResponse({ description: 'Legacy Auth endpoints are disabled' })
89
171
  @ApiOperation({ description: 'Sign up via email and password' })
172
+ @ApiTooManyRequestsResponse({ description: 'Rate limit exceeded' })
90
173
  @Post('signup')
91
174
  @Roles(RoleEnum.S_EVERYONE)
92
- async signUp(@Res({ passthrough: true }) res: ResponseType, @Body() input: CoreAuthSignUpInput): Promise<CoreAuthModel> {
175
+ @UseGuards(LegacyAuthRateLimitGuard)
176
+ async signUp(
177
+ @Res({ passthrough: true }) res: ResponseType,
178
+ @Body() input: CoreAuthSignUpInput,
179
+ ): Promise<CoreAuthModel> {
180
+ this.checkLegacyRESTEnabled('signup');
93
181
  const result = await this.authService.signUp(input);
94
182
  return this.processCookies(res, result);
95
183
  }
@@ -5,9 +5,11 @@ import { PassportModule } from '@nestjs/passport';
5
5
  import { PubSub } from 'graphql-subscriptions';
6
6
 
7
7
  import { AuthGuardStrategy } from './auth-guard-strategy.enum';
8
+ import { LegacyAuthRateLimitGuard } from './guards/legacy-auth-rate-limit.guard';
8
9
  import { RolesGuard } from './guards/roles.guard';
9
10
  import { CoreAuthUserService } from './services/core-auth-user.service';
10
11
  import { CoreAuthService } from './services/core-auth.service';
12
+ import { LegacyAuthRateLimiter } from './services/legacy-auth-rate-limiter.service';
11
13
  import { JwtRefreshStrategy } from './strategies/jwt-refresh.strategy';
12
14
  import { JwtStrategy } from './strategies/jwt.strategy';
13
15
 
@@ -68,6 +70,9 @@ export class CoreAuthModule {
68
70
  provide: JwtRefreshStrategy,
69
71
  useClass: options.jwtRefreshStrategy || JwtRefreshStrategy,
70
72
  },
73
+ // Rate limiting for Legacy Auth endpoints (disabled by default, configure via auth.rateLimit)
74
+ LegacyAuthRateLimiter,
75
+ LegacyAuthRateLimitGuard,
71
76
  ];
72
77
  if (Array.isArray(options?.providers)) {
73
78
  providers = imports.concat(options.providers);
@@ -75,7 +80,16 @@ export class CoreAuthModule {
75
80
 
76
81
  // Return CoreAuthModule
77
82
  return {
78
- exports: [CoreAuthService, JwtModule, JwtStrategy, JwtRefreshStrategy, PassportModule, UserModule],
83
+ exports: [
84
+ CoreAuthService,
85
+ JwtModule,
86
+ JwtStrategy,
87
+ JwtRefreshStrategy,
88
+ LegacyAuthRateLimiter,
89
+ LegacyAuthRateLimitGuard,
90
+ PassportModule,
91
+ UserModule,
92
+ ],
79
93
  imports,
80
94
  module: CoreAuthModule,
81
95
  providers,
@@ -10,7 +10,9 @@ import { ServiceOptions } from '../../common/interfaces/service-options.interfac
10
10
  import { ConfigService } from '../../common/services/config.service';
11
11
  import { AuthGuardStrategy } from './auth-guard-strategy.enum';
12
12
  import { CoreAuthModel } from './core-auth.model';
13
+ import { LegacyAuthDisabledException } from './exceptions/legacy-auth-disabled.exception';
13
14
  import { AuthGuard } from './guards/auth.guard';
15
+ import { LegacyAuthRateLimitGuard } from './guards/legacy-auth-rate-limit.guard';
14
16
  import { CoreAuthSignInInput } from './inputs/core-auth-sign-in.input';
15
17
  import { CoreAuthSignUpInput } from './inputs/core-auth-sign-up.input';
16
18
  import { ICoreAuthUser } from './interfaces/core-auth-user.interface';
@@ -19,6 +21,26 @@ import { Tokens } from './tokens.decorator';
19
21
 
20
22
  /**
21
23
  * Authentication resolver for the sign in
24
+ *
25
+ * This resolver provides Legacy Auth endpoints via GraphQL.
26
+ * In a future version, BetterAuth (IAM) will become the default.
27
+ *
28
+ * ## Disabling Legacy Endpoints
29
+ *
30
+ * After all users have migrated to BetterAuth (IAM), these endpoints
31
+ * can be disabled via configuration:
32
+ *
33
+ * ```typescript
34
+ * auth: {
35
+ * legacyEndpoints: {
36
+ * enabled: false, // Disable all legacy endpoints
37
+ * // or
38
+ * graphql: false // Disable only GraphQL endpoints
39
+ * }
40
+ * }
41
+ * ```
42
+ *
43
+ * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
22
44
  */
23
45
  @Resolver(() => CoreAuthModel, { isAbstract: true })
24
46
  @Roles(RoleEnum.ADMIN)
@@ -31,67 +53,113 @@ export class CoreAuthResolver {
31
53
  protected readonly configService: ConfigService,
32
54
  ) {}
33
55
 
56
+ // ===========================================================================
57
+ // Helper - Legacy Endpoint Check
58
+ // ===========================================================================
59
+
60
+ /**
61
+ * Check if legacy GraphQL endpoints are enabled
62
+ *
63
+ * Throws LegacyAuthDisabledException if:
64
+ * - config.auth.legacyEndpoints.enabled is false
65
+ * - config.auth.legacyEndpoints.graphql is false
66
+ *
67
+ * @throws LegacyAuthDisabledException
68
+ */
69
+ protected checkLegacyGraphQLEnabled(endpointName: string): void {
70
+ const authConfig = this.configService.getFastButReadOnly('auth');
71
+ const legacyConfig = authConfig?.legacyEndpoints;
72
+
73
+ // Check if legacy endpoints are globally disabled
74
+ if (legacyConfig?.enabled === false) {
75
+ throw new LegacyAuthDisabledException(endpointName);
76
+ }
77
+
78
+ // Check if GraphQL endpoints specifically are disabled
79
+ if (legacyConfig?.graphql === false) {
80
+ throw new LegacyAuthDisabledException(endpointName);
81
+ }
82
+ }
83
+
34
84
  // ===========================================================================
35
85
  // Mutations
36
86
  // ===========================================================================
37
87
 
38
88
  /**
39
89
  * Logout user (from specific device)
90
+ *
91
+ * @deprecated Will be replaced by BetterAuth signOut in a future version
92
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
40
93
  */
41
94
  @Mutation(() => Boolean, { description: 'Logout user (from specific device)' })
42
95
  @Roles(RoleEnum.S_EVERYONE)
43
- @UseGuards(AuthGuard(AuthGuardStrategy.JWT))
96
+ @UseGuards(LegacyAuthRateLimitGuard, AuthGuard(AuthGuardStrategy.JWT))
44
97
  async logout(
45
98
  @CurrentUser() currentUser: ICoreAuthUser,
46
99
  @Context() ctx: { res: ResponseType },
47
100
  @Tokens('token') token: string,
48
101
  @Args('allDevices', { nullable: true }) allDevices?: boolean,
49
102
  ): Promise<boolean> {
103
+ this.checkLegacyGraphQLEnabled('logout');
50
104
  const result = await this.authService.logout(token, { allDevices, currentUser });
51
105
  return this.processCookies(ctx, result);
52
106
  }
53
107
 
54
108
  /**
55
109
  * Refresh token (for specific device)
110
+ *
111
+ * @deprecated Will be replaced by BetterAuth session refresh in a future version
112
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
56
113
  */
57
114
  @Mutation(() => CoreAuthModel, { description: 'Refresh tokens (for specific device)' })
58
115
  @Roles(RoleEnum.S_EVERYONE)
59
- @UseGuards(AuthGuard(AuthGuardStrategy.JWT_REFRESH))
116
+ @UseGuards(LegacyAuthRateLimitGuard, AuthGuard(AuthGuardStrategy.JWT_REFRESH))
60
117
  async refreshToken(
61
118
  @CurrentUser() user: ICoreAuthUser,
62
119
  @Tokens('refreshToken') refreshToken: string,
63
120
  @Context() ctx: { res: ResponseType },
64
121
  ): Promise<CoreAuthModel> {
122
+ this.checkLegacyGraphQLEnabled('refreshToken');
65
123
  const result = await this.authService.refreshTokens(user, refreshToken);
66
124
  return this.processCookies(ctx, result);
67
125
  }
68
126
 
69
127
  /**
70
128
  * Sign in user via email and password (on specific device)
129
+ *
130
+ * @deprecated Will be replaced by BetterAuth signIn in a future version
131
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
71
132
  */
72
133
  @Mutation(() => CoreAuthModel, {
73
134
  description: 'Sign in user via email and password and get JWT tokens (for specific device)',
74
135
  })
75
136
  @Roles(RoleEnum.S_EVERYONE)
137
+ @UseGuards(LegacyAuthRateLimitGuard)
76
138
  async signIn(
77
139
  @GraphQLServiceOptions({ gqlPath: 'signIn.user' }) serviceOptions: ServiceOptions,
78
140
  @Context() ctx: { res: ResponseType },
79
141
  @Args('input') input: CoreAuthSignInInput,
80
142
  ): Promise<CoreAuthModel> {
143
+ this.checkLegacyGraphQLEnabled('signIn');
81
144
  const result = await this.authService.signIn(input, serviceOptions);
82
145
  return this.processCookies(ctx, result);
83
146
  }
84
147
 
85
148
  /**
86
149
  * Register a new user account (on specific device)
150
+ *
151
+ * @deprecated Will be replaced by BetterAuth signUp in a future version
152
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
87
153
  */
88
154
  @Mutation(() => CoreAuthModel, { description: 'Register a new user account (on specific device)' })
89
155
  @Roles(RoleEnum.S_EVERYONE)
156
+ @UseGuards(LegacyAuthRateLimitGuard)
90
157
  async signUp(
91
158
  @GraphQLServiceOptions({ gqlPath: 'signUp.user' }) serviceOptions: ServiceOptions,
92
159
  @Context() ctx: { res: ResponseType },
93
160
  @Args('input') input: CoreAuthSignUpInput,
94
161
  ): Promise<CoreAuthModel> {
162
+ this.checkLegacyGraphQLEnabled('signUp');
95
163
  const result = await this.authService.signUp(input, serviceOptions);
96
164
  return this.processCookies(ctx, result);
97
165
  }
@@ -0,0 +1,35 @@
1
+ import { GoneException } from '@nestjs/common';
2
+
3
+ /**
4
+ * Exception thrown when Legacy Auth endpoints are accessed but disabled
5
+ *
6
+ * This exception is thrown when:
7
+ * - config.auth.legacyEndpoints.enabled is false
8
+ * - config.auth.legacyEndpoints.graphql is false (for GraphQL endpoints)
9
+ * - config.auth.legacyEndpoints.rest is false (for REST endpoints)
10
+ *
11
+ * HTTP Status: 410 Gone
12
+ *
13
+ * This status code indicates that the resource is no longer available
14
+ * and will not be available again - appropriate for deprecated endpoints.
15
+ *
16
+ * @since 11.7.1
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * if (!this.isLegacyEndpointEnabled()) {
21
+ * throw new LegacyAuthDisabledException();
22
+ * }
23
+ * ```
24
+ */
25
+ export class LegacyAuthDisabledException extends GoneException {
26
+ constructor(endpoint?: string) {
27
+ super({
28
+ error: 'Legacy Auth Disabled',
29
+ message: endpoint
30
+ ? `Legacy Auth endpoint '${endpoint}' is disabled. Use BetterAuth (IAM) endpoints instead.`
31
+ : 'Legacy Auth endpoints are disabled. Use BetterAuth (IAM) endpoints instead.',
32
+ statusCode: 410,
33
+ });
34
+ }
35
+ }