@lenne.tech/nest-server 11.7.0 → 11.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/config.env.js +17 -1
- package/dist/config.env.js.map +1 -1
- package/dist/core/common/interfaces/server-options.interface.d.ts +35 -15
- package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.controller.js +29 -3
- package/dist/core/modules/auth/core-auth.controller.js.map +1 -1
- package/dist/core/modules/auth/core-auth.module.js +14 -1
- package/dist/core/modules/auth/core-auth.module.js.map +1 -1
- package/dist/core/modules/auth/core-auth.resolver.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.resolver.js +21 -3
- package/dist/core/modules/auth/core-auth.resolver.js.map +1 -1
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.d.ts +4 -0
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js +17 -0
- package/dist/core/modules/auth/exceptions/legacy-auth-disabled.exception.js.map +1 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.d.ts +9 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js +74 -0
- package/dist/core/modules/auth/guards/legacy-auth-rate-limit.guard.js.map +1 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.d.ts +7 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.js +5 -0
- package/dist/core/modules/auth/interfaces/auth-provider.interface.js.map +1 -0
- package/dist/core/modules/auth/interfaces/core-auth-user.interface.d.ts +1 -0
- package/dist/core/modules/auth/services/core-auth.service.d.ts +10 -1
- package/dist/core/modules/auth/services/core-auth.service.js +141 -9
- package/dist/core/modules/auth/services/core-auth.service.js.map +1 -1
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.d.ts +31 -0
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js +153 -0
- package/dist/core/modules/auth/services/legacy-auth-rate-limiter.service.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.d.ts +10 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js +57 -0
- package/dist/core/modules/better-auth/better-auth-migration-status.model.js.map +1 -0
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js +1 -1
- package/dist/core/modules/better-auth/better-auth-rate-limiter.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth-user.mapper.d.ts +33 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js +395 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.config.js +29 -10
- package/dist/core/modules/better-auth/better-auth.config.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.middleware.d.ts +1 -0
- package/dist/core/modules/better-auth/better-auth.middleware.js +55 -1
- package/dist/core/modules/better-auth/better-auth.middleware.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.module.d.ts +1 -1
- package/dist/core/modules/better-auth/better-auth.module.js +46 -18
- package/dist/core/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.resolver.js +0 -11
- package/dist/core/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.service.d.ts +22 -1
- package/dist/core/modules/better-auth/better-auth.service.js +209 -8
- package/dist/core/modules/better-auth/better-auth.service.js.map +1 -1
- package/dist/core/modules/better-auth/better-auth.types.d.ts +2 -0
- package/dist/core/modules/better-auth/better-auth.types.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.controller.d.ts +1 -0
- package/dist/core/modules/better-auth/core-better-auth.controller.js +15 -2
- package/dist/core/modules/better-auth/core-better-auth.controller.js.map +1 -1
- package/dist/core/modules/better-auth/core-better-auth.resolver.d.ts +7 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +72 -12
- package/dist/core/modules/better-auth/core-better-auth.resolver.js.map +1 -1
- package/dist/core/modules/better-auth/index.d.ts +1 -0
- package/dist/core/modules/better-auth/index.js +1 -0
- package/dist/core/modules/better-auth/index.js.map +1 -1
- package/dist/core/modules/user/core-user.service.d.ts +7 -1
- package/dist/core/modules/user/core-user.service.js +57 -3
- package/dist/core/modules/user/core-user.service.js.map +1 -1
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.d.ts +4 -0
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.js +3 -0
- package/dist/core/modules/user/interfaces/core-user-service-options.interface.js.map +1 -0
- package/dist/core.module.d.ts +3 -0
- package/dist/core.module.js +136 -55
- package/dist/core.module.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/server/modules/auth/auth.resolver.js +2 -0
- package/dist/server/modules/auth/auth.resolver.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.module.d.ts +1 -1
- package/dist/server/modules/better-auth/better-auth.module.js +2 -1
- package/dist/server/modules/better-auth/better-auth.module.js.map +1 -1
- package/dist/server/modules/better-auth/better-auth.resolver.d.ts +5 -0
- package/dist/server/modules/better-auth/better-auth.resolver.js +27 -11
- package/dist/server/modules/better-auth/better-auth.resolver.js.map +1 -1
- package/dist/server/modules/user/user.controller.js +0 -8
- package/dist/server/modules/user/user.controller.js.map +1 -1
- package/dist/server/modules/user/user.service.d.ts +3 -1
- package/dist/server/modules/user/user.service.js +7 -3
- package/dist/server/modules/user/user.service.js.map +1 -1
- package/dist/tsconfig.build.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/config.env.ts +32 -2
- package/src/core/common/interfaces/server-options.interface.ts +304 -58
- package/src/core/modules/auth/core-auth.controller.ts +94 -6
- package/src/core/modules/auth/core-auth.module.ts +15 -1
- package/src/core/modules/auth/core-auth.resolver.ts +71 -3
- package/src/core/modules/auth/exceptions/legacy-auth-disabled.exception.ts +35 -0
- package/src/core/modules/auth/guards/legacy-auth-rate-limit.guard.ts +109 -0
- package/src/core/modules/auth/interfaces/auth-provider.interface.ts +86 -0
- package/src/core/modules/auth/interfaces/core-auth-user.interface.ts +6 -0
- package/src/core/modules/auth/services/core-auth.service.ts +245 -6
- package/src/core/modules/auth/services/legacy-auth-rate-limiter.service.ts +283 -0
- package/src/core/modules/better-auth/INTEGRATION-CHECKLIST.md +255 -0
- package/src/core/modules/better-auth/README.md +565 -208
- package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
- package/src/core/modules/better-auth/better-auth-rate-limiter.service.ts +1 -1
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +737 -0
- package/src/core/modules/better-auth/better-auth.config.ts +45 -15
- package/src/core/modules/better-auth/better-auth.middleware.ts +85 -2
- package/src/core/modules/better-auth/better-auth.module.ts +83 -27
- package/src/core/modules/better-auth/better-auth.resolver.ts +0 -11
- package/src/core/modules/better-auth/better-auth.service.ts +367 -12
- package/src/core/modules/better-auth/better-auth.types.ts +16 -0
- package/src/core/modules/better-auth/core-better-auth.controller.ts +44 -3
- package/src/core/modules/better-auth/core-better-auth.resolver.ts +136 -16
- package/src/core/modules/better-auth/index.ts +1 -0
- package/src/core/modules/user/core-user.service.ts +131 -4
- package/src/core/modules/user/interfaces/core-user-service-options.interface.ts +15 -0
- package/src/core.module.ts +264 -76
- package/src/index.ts +5 -0
- package/src/server/modules/auth/auth.resolver.ts +8 -0
- package/src/server/modules/better-auth/better-auth.module.ts +9 -3
- package/src/server/modules/better-auth/better-auth.resolver.ts +18 -11
- package/src/server/modules/user/user.controller.ts +1 -9
- package/src/server/modules/user/user.service.ts +4 -2
package/src/core.module.ts
CHANGED
|
@@ -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
|
-
*
|
|
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(
|
|
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,25 @@ export class CoreModule implements NestModule {
|
|
|
229
230
|
imports.push(CoreHealthCheckModule);
|
|
230
231
|
}
|
|
231
232
|
|
|
232
|
-
// Add BetterAuthModule
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
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
|
+
// betterAuth can be: boolean | IBetterAuth | undefined
|
|
237
|
+
const betterAuthConfig = config.betterAuth;
|
|
238
|
+
const isBetterAuthEnabled =
|
|
239
|
+
betterAuthConfig === true || (typeof betterAuthConfig === 'object' && betterAuthConfig?.enabled !== false);
|
|
240
|
+
const isAutoRegister = typeof betterAuthConfig === 'object' && betterAuthConfig?.autoRegister === true;
|
|
241
|
+
|
|
242
|
+
if (isBetterAuthEnabled) {
|
|
243
|
+
if (isIamOnlyMode || isAutoRegister) {
|
|
244
|
+
imports.push(
|
|
245
|
+
BetterAuthModule.forRoot({
|
|
246
|
+
config: betterAuthConfig === true ? {} : betterAuthConfig || {},
|
|
247
|
+
// Pass JWT secrets for backwards compatibility fallback
|
|
248
|
+
fallbackSecrets: [config.jwt?.secret, config.jwt?.refresh?.secret],
|
|
249
|
+
}),
|
|
250
|
+
);
|
|
251
|
+
}
|
|
242
252
|
}
|
|
243
253
|
|
|
244
254
|
// Set exports
|
|
@@ -255,4 +265,182 @@ export class CoreModule implements NestModule {
|
|
|
255
265
|
providers,
|
|
256
266
|
};
|
|
257
267
|
}
|
|
268
|
+
|
|
269
|
+
// =============================================================================
|
|
270
|
+
// GraphQL Driver Configuration Helpers
|
|
271
|
+
// =============================================================================
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Build GraphQL driver configuration for IAM-only mode
|
|
275
|
+
*
|
|
276
|
+
* Uses BetterAuthService for subscription authentication via JWT tokens.
|
|
277
|
+
* This is the recommended mode for new projects.
|
|
278
|
+
*/
|
|
279
|
+
private static buildIamOnlyGraphQlDriver(cors: object, options: Partial<IServerOptions>) {
|
|
280
|
+
return {
|
|
281
|
+
imports: [BetterAuthModule],
|
|
282
|
+
inject: [BetterAuthService, BetterAuthUserMapper],
|
|
283
|
+
useFactory: async (betterAuthService: BetterAuthService, userMapper: BetterAuthUserMapper) =>
|
|
284
|
+
Object.assign(
|
|
285
|
+
{
|
|
286
|
+
autoSchemaFile: 'schema.gql',
|
|
287
|
+
context: ({ req, res }) => ({ req, res }),
|
|
288
|
+
cors,
|
|
289
|
+
installSubscriptionHandlers: true,
|
|
290
|
+
subscriptions: {
|
|
291
|
+
'graphql-ws': {
|
|
292
|
+
context: ({ extra }) => extra,
|
|
293
|
+
onConnect: async (context: Context<any>) => {
|
|
294
|
+
const { connectionParams, extra } = context;
|
|
295
|
+
const enableAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
|
|
296
|
+
|
|
297
|
+
if (enableAuth) {
|
|
298
|
+
// Get headers from raw headers or connection params
|
|
299
|
+
const headers = CoreModule.getHeaderFromArray(extra.request?.rawHeaders);
|
|
300
|
+
const authToken: string =
|
|
301
|
+
connectionParams?.Authorization?.split(' ')[1] ?? headers.Authorization?.split(' ')[1];
|
|
302
|
+
|
|
303
|
+
if (authToken) {
|
|
304
|
+
// Validate via BetterAuth session
|
|
305
|
+
const { session, user: sessionUser } = await betterAuthService.getSession({
|
|
306
|
+
headers: { authorization: `Bearer ${authToken}` },
|
|
307
|
+
});
|
|
308
|
+
|
|
309
|
+
if (!session || !sessionUser) {
|
|
310
|
+
throw new UnauthorizedException('Invalid or expired session');
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
// Map to full user with roles
|
|
314
|
+
const user = await userMapper.mapSessionUser(sessionUser);
|
|
315
|
+
if (!user) {
|
|
316
|
+
throw new UnauthorizedException('User not found');
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
extra.user = user;
|
|
320
|
+
extra.headers = connectionParams ?? headers;
|
|
321
|
+
return extra;
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
throw new UnauthorizedException('Missing authentication token');
|
|
325
|
+
}
|
|
326
|
+
},
|
|
327
|
+
},
|
|
328
|
+
'subscriptions-transport-ws': {
|
|
329
|
+
onConnect: async (connectionParams) => {
|
|
330
|
+
const enableAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
|
|
331
|
+
|
|
332
|
+
if (enableAuth) {
|
|
333
|
+
const authToken: string = connectionParams?.Authorization?.split(' ')[1];
|
|
334
|
+
|
|
335
|
+
if (authToken) {
|
|
336
|
+
// Validate via BetterAuth session
|
|
337
|
+
const { session, user: sessionUser } = await betterAuthService.getSession({
|
|
338
|
+
headers: { authorization: `Bearer ${authToken}` },
|
|
339
|
+
});
|
|
340
|
+
|
|
341
|
+
if (!session || !sessionUser) {
|
|
342
|
+
throw new UnauthorizedException('Invalid or expired session');
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Map to full user with roles
|
|
346
|
+
const user = await userMapper.mapSessionUser(sessionUser);
|
|
347
|
+
if (!user) {
|
|
348
|
+
throw new UnauthorizedException('User not found');
|
|
349
|
+
}
|
|
350
|
+
|
|
351
|
+
return { headers: connectionParams, user };
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
throw new UnauthorizedException('Missing authentication token');
|
|
355
|
+
}
|
|
356
|
+
},
|
|
357
|
+
},
|
|
358
|
+
},
|
|
359
|
+
},
|
|
360
|
+
options?.graphQl?.driver,
|
|
361
|
+
),
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
/**
|
|
366
|
+
* Build GraphQL driver configuration for Legacy Auth mode
|
|
367
|
+
*
|
|
368
|
+
* Uses the provided AuthService for subscription authentication via JWT tokens.
|
|
369
|
+
* This is for existing projects that need backwards compatibility.
|
|
370
|
+
*
|
|
371
|
+
* @deprecated Use IAM-only mode (single-parameter forRoot) for new projects
|
|
372
|
+
*/
|
|
373
|
+
private static buildLegacyGraphQlDriver(
|
|
374
|
+
AuthService: any,
|
|
375
|
+
AuthModule: any,
|
|
376
|
+
cors: object,
|
|
377
|
+
options: Partial<IServerOptions>,
|
|
378
|
+
) {
|
|
379
|
+
// Store config reference for use in callbacks
|
|
380
|
+
const enableSubscriptionAuth = options?.graphQl?.enableSubscriptionAuth ?? true;
|
|
381
|
+
|
|
382
|
+
return {
|
|
383
|
+
imports: [AuthModule],
|
|
384
|
+
inject: [AuthService],
|
|
385
|
+
useFactory: async (authService: any) =>
|
|
386
|
+
Object.assign(
|
|
387
|
+
{
|
|
388
|
+
autoSchemaFile: 'schema.gql',
|
|
389
|
+
context: ({ req, res }) => ({ req, res }),
|
|
390
|
+
cors,
|
|
391
|
+
installSubscriptionHandlers: true,
|
|
392
|
+
subscriptions: {
|
|
393
|
+
'graphql-ws': {
|
|
394
|
+
context: ({ extra }) => extra,
|
|
395
|
+
onConnect: async (context: Context<any>) => {
|
|
396
|
+
const { connectionParams, extra } = context;
|
|
397
|
+
if (enableSubscriptionAuth) {
|
|
398
|
+
// get authToken from authorization header
|
|
399
|
+
const headers = CoreModule.getHeaderFromArray(extra.request?.rawHeaders);
|
|
400
|
+
const authToken: string =
|
|
401
|
+
connectionParams?.Authorization?.split(' ')[1] ?? headers.Authorization?.split(' ')[1];
|
|
402
|
+
if (authToken) {
|
|
403
|
+
// verify authToken/getJwtPayLoad
|
|
404
|
+
const payload = authService.decodeJwt(authToken);
|
|
405
|
+
const user = await authService.validateUser(payload);
|
|
406
|
+
if (!user) {
|
|
407
|
+
throw new UnauthorizedException('No user found for token');
|
|
408
|
+
}
|
|
409
|
+
// the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
|
|
410
|
+
extra.user = user;
|
|
411
|
+
extra.headers = connectionParams ?? headers;
|
|
412
|
+
return extra;
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
throw new UnauthorizedException('Missing authentication token');
|
|
416
|
+
}
|
|
417
|
+
},
|
|
418
|
+
},
|
|
419
|
+
'subscriptions-transport-ws': {
|
|
420
|
+
onConnect: async (connectionParams) => {
|
|
421
|
+
if (enableSubscriptionAuth) {
|
|
422
|
+
// get authToken from authorization header
|
|
423
|
+
const authToken: string = connectionParams?.Authorization?.split(' ')[1];
|
|
424
|
+
|
|
425
|
+
if (authToken) {
|
|
426
|
+
// verify authToken/getJwtPayLoad
|
|
427
|
+
const payload = authService.decodeJwt(authToken);
|
|
428
|
+
const user = await authService.validateUser(payload);
|
|
429
|
+
if (!user) {
|
|
430
|
+
throw new UnauthorizedException('No user found for token');
|
|
431
|
+
}
|
|
432
|
+
// the user/jwtPayload object found will be available as context.currentUser/jwtPayload in your GraphQL resolvers
|
|
433
|
+
return { headers: connectionParams, user };
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
throw new UnauthorizedException('Missing authentication token');
|
|
437
|
+
}
|
|
438
|
+
},
|
|
439
|
+
},
|
|
440
|
+
},
|
|
441
|
+
},
|
|
442
|
+
options?.graphQl?.driver,
|
|
443
|
+
),
|
|
444
|
+
};
|
|
445
|
+
}
|
|
258
446
|
}
|
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
|
}
|
|
@@ -10,9 +10,13 @@ import { BetterAuthResolver } from './better-auth.resolver';
|
|
|
10
10
|
*/
|
|
11
11
|
export interface ServerBetterAuthModuleOptions {
|
|
12
12
|
/**
|
|
13
|
-
* Better-auth configuration
|
|
13
|
+
* Better-auth configuration.
|
|
14
|
+
* Accepts:
|
|
15
|
+
* - `true`: Enable with all defaults (including JWT)
|
|
16
|
+
* - `false`: Disable BetterAuth
|
|
17
|
+
* - `{ ... }`: Enable with custom configuration
|
|
14
18
|
*/
|
|
15
|
-
config: IBetterAuth;
|
|
19
|
+
config: boolean | IBetterAuth;
|
|
16
20
|
|
|
17
21
|
/**
|
|
18
22
|
* Fallback secrets for backwards compatibility with JWT config.
|
|
@@ -63,7 +67,9 @@ export class BetterAuthModule {
|
|
|
63
67
|
const { config, fallbackSecrets } = options;
|
|
64
68
|
|
|
65
69
|
// If better-auth is explicitly disabled, return minimal module
|
|
66
|
-
|
|
70
|
+
// Supports: false, { enabled: false }, or undefined/null
|
|
71
|
+
const isDisabled = config === false || (typeof config === 'object' && config?.enabled === false);
|
|
72
|
+
if (isDisabled) {
|
|
67
73
|
return {
|
|
68
74
|
exports: [],
|
|
69
75
|
module: BetterAuthModule,
|
|
@@ -1,12 +1,10 @@
|
|
|
1
|
-
import { UseGuards } from '@nestjs/common';
|
|
2
1
|
import { Args, Context, Mutation, Query, Resolver } from '@nestjs/graphql';
|
|
3
2
|
import { Request, Response } from 'express';
|
|
4
3
|
|
|
5
4
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
6
5
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
7
|
-
import { AuthGuardStrategy } from '../../../core/modules/auth/auth-guard-strategy.enum';
|
|
8
|
-
import { AuthGuard } from '../../../core/modules/auth/guards/auth.guard';
|
|
9
6
|
import { BetterAuthAuthModel } from '../../../core/modules/better-auth/better-auth-auth.model';
|
|
7
|
+
import { BetterAuthMigrationStatusModel } from '../../../core/modules/better-auth/better-auth-migration-status.model';
|
|
10
8
|
import {
|
|
11
9
|
BetterAuth2FASetupModel,
|
|
12
10
|
BetterAuthFeaturesModel,
|
|
@@ -61,7 +59,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
61
59
|
nullable: true,
|
|
62
60
|
})
|
|
63
61
|
@Roles(RoleEnum.S_USER)
|
|
64
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
65
62
|
override async betterAuthSession(@Context() ctx: { req: Request }): Promise<BetterAuthSessionModel | null> {
|
|
66
63
|
return super.betterAuthSession(ctx);
|
|
67
64
|
}
|
|
@@ -78,12 +75,28 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
78
75
|
return super.betterAuthFeatures();
|
|
79
76
|
}
|
|
80
77
|
|
|
78
|
+
@Query(() => BetterAuthMigrationStatusModel, {
|
|
79
|
+
description: 'Get migration status from Legacy Auth to Better-Auth (IAM) - Admin only',
|
|
80
|
+
})
|
|
81
|
+
@Roles(RoleEnum.ADMIN)
|
|
82
|
+
override async betterAuthMigrationStatus(): Promise<BetterAuthMigrationStatusModel> {
|
|
83
|
+
return super.betterAuthMigrationStatus();
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
@Query(() => String, {
|
|
87
|
+
description: 'Get fresh JWT token for the current session (requires valid session)',
|
|
88
|
+
nullable: true,
|
|
89
|
+
})
|
|
90
|
+
@Roles(RoleEnum.S_USER)
|
|
91
|
+
override async betterAuthToken(@Context() ctx: { req: Request }): Promise<null | string> {
|
|
92
|
+
return super.betterAuthToken(ctx);
|
|
93
|
+
}
|
|
94
|
+
|
|
81
95
|
@Query(() => [BetterAuthPasskeyModel], {
|
|
82
96
|
description: 'List passkeys for the current user',
|
|
83
97
|
nullable: true,
|
|
84
98
|
})
|
|
85
99
|
@Roles(RoleEnum.S_USER)
|
|
86
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
87
100
|
override async betterAuthListPasskeys(@Context() ctx: { req: Request }): Promise<BetterAuthPasskeyModel[] | null> {
|
|
88
101
|
return super.betterAuthListPasskeys(ctx);
|
|
89
102
|
}
|
|
@@ -118,7 +131,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
118
131
|
|
|
119
132
|
@Mutation(() => Boolean, { description: 'Sign out via Better-Auth' })
|
|
120
133
|
@Roles(RoleEnum.S_USER)
|
|
121
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
122
134
|
override async betterAuthSignOut(@Context() ctx: { req: Request }): Promise<boolean> {
|
|
123
135
|
return super.betterAuthSignOut(ctx);
|
|
124
136
|
}
|
|
@@ -142,7 +154,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
142
154
|
description: 'Enable 2FA for the current user',
|
|
143
155
|
})
|
|
144
156
|
@Roles(RoleEnum.S_USER)
|
|
145
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
146
157
|
override async betterAuthEnable2FA(
|
|
147
158
|
@Args('password') password: string,
|
|
148
159
|
@Context() ctx: { req: Request },
|
|
@@ -154,7 +165,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
154
165
|
description: 'Disable 2FA for the current user',
|
|
155
166
|
})
|
|
156
167
|
@Roles(RoleEnum.S_USER)
|
|
157
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
158
168
|
override async betterAuthDisable2FA(
|
|
159
169
|
@Args('password') password: string,
|
|
160
170
|
@Context() ctx: { req: Request },
|
|
@@ -167,7 +177,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
167
177
|
nullable: true,
|
|
168
178
|
})
|
|
169
179
|
@Roles(RoleEnum.S_USER)
|
|
170
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
171
180
|
override async betterAuthGenerateBackupCodes(@Context() ctx: { req: Request }): Promise<null | string[]> {
|
|
172
181
|
return super.betterAuthGenerateBackupCodes(ctx);
|
|
173
182
|
}
|
|
@@ -180,7 +189,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
180
189
|
description: 'Get passkey registration challenge for WebAuthn',
|
|
181
190
|
})
|
|
182
191
|
@Roles(RoleEnum.S_USER)
|
|
183
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
184
192
|
override async betterAuthGetPasskeyChallenge(
|
|
185
193
|
@Context() ctx: { req: Request },
|
|
186
194
|
): Promise<BetterAuthPasskeyChallengeModel> {
|
|
@@ -191,7 +199,6 @@ export class BetterAuthResolver extends CoreBetterAuthResolver {
|
|
|
191
199
|
description: 'Delete a passkey by ID',
|
|
192
200
|
})
|
|
193
201
|
@Roles(RoleEnum.S_USER)
|
|
194
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
195
202
|
override async betterAuthDeletePasskey(
|
|
196
203
|
@Args('passkeyId') passkeyId: string,
|
|
197
204
|
@Context() ctx: { req: Request },
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Body, Controller, Delete, Get, Param, Patch, Post, Query
|
|
1
|
+
import { Body, Controller, Delete, Get, Param, Patch, Post, Query } from '@nestjs/common';
|
|
2
2
|
import {
|
|
3
3
|
ApiBearerAuth,
|
|
4
4
|
ApiBody,
|
|
@@ -15,8 +15,6 @@ import { CurrentUser } from '../../../core/common/decorators/current-user.decora
|
|
|
15
15
|
import { Roles } from '../../../core/common/decorators/roles.decorator';
|
|
16
16
|
import { RoleEnum } from '../../../core/common/enums/role.enum';
|
|
17
17
|
import { ServiceOptions } from '../../../core/common/interfaces/service-options.interface';
|
|
18
|
-
import { AuthGuardStrategy } from '../../../core/modules/auth/auth-guard-strategy.enum';
|
|
19
|
-
import { AuthGuard } from '../../../core/modules/auth/guards/auth.guard';
|
|
20
18
|
import { UserCreateInput } from './inputs/user-create.input';
|
|
21
19
|
import { UserInput } from './inputs/user.input';
|
|
22
20
|
import { FindAndCountUsersResult } from './outputs/find-and-count-users-result.output';
|
|
@@ -48,7 +46,6 @@ export class UserController {
|
|
|
48
46
|
@ApiOperation({ description: 'Find users (via filter)', summary: 'Get all users' })
|
|
49
47
|
@Get()
|
|
50
48
|
@Roles(RoleEnum.ADMIN)
|
|
51
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
52
49
|
async findUsers(@CurrentUser() currentUser: User): Promise<User[]> {
|
|
53
50
|
const serviceOptions: ServiceOptions = {
|
|
54
51
|
currentUser,
|
|
@@ -67,7 +64,6 @@ export class UserController {
|
|
|
67
64
|
})
|
|
68
65
|
@Get('count')
|
|
69
66
|
@Roles(RoleEnum.ADMIN)
|
|
70
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
71
67
|
async findAndCountUsers(@CurrentUser() currentUser: User): Promise<FindAndCountUsersResult> {
|
|
72
68
|
const serviceOptions: ServiceOptions = {
|
|
73
69
|
currentUser,
|
|
@@ -99,7 +95,6 @@ export class UserController {
|
|
|
99
95
|
@ApiParam({ description: 'User ID', name: 'id', type: String })
|
|
100
96
|
@Get(':id')
|
|
101
97
|
@Roles(RoleEnum.S_USER)
|
|
102
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
103
98
|
async getUser(@CurrentUser() currentUser: User, @Param('id') id: string): Promise<User> {
|
|
104
99
|
const serviceOptions: ServiceOptions = {
|
|
105
100
|
currentUser,
|
|
@@ -121,7 +116,6 @@ export class UserController {
|
|
|
121
116
|
@ApiOperation({ description: 'Create a new user', summary: 'Create user' })
|
|
122
117
|
@Post()
|
|
123
118
|
@Roles(RoleEnum.ADMIN)
|
|
124
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
125
119
|
async createUser(@CurrentUser() currentUser: User, @Body() input: UserCreateInput): Promise<User> {
|
|
126
120
|
const serviceOptions: ServiceOptions = {
|
|
127
121
|
currentUser,
|
|
@@ -208,7 +202,6 @@ export class UserController {
|
|
|
208
202
|
@ApiParam({ description: 'User ID', name: 'id', type: String })
|
|
209
203
|
@Patch(':id')
|
|
210
204
|
@Roles(RoleEnum.S_USER)
|
|
211
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
212
205
|
async updateUser(@CurrentUser() currentUser: User, @Param('id') id: string, @Body() input: UserInput): Promise<User> {
|
|
213
206
|
const serviceOptions: ServiceOptions = {
|
|
214
207
|
currentUser,
|
|
@@ -231,7 +224,6 @@ export class UserController {
|
|
|
231
224
|
@ApiParam({ description: 'User ID', name: 'id', type: String })
|
|
232
225
|
@Delete(':id')
|
|
233
226
|
@Roles(RoleEnum.S_USER)
|
|
234
|
-
@UseGuards(AuthGuard(AuthGuardStrategy.JWT))
|
|
235
227
|
async deleteUser(@CurrentUser() currentUser: User, @Param('id') id: string): Promise<User> {
|
|
236
228
|
const serviceOptions: ServiceOptions = {
|
|
237
229
|
currentUser,
|
|
@@ -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
|
// ===================================================================================================================
|