@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.
- 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 +17 -0
- package/dist/core/modules/auth/core-auth.controller.d.ts +1 -0
- package/dist/core/modules/auth/core-auth.controller.js +28 -2
- 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 +20 -2
- 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-user.mapper.d.ts +33 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.js +443 -0
- package/dist/core/modules/better-auth/better-auth-user.mapper.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 +2 -0
- package/dist/core/modules/better-auth/core-better-auth.resolver.js +14 -0
- 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 +133 -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.resolver.d.ts +2 -0
- package/dist/server/modules/better-auth/better-auth.resolver.js +13 -0
- package/dist/server/modules/better-auth/better-auth.resolver.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 +175 -0
- package/src/core/modules/auth/core-auth.controller.ts +93 -5
- package/src/core/modules/auth/core-auth.module.ts +15 -1
- package/src/core/modules/auth/core-auth.resolver.ts +70 -2
- 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 +254 -0
- package/src/core/modules/better-auth/README.md +487 -169
- package/src/core/modules/better-auth/better-auth-migration-status.model.ts +73 -0
- package/src/core/modules/better-auth/better-auth-user.mapper.ts +805 -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 +25 -0
- 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 +258 -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.resolver.ts +9 -0
- 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,19 @@ 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
|
+
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
|
// ===================================================================================================================
|