@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
@@ -19,7 +19,9 @@ import { EmailService } from './core/common/services/email.service';
19
19
  import { MailjetService } from './core/common/services/mailjet.service';
20
20
  import { ModelDocService } from './core/common/services/model-doc.service';
21
21
  import { TemplateService } from './core/common/services/template.service';
22
+ import { BetterAuthUserMapper } from './core/modules/better-auth/better-auth-user.mapper';
22
23
  import { BetterAuthModule } from './core/modules/better-auth/better-auth.module';
24
+ import { BetterAuthService } from './core/modules/better-auth/better-auth.service';
23
25
  import { CoreHealthCheckModule } from './core/modules/health-check/core-health-check.module';
24
26
 
25
27
  /**
@@ -66,10 +68,65 @@ export class CoreModule implements NestModule {
66
68
  }
67
69
 
68
70
  /**
69
- * Dynamic module
70
- * see https://docs.nestjs.com/modules#dynamic-modules
71
+ * Dynamic module initialization
72
+ *
73
+ * @see https://docs.nestjs.com/modules#dynamic-modules
74
+ *
75
+ * ## Signatures
76
+ *
77
+ * ### IAM-Only Signature (Recommended for new projects)
78
+ *
79
+ * ```typescript
80
+ * CoreModule.forRoot(envConfig)
81
+ * ```
82
+ *
83
+ * Use this for new projects that only use BetterAuth (IAM) for authentication.
84
+ * GraphQL Subscription authentication uses BetterAuth JWT tokens.
85
+ *
86
+ * **Requirements:**
87
+ * - Configure `betterAuth` in your config (enabled by default)
88
+ * - Create BetterAuthModule, Resolver, and Controller in your project
89
+ * - Inject BetterAuthUserMapper in UserService
90
+ *
91
+ * ### Legacy + IAM Signature (For existing projects)
92
+ *
93
+ * ```typescript
94
+ * CoreModule.forRoot(CoreAuthService, AuthModule.forRoot(envConfig.jwt), envConfig)
95
+ * ```
96
+ *
97
+ * @deprecated This 3-parameter signature is deprecated for new projects.
98
+ * Use the single-parameter signature `CoreModule.forRoot(envConfig)` instead.
99
+ * Existing projects can continue using this signature during migration.
100
+ *
101
+ * Use this for existing projects that need Legacy Auth for backwards compatibility.
102
+ * Both Legacy Auth and BetterAuth (IAM) can run in parallel.
103
+ *
104
+ * ## Migration Path
105
+ *
106
+ * 1. **Existing projects**: Use the 3-parameter signature, run Legacy + IAM in parallel
107
+ * 2. **Monitor**: Use `betterAuthMigrationStatus` query to track user migration
108
+ * 3. **Disable Legacy**: Set `auth.legacyEndpoints.enabled: false` after all users migrated
109
+ * 4. **New projects**: Use the single-parameter signature with IAM only
110
+ *
111
+ * @see https://github.com/lenneTech/nest-server/blob/develop/.claude/rules/module-deprecation.md
71
112
  */
