@lenne.tech/nest-server 11.6.2 → 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 (136) hide show
  1. package/dist/config.env.js +19 -12
  2. package/dist/config.env.js.map +1 -1
  3. package/dist/core/common/helpers/filter.helper.d.ts +9 -9
  4. package/dist/core/common/helpers/filter.helper.js +2 -4
  5. package/dist/core/common/helpers/filter.helper.js.map +1 -1
  6. package/dist/core/common/helpers/gridfs.helper.js +3 -3
  7. package/dist/core/common/helpers/gridfs.helper.js.map +1 -1
  8. package/dist/core/common/interfaces/server-options.interface.d.ts +21 -3
  9. package/dist/core/common/services/crud.service.d.ts +16 -16
  10. package/dist/core/common/services/crud.service.js +1 -1
  11. package/dist/core/common/services/crud.service.js.map +1 -1
  12. package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
  13. package/dist/core/modules/auth/core-auth.controller.js +28 -2
  14. package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
  15. package/dist/core/modules/auth/core-auth.module.js +14 -1
  16. package/dist/core/modules/auth/core-auth.module.js.map +1 -1
  17. package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -0
  18. package/dist/core/modules/auth/core-auth.resolver.js +20 -2
  19. package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
  20. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.d.ts +4 -0
  21. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js +17 -0
  22. package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js.map +1 -0
  23. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.d.ts +9 -0
  24. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +74 -0
  25. package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -0
  26. package/dist/core/modules/auth/interfaces/auth-provider.interface.d.ts +7 -0
  27. package/dist/core/modules/auth/interfaces/auth-provider.interface.js +5 -0
  28. package/dist/core/modules/auth/interfaces/auth-provider.interface.js.map +1 -0
  29. package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +1 -0
  30. package/dist/core/modules/auth/services/core-auth.service.d.ts +10 -1
  31. package/dist/core/modules/auth/services/core-auth.service.js +141 -9
  32. package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
  33. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.d.ts +31 -0
  34. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js +153 -0
  35. package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js.map +1 -0
  36. package/dist/core/modules/better-auth/better-auth-migration-status.model.d.ts +10 -0
  37. package/dist/core/modules/better-auth/better-auth-migration-status.model.js +57 -0
  38. package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +1 -0
  39. package/dist/core/modules/better-auth/better-auth-models.d.ts +0 -1
  40. package/dist/core/modules/better-auth/better-auth-models.js +0 -4
  41. package/dist/core/modules/better-auth/better-auth-models.js.map +1 -1
  42. package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +33 -0
  43. package/dist/core/modules/better-auth/better-auth-user.mapper.js +443 -0
  44. package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -1
  45. package/dist/core/modules/better-auth/better-auth.config.js +3 -0
  46. package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
  47. package/dist/core/modules/better-auth/better-auth.module.d.ts +10 -2
  48. package/dist/core/modules/better-auth/better-auth.module.js +40 -52
  49. package/dist/core/modules/better-auth/better-auth.module.js.map +1 -1
  50. package/dist/core/modules/better-auth/better-auth.resolver.d.ts +8 -12
  51. package/dist/core/modules/better-auth/better-auth.resolver.js +33 -351
  52. package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
  53. package/dist/core/modules/better-auth/better-auth.service.d.ts +0 -1
  54. package/dist/core/modules/better-auth/better-auth.service.js +0 -3
  55. package/dist/core/modules/better-auth/better-auth.service.js.map +1 -1
  56. package/dist/core/modules/better-auth/better-auth.types.d.ts +9 -8
  57. package/dist/core/modules/better-auth/better-auth.types.js +14 -3
  58. package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
  59. package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +67 -0
  60. package/dist/core/modules/better-auth/core-better-auth.controller.js +504 -0
  61. package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -0
  62. package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +61 -0
  63. package/dist/core/modules/better-auth/core-better-auth.resolver.js +552 -0
  64. package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -0
  65. package/dist/core/modules/better-auth/index.d.ts +3 -0
  66. package/dist/core/modules/better-auth/index.js +3 -0
  67. package/dist/core/modules/better-auth/index.js.map +1 -1
  68. package/dist/core/modules/user/core-user.service.d.ts +7 -1
  69. package/dist/core/modules/user/core-user.service.js +57 -3
  70. package/dist/core/modules/user/core-user.service.js.map +1 -1
  71. package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +4 -0
  72. package/dist/core/modules/user/interfaces/core-user-service-options.interface.js +3 -0
  73. package/dist/core/modules/user/interfaces/core-user-service-options.interface.js.map +1 -0
  74. package/dist/core.module.d.ts +3 -0
  75. package/dist/core.module.js +132 -54
  76. package/dist/core.module.js.map +1 -1
  77. package/dist/index.d.ts +5 -0
  78. package/dist/index.js +5 -0
  79. package/dist/index.js.map +1 -1
  80. package/dist/server/modules/auth/auth.resolver.js +2 -0
  81. package/dist/server/modules/auth/auth.resolver.js.map +1 -1
  82. package/dist/server/modules/better-auth/better-auth.controller.d.ts +10 -0
  83. package/dist/server/modules/better-auth/better-auth.controller.js +36 -0
  84. package/dist/server/modules/better-auth/better-auth.controller.js.map +1 -0
  85. package/dist/server/modules/better-auth/better-auth.module.d.ts +9 -0
  86. package/dist/server/modules/better-auth/better-auth.module.js +44 -0
  87. package/dist/server/modules/better-auth/better-auth.module.js.map +1 -0
  88. package/dist/server/modules/better-auth/better-auth.resolver.d.ts +47 -0
  89. package/dist/server/modules/better-auth/better-auth.resolver.js +234 -0
  90. package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -0
  91. package/dist/server/modules/file/file-info.model.d.ts +71 -3
  92. package/dist/server/modules/user/user.model.d.ts +169 -3
  93. package/dist/server/modules/user/user.service.d.ts +3 -1
  94. package/dist/server/modules/user/user.service.js +7 -3
  95. package/dist/server/modules/user/user.service.js.map +1 -1
  96. package/dist/server/server.module.js +6 -1
  97. package/dist/server/server.module.js.map +1 -1
  98. package/dist/tsconfig.build.tsbuildinfo +1 -1
  99. package/package.json +20 -29
  100. package/src/config.env.ts +34 -13
  101. package/src/core/common/helpers/filter.helper.ts +15 -17
  102. package/src/core/common/helpers/gridfs.helper.ts +5 -5
  103. package/src/core/common/interfaces/server-options.interface.ts +222 -14
  104. package/src/core/common/services/crud.service.ts +22 -22
  105. package/src/core/modules/auth/core-auth.controller.ts +93 -5
  106. package/src/core/modules/auth/core-auth.module.ts +15 -1
  107. package/src/core/modules/auth/core-auth.resolver.ts +70 -2
  108. package/src/core/modules/auth/exceptions/legacy-auth-disabled.exception.ts +35 -0
  109. package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +109 -0
  110. package/src/core/modules/auth/interfaces/auth-provider.interface.ts +86 -0
  111. package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +6 -0
  112. package/src/core/modules/auth/services/core-auth.service.ts +245 -6
  113. package/src/core/modules/auth/services/legacy-auth-rate-limiter.service.ts +283 -0
  114. package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +254 -0
  115. package/src/core/modules/better-auth/README.md +698 -54
  116. package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
  117. package/src/core/modules/better-auth/better-auth-models.ts +0 -3
  118. package/src/core/modules/better-auth/better-auth-user.mapper.ts +805 -0
  119. package/src/core/modules/better-auth/better-auth.config.ts +5 -0
  120. package/src/core/modules/better-auth/better-auth.module.ts +107 -66
  121. package/src/core/modules/better-auth/better-auth.resolver.ts +88 -553
  122. package/src/core/modules/better-auth/better-auth.service.ts +0 -9
  123. package/src/core/modules/better-auth/better-auth.types.ts +25 -10
  124. package/src/core/modules/better-auth/core-better-auth.controller.ts +646 -0
  125. package/src/core/modules/better-auth/core-better-auth.resolver.ts +730 -0
  126. package/src/core/modules/better-auth/index.ts +9 -1
  127. package/src/core/modules/user/core-user.service.ts +131 -4
  128. package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +15 -0
  129. package/src/core.module.ts +257 -74
  130. package/src/index.ts +5 -0
  131. package/src/server/modules/auth/auth.resolver.ts +8 -0
  132. package/src/server/modules/better-auth/better-auth.controller.ts +41 -0
  133. package/src/server/modules/better-auth/better-auth.module.ts +88 -0
  134. package/src/server/modules/better-auth/better-auth.resolver.ts +210 -0
  135. package/src/server/modules/user/user.service.ts +4 -2
  136. package/src/server/server.module.ts +10 -1