72
- static forRoot(AuthService: any, AuthModule: any, options: Partial<IServerOptions>): DynamicModule {
113
+ static forRoot(options: Partial<IServerOptions>): DynamicModule;
114
+ /**
115
+ * @deprecated Use the single-parameter signature `CoreModule.forRoot(envConfig)` for new projects.
116
+ * This 3-parameter signature is for existing projects during migration to IAM.
117
+ */
118
+ static forRoot(AuthService: any, AuthModule: any, options: Partial<IServerOptions>): DynamicModule;
119
+ static forRoot(
120
+ authServiceOrOptions: any,
121
+ authModuleOrUndefined?: any,
122
+ optionsOrUndefined?: Partial<IServerOptions>,
123
+ ): DynamicModule {
124
+ // Detect which signature was used
125
+ const isIamOnlyMode = authModuleOrUndefined === undefined && optionsOrUndefined === undefined;
126
+ const AuthService = isIamOnlyMode ? null : authServiceOrOptions;
127
+ const AuthModule = isIamOnlyMode ? null : authModuleOrUndefined;
128
+ const options: Partial<IServerOptions> = isIamOnlyMode ? authServiceOrOptions : optionsOrUndefined;
129
+
73
130
  // Process config
74
131
  let cors = {};
75
132
  if (options?.cookies) {
@@ -78,73 +135,17 @@ export class CoreModule implements NestModule {
78
135
  origin: true,
79
136
  };
80
137
  }
138
+
139
+ // Build GraphQL driver configuration based on auth mode
140
+ const graphQlDriverConfig = isIamOnlyMode
141
+ ? this.buildIamOnlyGraphQlDriver(cors, options)
142
+ : this.buildLegacyGraphQlDriver(AuthService, AuthModule, cors, options);
143
+
81
144
  const config: IServerOptions = merge(
82
145
  {
83
146
  env: 'develop',
84
147
  graphQl: {
85
- driver: {
86
- imports: [AuthModule],
87
- inject: [AuthService],
88
- useFactory: async (authService: any) =>
89
- Object.assign(
90
- {
91
- autoSchemaFile: 'schema.gql',
92
- context: ({ req, res }) => ({ req, res }),
93
- cors,
94
- installSubscriptionHandlers: true,
95
- subscriptions: {
96
- 'graphql-ws': {
97
- context: ({ extra }) => extra,
98
- onConnect: async (context: Context<any>) => {
99
- const { connectionParams, extra } = context;
100
- if (config.graphQl.enableSubscriptionAuth) {
101
- // get authToken from authorization header
102
- const headers = this.getHeaderFromArray(extra.request?.rawHeaders);
103
- const authToken: string =
104
- connectionParams?.Authorization?.split(' ')[1] ?? headers.Authorization?.split(' ')[1];
105
- if (authToken) {
106
- // verify authToken/getJwtPayLoad
107
- const payload = authService.decodeJwt(authToken);
108
- const user = await authService.validateUser(payload);
109
- if (!user) {
110
- throw new UnauthorizedException('No user found for token');
111
- }
112
- // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
113
- extra.user = user;
114
- extra.headers = connectionParams ?? headers;
115
- return extra;
116
- }
117
-
118
- throw new UnauthorizedException('Missing authentication token');
119
- }
120
- },
121
- },
122
- 'subscriptions-transport-ws': {
123
- onConnect: async (connectionParams) => {
124
- if (config.graphQl.enableSubscriptionAuth) {
125
- // get authToken from authorization header
126
- const authToken: string = connectionParams?.Authorization?.split(' ')[1];
127
-
128
- if (authToken) {
129
- // verify authToken/getJwtPayLoad
130
- const payload = authService.decodeJwt(authToken);
131
- const user = await authService.validateUser(payload);
132
- if (!user) {
133
- throw new UnauthorizedException('No user found for token');
134
- }
135
- // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
136
- return { headers: connectionParams, user };
137
- }
138
-
139
- throw new UnauthorizedException('Missing authentication token');
140
- }
141
- },
142
- },
143
- },
144
- },
145
- options?.graphQl?.driver,
146
- ),
147
- },
148
+ driver: graphQlDriverConfig,
148
149
  enableSubscriptionAuth: true,
149
150
  },
150
151
  mongoose: {
@@ -229,16 +230,19 @@ export class CoreModule implements NestModule {
229
230
  imports.push(CoreHealthCheckModule);
230
231
  }
231
232
 
232
- // Add BetterAuthModule only if autoRegister is explicitly true
233
- // By default (autoRegister: false), projects integrate via an extended module in their project
234
- if (config.betterAuth?.enabled !== false && config.betterAuth?.autoRegister === true) {
235
- imports.push(
236
- BetterAuthModule.forRoot({
237
- config: config.betterAuth || {},
238
- // Pass JWT secrets for backwards compatibility fallback
239
- fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
240
- }),
241
- );
233
+ // Add BetterAuthModule based on mode
234
+ // IAM-only mode: Always register BetterAuthModule (required for subscription auth)
235
+ // Legacy mode: Only register if autoRegister is explicitly true
236
+ if (config.betterAuth?.enabled !== false) {
237
+ if (isIamOnlyMode || config.betterAuth?.autoRegister === true) {
238
+ imports.push(
239
+ BetterAuthModule.forRoot({
240
+ config: config.betterAuth || {},
241
+ // Pass JWT secrets for backwards compatibility fallback
242
+ fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
243
+ }),
244
+ );
245
+ }
242
246
  }
243
247
 
244
248
  // Set exports
@@ -255,4 +259,182 @@ export class CoreModule implements NestModule {
255
259
  providers,
256
260
  };
257
261
  }
262
+
263
+ // =============================================================================
264
+ // GraphQL Driver Configuration Helpers
265
+ // =============================================================================
266
+
267
+ /**
268
+ * Build GraphQL driver configuration for IAM-only mode
269
+ *
270
+ * Uses BetterAuthService for subscription authentication via JWT tokens.
271
+ * This is the recommended mode for new projects.
272
+ */
273
+ private static buildIamOnlyGraphQlDriver(cors: object, options: Partial<IServerOptions>) {
274
+ return {
275
+ imports: [BetterAuthModule],
276
+ inject: [BetterAuthService, BetterAuthUserMapper],
277
+ useFactory: async (betterAuthService: BetterAuthService, userMapper: BetterAuthUserMapper) =>
278
+ Object.assign(
279
+ {
280
+ autoSchemaFile: 'schema.gql',
281
+ context: ({ req, res }) => ({ req, res }),
282
+ cors,
283
+ installSubscriptionHandlers: true,
284
+ subscriptions: {
285
+ 'graphql-ws': {
286
+ context: ({ extra }) => extra,
287
+ onConnect: async (context: Context<any>) => {
288
+ const { connectionParams, extra } = context;
289
+ const enableAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
290
+
291
+ if (enableAuth) {
292
+ // Get headers from raw headers or connection params
293
+ const headers = CoreModule.getHeaderFromArray(extra.request?.rawHeaders);
294
+ const authToken: string =
295
+ connectionParams?.Authorization?.split(' ')[1] ?? headers.Authorization?.split(' ')[1];
296
+
297
+ if (authToken) {
298
+ // Validate via BetterAuth session
299
+ const { session, user: sessionUser } = await betterAuthService.getSession({
300
+ headers: { authorization: `Bearer ${authToken}` },
301
+ });
302
+
303
+ if (!session || !sessionUser) {
304
+ throw new UnauthorizedException('Invalid or expired session');
305
+ }
306
+
307
+ // Map to full user with roles
308
+ const user = await userMapper.mapSessionUser(sessionUser);
309
+ if (!user) {
310
+ throw new UnauthorizedException('User not found');
311
+ }
312
+
313
+ extra.user = user;
314
+ extra.headers = connectionParams ?? headers;
315
+ return extra;
316
+ }
317
+
318
+ throw new UnauthorizedException('Missing authentication token');
319
+ }
320
+ },
321
+ },
322
+ 'subscriptions-transport-ws': {
323
+ onConnect: async (connectionParams) => {
324
+ const enableAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
325
+
326
+ if (enableAuth) {
327
+ const authToken: string = connectionParams?.Authorization?.split(' ')[1];
328
+
329
+ if (authToken) {
330
+ // Validate via BetterAuth session
331
+ const { session, user: sessionUser } = await betterAuthService.getSession({
332
+ headers: { authorization: `Bearer ${authToken}` },
333
+ });
334
+
335
+ if (!session || !sessionUser) {
336
+ throw new UnauthorizedException('Invalid or expired session');
337
+ }
338
+
339
+ // Map to full user with roles
340
+ const user = await userMapper.mapSessionUser(sessionUser);
341
+ if (!user) {
342
+ throw new UnauthorizedException('User not found');
343
+ }
344
+
345
+ return { headers: connectionParams, user };
346
+ }
347
+
348
+ throw new UnauthorizedException('Missing authentication token');
349
+ }
350
+ },
351
+ },
352
+ },
353
+ },
354
+ options?.graphQl?.driver,
355
+ ),
356
+ };
357
+ }
358
+
359
+ /**
360
+ * Build GraphQL driver configuration for Legacy Auth mode
361
+ *
362
+ * Uses the provided AuthService for subscription authentication via JWT tokens.
363
+ * This is for existing projects that need backwards compatibility.
364
+ *
365
+ * @deprecated Use IAM-only mode (single-parameter forRoot) for new projects
366
+ */
367
+ private static buildLegacyGraphQlDriver(
368
+ AuthService: any,
369
+ AuthModule: any,
370
+ cors: object,
371
+ options: Partial<IServerOptions>,
372
+ ) {
373
+ // Store config reference for use in callbacks
374
+ const enableSubscriptionAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
375
+
376
+ return {
377
+ imports: [AuthModule],
378
+ inject: [AuthService],
379
+ useFactory: async (authService: any) =>
380
+ Object.assign(
381
+ {
382
+ autoSchemaFile: 'schema.gql',
383
+ context: ({ req, res }) => ({ req, res }),
384
+ cors,
385
+ installSubscriptionHandlers: true,
386
+ subscriptions: {
387
+ 'graphql-ws': {
388
+ context: ({ extra }) => extra,
389
+ onConnect: async (context: Context<any>) => {
390
+ const { connectionParams, extra } = context;
391
+ if (enableSubscriptionAuth) {
392
+ // get authToken from authorization header
393
+ const headers = CoreModule.getHeaderFromArray(extra.request?.rawHeaders);
394
+ const authToken: string =
395
+ connectionParams?.Authorization?.split(' ')[1] ?? headers.Authorization?.split(' ')[1];
396
+ if (authToken) {
397
+ // verify authToken/getJwtPayLoad
398
+ const payload = authService.decodeJwt(authToken);
399
+ const user = await authService.validateUser(payload);
400
+ if (!user) {
401
+ throw new UnauthorizedException('No user found for token');
402
+ }
403
+ // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
404
+ extra.user = user;
405
+ extra.headers = connectionParams ?? headers;
406
+ return extra;
407
+ }
408
+
409
+ throw new UnauthorizedException('Missing authentication token');
410
+ }
411
+ },
412
+ },
413
+ 'subscriptions-transport-ws': {
414
+ onConnect: async (connectionParams) => {
415
+ if (enableSubscriptionAuth) {
416
+ // get authToken from authorization header
417
+ const authToken: string = connectionParams?.Authorization?.split(' ')[1];
418
+
419
+ if (authToken) {
420
+ // verify authToken/getJwtPayLoad
421
+ const payload = authService.decodeJwt(authToken);
422
+ const user = await authService.validateUser(payload);
423
+ if (!user) {
424
+ throw new UnauthorizedException('No user found for token');
425
+ }
426
+ // the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
427
+ return { headers: connectionParams, user };
428
+ }
429
+
430
+ throw new UnauthorizedException('Missing authentication token');
431
+ }
432
+ },
433
+ },
434
+ },
435
+ },
436
+ options?.graphQl?.driver,
437
+ ),
438
+ };
439
+ }
258
440
  }
package/src/index.ts CHANGED
@@ -108,15 +108,19 @@ export * from './core/modules/auth/core-auth.resolver';
108
108
  export * from './core/modules/auth/exceptions/expired-refresh-token.exception';
109
109
  export * from './core/modules/auth/exceptions/expired-token.exception';
110
110
  export * from './core/modules/auth/exceptions/invalid-token.exception';
111
+ export * from './core/modules/auth/exceptions/legacy-auth-disabled.exception';
111
112
  export * from './core/modules/auth/guards/auth.guard';
113
+ export * from './core/modules/auth/guards/legacy-auth-rate-limit.guard';
112
114
  export * from './core/modules/auth/guards/roles.guard';
113
115
  export * from './core/modules/auth/inputs/core-auth-sign-in.input';
114
116
  export * from './core/modules/auth/inputs/core-auth-sign-up.input';
117
+ export * from './core/modules/auth/interfaces/auth-provider.interface';
115
118
  export * from './core/modules/auth/interfaces/core-auth-user.interface';
116
119
  export * from './core/modules/auth/interfaces/core-token-data.interface';
117
120
  export * from './core/modules/auth/interfaces/jwt-payload.interface';
118
121
  export * from './core/modules/auth/services/core-auth-user.service';
119
122
  export * from './core/modules/auth/services/core-auth.service';
123
+ export * from './core/modules/auth/services/legacy-auth-rate-limiter.service';
120
124
  export * from './core/modules/auth/strategies/jwt-refresh.strategy';
121
125
  export * from './core/modules/auth/strategies/jwt.strategy';
122
126
  export * from './core/modules/auth/tokens.decorator';
@@ -162,6 +166,7 @@ export * from './core/modules/user/core-user.model';
162
166
  export * from './core/modules/user/core-user.service';
163
167
  export * from './core/modules/user/inputs/core-user-create.input';
164
168
  export * from './core/modules/user/inputs/core-user.input';
169
+ export * from './core/modules/user/interfaces/core-user-service-options.interface';
165
170
 
166
171
  // =====================================================================================================================
167
172
  // Tests
@@ -30,6 +30,8 @@ export class AuthResolver extends CoreAuthResolver {
30
30
 
31
31
  /**
32
32
  * SignIn for User
33
+ *
34
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
33
35
  */
34
36
  @Mutation(() => Auth, { description: 'Sign in and get JWT token' })
35
37
  @Roles(RoleEnum.S_EVERYONE)
@@ -38,6 +40,8 @@ export class AuthResolver extends CoreAuthResolver {
38
40
  @Context() ctx: { res: ResponseType },
39
41
  @Args('input') input: AuthSignInInput,
40
42
  ): Promise<Auth> {
43
+ // Check if legacy endpoints are enabled before proceeding
44
+ this.checkLegacyGraphQLEnabled('signIn');
41
45
  const result = await this.authService.signIn(input, {
42
46
  ...serviceOptions,
43
47
  inputType: AuthSignInInput,
@@ -47,6 +51,8 @@ export class AuthResolver extends CoreAuthResolver {
47
51
 
48
52
  /**
49
53
  * Sign up for user
54
+ *
55
+ * @throws LegacyAuthDisabledException if legacy endpoints are disabled
50
56
  */
51
57
  @Mutation(() => Auth, {
52
58
  description: 'Sign up user and get JWT token',
@@ -57,6 +63,8 @@ export class AuthResolver extends CoreAuthResolver {
57
63
  @Context() ctx: { res: ResponseType },
58
64
  @Args('input') input: AuthSignUpInput,
59
65
  ): Promise<Auth> {
66
+ // Check if legacy endpoints are enabled before proceeding
67
+ this.checkLegacyGraphQLEnabled('signUp');
60
68
  const result = await this.authService.signUp(input, serviceOptions);
61
69
  return this.processCookies(ctx, result);
62
70
  }
@@ -7,6 +7,7 @@ import { RoleEnum } from '../../../core/common/enums/role.enum';
7
7
  import { AuthGuardStrategy } from '../../../core/modules/auth/auth-guard-strategy.enum';
8
8
  import { AuthGuard } from '../../../core/modules/auth/guards/auth.guard';
9
9
  import { BetterAuthAuthModel } from '../../../core/modules/better-auth/better-auth-auth.model';
10
+ import { BetterAuthMigrationStatusModel } from '../../../core/modules/better-auth/better-auth-migration-status.model';
10
11
  import {
11
12
  BetterAuth2FASetupModel,
12
13
  BetterAuthFeaturesModel,
@@ -78,6 +79,14 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
78
79
  return super.betterAuthFeatures();
79
80
  }
80
81
 
82
+ @Query(() => BetterAuthMigrationStatusModel, {
83
+ description: 'Get migration status from Legacy Auth to Better-Auth (IAM) - Admin only',
84
+ })
85
+ @Roles(RoleEnum.ADMIN)
86
+ override async betterAuthMigrationStatus(): Promise<BetterAuthMigrationStatusModel> {
87
+ return super.betterAuthMigrationStatus();
88
+ }
89
+
81
90
  @Query(() => [BetterAuthPasskeyModel], {
82
91
  description: 'List passkeys for the current user',
83
92
  nullable: true,
@@ -1,4 +1,4 @@
1
- import { Inject, Injectable, UnauthorizedException, UnprocessableEntityException } from '@nestjs/common';
1
+ import { Inject, Injectable, Optional, UnauthorizedException, UnprocessableEntityException } from '@nestjs/common';
2
2
  import { InjectModel } from '@nestjs/mongoose';
3
3
  import fs = require('fs');
4
4
  import { PubSub } from 'graphql-subscriptions';
@@ -9,6 +9,7 @@ import { ServiceOptions } from '../../../core/common/interfaces/service-options.
9
9
  import { ConfigService } from '../../../core/common/services/config.service';
10
10
  import { EmailService } from '../../../core/common/services/email.service';
11
11
  import { CoreModelConstructor } from '../../../core/common/types/core-model-constructor.type';
12
+ import { BetterAuthUserMapper } from '../../../core/modules/better-auth/better-auth-user.mapper';
12
13
  import { CoreUserService } from '../../../core/modules/user/core-user.service';
13
14
  import { UserCreateInput } from './inputs/user-create.input';
14
15
  import { UserInput } from './inputs/user.input';
@@ -32,8 +33,9 @@ export class UserService extends CoreUserService<User, UserInput, UserCreateInpu
32
33
  @Inject('USER_CLASS') protected override readonly mainModelConstructor: CoreModelConstructor<User>,
33
34
  @InjectModel('User') protected override readonly mainDbModel: Model<UserDocument>,
34
35
  @Inject('PUB_SUB') protected readonly pubSub: PubSub,
36
+ @Optional() private readonly betterAuthUserMapper?: BetterAuthUserMapper,
35
37
  ) {
36
- super(configService, emailService, mainDbModel, mainModelConstructor);
38
+ super(configService, emailService, mainDbModel, mainModelConstructor, { betterAuthUserMapper });
37
39
  }
38
40
 
39
41
  // ===================================================================================================================