@@ -2,10 +2,10 @@ import { NotFoundException } from '@nestjs/common';
2
2
  import {
3
3
  AggregateOptions,
4
4
  Document,
5
- FilterQuery,
6
5
  Model as MongooseModel,
7
6
  PipelineStage,
8
7
  Query,
8
+ QueryFilter,
9
9
  QueryOptions,
10
10
  } from 'mongoose';
11
11
 
@@ -147,7 +147,7 @@ export abstract class CrudService<
147
147
  * Get items via filter
148
148
  */
149
149
  async find(
150
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
150
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
151
151
  serviceOptions?: ServiceOptions,
152
152
  ): Promise<Model[]> {
153
153
  // If filter is not instance of FilterArgs a simple form with filterQuery and queryOptions is set
@@ -191,7 +191,7 @@ export abstract class CrudService<
191
191
  * Warning: Disables the handling of rights and restrictions!
192
192
  */
193
193
  async findForce(
194
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
194
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
195
195
  serviceOptions: ServiceOptions = {},
196
196
  ): Promise<Model[]> {
197
197
  serviceOptions = serviceOptions || {};
@@ -204,7 +204,7 @@ export abstract class CrudService<
204
204
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
205
205
  */
206
206
  async findRaw(
207
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
207
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
208
208
  serviceOptions: ServiceOptions = {},
209
209
  ): Promise<Model[]> {
210
210
  serviceOptions = serviceOptions || {};
@@ -216,7 +216,7 @@ export abstract class CrudService<
216
216
  * Get items and total count via filter
217
217
  */
218
218
  async findAndCount(
219
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
219
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
220
220
  serviceOptions?: ServiceOptions,
221
221
  ): Promise<{ items: Model[]; totalCount: number }> {
222
222
  // If filter is not instance of FilterArgs a simple form with filterQuery and queryOptions is set
@@ -280,10 +280,10 @@ export abstract class CrudService<
280
280
 
281
281
  // Find and process db items
282
282
  const collation = serviceOptions?.collation || ConfigService.get('mongoose.collation');
283
- const dbResult
284
- = (await this.mainDbModel.aggregate(aggregation, collation ? { collation } : {}).exec())[0] || {};
283
+ const dbResult =
284
+ (await this.mainDbModel.aggregate(aggregation, collation ? { collation } : {}).exec())[0] || {};
285
285
  dbResult.totalCount = dbResult.totalCount?.[0]?.total || 0;
286
- dbResult.items = dbResult.items?.map(item => this.mainDbModel.hydrate(item)) || [];
286
+ dbResult.items = dbResult.items?.map((item) => this.mainDbModel.hydrate(item)) || [];
287
287
  return dbResult;
288
288
  },
289
289
  { input: filter, outputPath: 'items', serviceOptions },
@@ -295,7 +295,7 @@ export abstract class CrudService<
295
295
  * Warning: Disables the handling of rights and restrictions!
296
296
  */
297
297
  async findAndCountForce(
298
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
298
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
299
299
  serviceOptions: ServiceOptions = {},
300
300
  ): Promise<{ items: Model[]; totalCount: number }> {
301
301
  serviceOptions.raw = true;
@@ -307,7 +307,7 @@ export abstract class CrudService<
307
307
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
308
308
  */
309
309
  async findAndCountRaw(
310
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
310
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
311
311
  serviceOptions: ServiceOptions = {},
312
312
  ): Promise<{ items: Model[]; totalCount: number }> {
313
313
  serviceOptions = serviceOptions || {};
@@ -319,7 +319,7 @@ export abstract class CrudService<
319
319
  * Find and update
320
320
  */
321
321
  async findAndUpdate(
322
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
322
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
323
323
  update: PlainObject<UpdateInput>,
324
324
  serviceOptions?: ServiceOptions,
325
325
  ): Promise<Model[]> {
@@ -348,7 +348,7 @@ export abstract class CrudService<
348
348
  * Warning: Disables the handling of rights and restrictions!
349
349
  */
350
350
  async findAndUpdateForce(
351
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
351
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
352
352
  update: PlainObject<UpdateInput>,
353
353
  serviceOptions: ServiceOptions = {},
354
354
  ): Promise<Model[]> {
@@ -362,7 +362,7 @@ export abstract class CrudService<
362
362
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
363
363
  */
364
364
  async findAndUpdateRaw(
365
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
365
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
366
366
  update: PlainObject<UpdateInput>,
367
367
  serviceOptions: ServiceOptions = {},
368
368
  ): Promise<Model[]> {
@@ -375,7 +375,7 @@ export abstract class CrudService<
375
375
  * Find one item via filter
376
376
  */
377
377
  async findOne(
378
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
378
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
379
379
  serviceOptions?: ServiceOptions,
380
380
  ): Promise<Model> {
381
381
  // If filter is not instance of FilterArgs a simple form with filterQuery and queryOptions is set
@@ -414,7 +414,7 @@ export abstract class CrudService<
414
414
  * Warning: Disables the handling of rights and restrictions!
415
415
  */
416
416
  async findOneForce(
417
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
417
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
418
418
  serviceOptions: ServiceOptions = {},
419
419
  ): Promise<Model> {
420
420
  serviceOptions = serviceOptions || {};
@@ -427,7 +427,7 @@ export abstract class CrudService<
427
427
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
428
428
  */
429
429
  async findOneRaw(
430
- filter?: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions; samples?: number },
430
+ filter?: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions; samples?: number },
431
431
  serviceOptions: ServiceOptions = {},
432
432
  ): Promise<Model> {
433
433
  serviceOptions = serviceOptions || {};
@@ -452,7 +452,7 @@ export abstract class CrudService<
452
452
  * CRUD alias for find
453
453
  */
454
454
  async read(
455
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
455
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
456
456
  serviceOptions?: ServiceOptions,
457
457
  ): Promise<Model[]>;
458
458
 
@@ -460,7 +460,7 @@ export abstract class CrudService<
460
460
  * CRUD alias for get or find
461
461
  */
462
462
  async read(
463
- input: FilterArgs | string | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
463
+ input: FilterArgs | string | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
464
464
  serviceOptions?: ServiceOptions,
465
465
  ): Promise<Model | Model[]> {
466
466
  if (typeof input === 'string') {
@@ -481,7 +481,7 @@ export abstract class CrudService<
481
481
  * Warning: Disables the handling of rights and restrictions!
482
482
  */
483
483
  async readForce(
484
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
484
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
485
485
  serviceOptions?: ServiceOptions,
486
486
  ): Promise<Model[]>;
487
487
 
@@ -490,7 +490,7 @@ export abstract class CrudService<
490
490
  * Warning: Disables the handling of rights and restrictions!
491
491
  */
492
492
  async readForce(
493
- input: FilterArgs | string | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
493
+ input: FilterArgs | string | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
494
494
  serviceOptions?: ServiceOptions,
495
495
  ): Promise<Model | Model[]> {
496
496
  if (typeof input === 'string') {
@@ -511,7 +511,7 @@ export abstract class CrudService<
511
511
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
512
512
  */
513
513
  async readRaw(
514
- filter: FilterArgs | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
514
+ filter: FilterArgs | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
515
515
  serviceOptions?: ServiceOptions,
516
516
  ): Promise<Model[]>;
517
517
 
@@ -520,7 +520,7 @@ export abstract class CrudService<
520
520
  * Warning: Disables the handling of rights and restrictions! The raw data may contain secrets (such as passwords).
521
521
  */
522
522
  async readRaw(
523
- input: FilterArgs | string | { filterQuery?: FilterQuery<any>; queryOptions?: QueryOptions },
523
+ input: FilterArgs | string | { filterQuery?: QueryFilter<any>; queryOptions?: QueryOptions },
524
524
  serviceOptions?: ServiceOptions,
525
525
  ): Promise<Model | Model[]> {
526
526
  if (typeof input === 'string') {
@@ -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
+ }