@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
|
@@ -211,9 +211,30 @@ export class CoreBetterAuthController {
|
|
|
211
211
|
throw new BadRequestException('Better-Auth API not available');
|
|
212
212
|
}
|
|
213
213
|
|
|
214
|
+
// Try to sign in, with automatic legacy user migration
|
|
215
|
+
return this.attemptSignIn(res, input, api, true);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Attempt sign-in with optional legacy user migration
|
|
220
|
+
* @param res - Response object
|
|
221
|
+
* @param input - Sign-in credentials
|
|
222
|
+
* @param api - Better-Auth API instance
|
|
223
|
+
* @param allowMigration - Whether to attempt legacy migration on failure
|
|
224
|
+
*/
|
|
225
|
+
private async attemptSignIn(
|
|
226
|
+
res: Response,
|
|
227
|
+
input: BetterAuthSignInInput,
|
|
228
|
+
api: ReturnType<BetterAuthService['getApi']>,
|
|
229
|
+
allowMigration: boolean,
|
|
230
|
+
): Promise<BetterAuthResponse> {
|
|
231
|
+
// Normalize password to SHA256 format for consistency with Legacy Auth
|
|
232
|
+
// This ensures users can sign in with either plain password or SHA256 hash
|
|
233
|
+
const normalizedPassword = this.userMapper.normalizePasswordForIam(input.password);
|
|
234
|
+
|
|
214
235
|
try {
|
|
215
|
-
const response = await api
|
|
216
|
-
body: { email: input.email, password:
|
|
236
|
+
const response = await api!.signInEmail({
|
|
237
|
+
body: { email: input.email, password: normalizedPassword },
|
|
217
238
|
});
|
|
218
239
|
|
|
219
240
|
if (!response) {
|
|
@@ -244,6 +265,18 @@ export class CoreBetterAuthController {
|
|
|
244
265
|
throw new UnauthorizedException('Invalid credentials');
|
|
245
266
|
} catch (error) {
|
|
246
267
|
this.logger.debug(`Sign-in error: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
268
|
+
|
|
269
|
+
// If migration is allowed, try to migrate legacy user and retry
|
|
270
|
+
if (allowMigration) {
|
|
271
|
+
// Pass the original password for legacy verification, but migration uses normalized password
|
|
272
|
+
const migrated = await this.userMapper.migrateAccountToIam(input.email, input.password);
|
|
273
|
+
if (migrated) {
|
|
274
|
+
this.logger.debug(`Migrated legacy user ${input.email} to IAM, retrying sign-in`);
|
|
275
|
+
// Retry sign-in after migration (without allowing another migration to prevent loops)
|
|
276
|
+
return this.attemptSignIn(res, input, api, false);
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
|
|
247
280
|
throw new UnauthorizedException('Invalid credentials');
|
|
248
281
|
}
|
|
249
282
|
}
|
|
@@ -267,12 +300,15 @@ export class CoreBetterAuthController {
|
|
|
267
300
|
throw new BadRequestException('Better-Auth API not available');
|
|
268
301
|
}
|
|
269
302
|
|
|
303
|
+
// Normalize password to SHA256 format for consistency with Legacy Auth
|
|
304
|
+
const normalizedPassword = this.userMapper.normalizePasswordForIam(input.password);
|
|
305
|
+
|
|
270
306
|
try {
|
|
271
307
|
const response = await api.signUpEmail({
|
|
272
308
|
body: {
|
|
273
309
|
email: input.email,
|
|
274
310
|
name: input.name || input.email.split('@')[0],
|
|
275
|
-
password:
|
|
311
|
+
password: normalizedPassword,
|
|
276
312
|
},
|
|
277
313
|
});
|
|
278
314
|
|
|
@@ -283,6 +319,11 @@ export class CoreBetterAuthController {
|
|
|
283
319
|
if (hasUser(response)) {
|
|
284
320
|
// Link or create user in our database
|
|
285
321
|
await this.userMapper.linkOrCreateUser(response.user);
|
|
322
|
+
|
|
323
|
+
// Sync password to legacy (enables IAM Sign-Up → Legacy Sign-In)
|
|
324
|
+
// Pass the plain password so it can be hashed with bcrypt for Legacy Auth
|
|
325
|
+
await this.userMapper.syncPasswordToLegacy(response.user.id, response.user.email, input.password);
|
|
326
|
+
|
|
286
327
|
const mappedUser = await this.userMapper.mapSessionUser(response.user);
|
|
287
328
|
|
|
288
329
|
const result: BetterAuthResponse = {
|
|
@@ -7,6 +7,7 @@ import { RoleEnum } from '../../common/enums/role.enum';
|
|
|
7
7
|
import { AuthGuardStrategy } from '../auth/auth-guard-strategy.enum';
|
|
8
8
|
import { AuthGuard } from '../auth/guards/auth.guard';
|
|
9
9
|
import { BetterAuthAuthModel } from './better-auth-auth.model';
|
|
10
|
+
import { BetterAuthMigrationStatusModel } from './better-auth-migration-status.model';
|
|
10
11
|
import {
|
|
11
12
|
BetterAuth2FASetupModel,
|
|
12
13
|
BetterAuthFeaturesModel,
|
|
@@ -130,6 +131,30 @@ export class CoreBetterAuthResolver {
|
|
|
130
131
|
};
|
|
131
132
|
}
|
|
132
133
|
|
|
134
|
+
/**
|
|
135
|
+
* Get migration status from Legacy Auth to Better-Auth (IAM)
|
|
136
|
+
*
|
|
137
|
+
* This query provides administrators with information about how many users
|
|
138
|
+
* have been migrated to the IAM system. This helps determine when it might
|
|
139
|
+
* be safe to consider disabling Legacy Auth endpoints.
|
|
140
|
+
*
|
|
141
|
+
* A user is considered fully migrated when:
|
|
142
|
+
* 1. They have an `iamId` set (linked to Better-Auth)
|
|
143
|
+
* 2. They have a credential account in Better-Auth
|
|
144
|
+
*
|
|
145
|
+
* Note: Even when canDisableLegacyAuth returns true, Legacy Auth cannot
|
|
146
|
+
* currently be removed because CoreModule.forRoot requires AuthService
|
|
147
|
+
* for GraphQL Subscriptions authentication.
|
|
148
|
+
*/
|
|
149
|
+
@Query(() => BetterAuthMigrationStatusModel, {
|
|
150
|
+
description: 'Get migration status from Legacy Auth to Better-Auth (IAM) - Admin only',
|
|
151
|
+
})
|
|
152
|
+
@Roles(RoleEnum.ADMIN)
|
|
153
|
+
async betterAuthMigrationStatus(): Promise<BetterAuthMigrationStatusModel> {
|
|
154
|
+
const status = await this.userMapper.getMigrationStatus();
|
|
155
|
+
return status;
|
|
156
|
+
}
|
|
157
|
+
|
|
133
158
|
// ===========================================================================
|
|
134
159
|
// Mutations
|
|
135
160
|
// ===========================================================================
|
|
@@ -18,6 +18,7 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
export * from './better-auth-auth.model';
|
|
21
|
+
export * from './better-auth-migration-status.model';
|
|
21
22
|
export * from './better-auth-models';
|
|
22
23
|
export * from './better-auth-rate-limit.middleware';
|
|
23
24
|
export * from './better-auth-rate-limiter.service';
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { BadRequestException, NotFoundException, UnprocessableEntityException } from '@nestjs/common';
|
|
1
|
+
import { BadRequestException, Logger, NotFoundException, UnprocessableEntityException } from '@nestjs/common';
|
|
2
2
|
import bcrypt = require('bcrypt');
|
|
3
3
|
import crypto = require('crypto');
|
|
4
4
|
import { sha256 } from 'js-sha256';
|
|
@@ -13,20 +13,31 @@ import { CoreModelConstructor } from '../../common/types/core-model-constructor.
|
|
|
13
13
|
import { CoreUserModel } from './core-user.model';
|
|
14
14
|
import { CoreUserCreateInput } from './inputs/core-user-create.input';
|
|
15
15
|
import { CoreUserInput } from './inputs/core-user.input';
|
|
16
|
+
import { CoreUserServiceOptions } from './interfaces/core-user-service-options.interface';
|
|
16
17
|
|
|
17
18
|
/**
|
|
18
19
|
* User service
|
|
20
|
+
*
|
|
21
|
+
* Provides user management with automatic synchronization between
|
|
22
|
+
* Legacy Auth and Better-Auth (IAM) systems when both are enabled.
|
|
19
23
|
*/
|
|
20
24
|
export abstract class CoreUserService<
|
|
21
25
|
TUser extends CoreUserModel,
|
|
22
26
|
TUserInput extends CoreUserInput,
|
|
23
27
|
TUserCreateInput extends CoreUserCreateInput,
|
|
24
28
|
> extends CrudService<TUser, TUserCreateInput, TUserInput> {
|
|
29
|
+
protected readonly userServiceLogger = new Logger(CoreUserService.name);
|
|
30
|
+
|
|
25
31
|
protected constructor(
|
|
26
32
|
protected override readonly configService: ConfigService,
|
|
27
33
|
protected readonly emailService: EmailService,
|
|
28
34
|
protected override readonly mainDbModel: Model<Document & TUser>,
|
|
29
35
|
protected override readonly mainModelConstructor: CoreModelConstructor<TUser>,
|
|
36
|
+
/**
|
|
37
|
+
* Optional configuration for additional features like IAM sync.
|
|
38
|
+
* Using options object pattern for extensibility without breaking changes.
|
|
39
|
+
*/
|
|
40
|
+
protected readonly options?: CoreUserServiceOptions,
|
|
30
41
|
) {
|
|
31
42
|
super();
|
|
32
43
|
}
|
|
@@ -125,6 +136,10 @@ export abstract class CoreUserService<
|
|
|
125
136
|
|
|
126
137
|
/**
|
|
127
138
|
* Set new password for user with token
|
|
139
|
+
*
|
|
140
|
+
* This method also syncs the password change to Better-Auth (IAM) if:
|
|
141
|
+
* - BetterAuthUserMapper is configured via options
|
|
142
|
+
* - User has an existing IAM credential account
|
|
128
143
|
*/
|
|
129
144
|
async resetPassword(token: string, newPassword: string, serviceOptions?: ServiceOptions): Promise<TUser> {
|
|
130
145
|
// Get user
|
|
@@ -133,6 +148,10 @@ export abstract class CoreUserService<
|
|
|
133
148
|
throw new NotFoundException(`No user found with password reset token: ${token}`);
|
|
134
149
|
}
|
|
135
150
|
|
|
151
|
+
// Store the original plain password for IAM sync before any hashing
|
|
152
|
+
// We need the plain password because IAM uses scrypt, not bcrypt+sha256
|
|
153
|
+
const plainPasswordForIamSync = /^[a-f0-9]{64}$/i.test(newPassword) ? undefined : newPassword;
|
|
154
|
+
|
|
136
155
|
return this.process(
|
|
137
156
|
async () => {
|
|
138
157
|
// Check if the password was transmitted encrypted
|
|
@@ -141,11 +160,26 @@ export abstract class CoreUserService<
|
|
|
141
160
|
newPassword = sha256(newPassword);
|
|
142
161
|
}
|
|
143
162
|
|
|
144
|
-
// Update
|
|
145
|
-
|
|
163
|
+
// Update Legacy Auth password
|
|
164
|
+
const updatedUser = await assignPlain(dbObject, {
|
|
146
165
|
password: await bcrypt.hash(newPassword, 10),
|
|
147
166
|
passwordResetToken: null,
|
|
148
167
|
}).save();
|
|
168
|
+
|
|
169
|
+
// Sync password to Better-Auth (IAM) if mapper is available
|
|
170
|
+
// This ensures users can sign in via IAM after password reset
|
|
171
|
+
if (this.options?.betterAuthUserMapper && plainPasswordForIamSync && dbObject.email) {
|
|
172
|
+
try {
|
|
173
|
+
await this.options.betterAuthUserMapper.syncPasswordChangeToIam(dbObject.email, plainPasswordForIamSync);
|
|
174
|
+
} catch (error) {
|
|
175
|
+
// Log but don't fail - Legacy Auth password was updated successfully
|
|
176
|
+
this.userServiceLogger.warn(
|
|
177
|
+
`Failed to sync password reset to IAM for ${dbObject.email}: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
178
|
+
);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
return updatedUser;
|
|
149
183
|
},
|
|
150
184
|
{ dbObject, serviceOptions },
|
|
151
185
|
);
|
|
@@ -186,7 +220,7 @@ export abstract class CoreUserService<
|
|
|
186
220
|
}
|
|
187
221
|
|
|
188
222
|
// Check roles values
|
|
189
|
-
if (roles.some(role => typeof role !== 'string')) {
|
|
223
|
+
if (roles.some((role) => typeof role !== 'string')) {
|
|
190
224
|
throw new BadRequestException('Roles contains invalid values');
|
|
191
225
|
}
|
|
192
226
|
|
|
@@ -198,4 +232,97 @@ export abstract class CoreUserService<
|
|
|
198
232
|
{ serviceOptions },
|
|
199
233
|
);
|
|
200
234
|
}
|
|
235
|
+
|
|
236
|
+
// ===================================================================================================================
|
|
237
|
+
// Auth System Sync Methods
|
|
238
|
+
// ===================================================================================================================
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Update user with automatic email and password sync between Legacy and IAM auth systems
|
|
242
|
+
*
|
|
243
|
+
* When the email changes and BetterAuthUserMapper is available, this method:
|
|
244
|
+
* - Invalidates all Better-Auth sessions (forces re-authentication)
|
|
245
|
+
* - The shared users collection is automatically updated
|
|
246
|
+
*
|
|
247
|
+
* When the password changes:
|
|
248
|
+
* - Updates the Legacy Auth password (bcrypt hash)
|
|
249
|
+
* - Syncs to Better-Auth (IAM) if the user has a credential account
|
|
250
|
+
*/
|
|
251
|
+
override async update(id: string, input: TUserInput, serviceOptions?: ServiceOptions): Promise<TUser> {
|
|
252
|
+
// Get the current user before update to detect email changes
|
|
253
|
+
const oldUser = (await this.mainDbModel.findById(id).lean().exec()) as null | TUser;
|
|
254
|
+
const oldEmail = oldUser?.email;
|
|
255
|
+
|
|
256
|
+
// Store plain password for IAM sync before any hashing occurs
|
|
257
|
+
// We need to capture this before super.update() which may hash it
|
|
258
|
+
const inputPassword = (input as any).password;
|
|
259
|
+
const plainPasswordForIamSync = inputPassword && !/^[a-f0-9]{64}$/i.test(inputPassword) ? inputPassword : undefined;
|
|
260
|
+
|
|
261
|
+
// Perform the update
|
|
262
|
+
const updatedUser = await super.update(id, input, serviceOptions);
|
|
263
|
+
|
|
264
|
+
// Sync email change to IAM if email was changed and mapper is available
|
|
265
|
+
if (this.options?.betterAuthUserMapper && oldEmail && input.email && oldEmail !== input.email) {
|
|
266
|
+
try {
|
|
267
|
+
await this.options.betterAuthUserMapper.syncEmailChangeFromLegacy(oldEmail, input.email);
|
|
268
|
+
this.userServiceLogger.debug(`Synced email change from Legacy to IAM: ${oldEmail} → ${input.email}`);
|
|
269
|
+
} catch (error) {
|
|
270
|
+
this.userServiceLogger.error(
|
|
271
|
+
`Failed to sync email change to IAM: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
272
|
+
);
|
|
273
|
+
// Don't throw - email sync failure shouldn't block the update
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// Sync password change to IAM if password was changed and mapper is available
|
|
278
|
+
if (this.options?.betterAuthUserMapper && plainPasswordForIamSync && oldUser?.email) {
|
|
279
|
+
try {
|
|
280
|
+
await this.options.betterAuthUserMapper.syncPasswordChangeToIam(oldUser.email, plainPasswordForIamSync);
|
|
281
|
+
this.userServiceLogger.debug(`Synced password change to IAM for user ${oldUser.email}`);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
this.userServiceLogger.warn(
|
|
284
|
+
`Failed to sync password change to IAM for ${oldUser.email}: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
285
|
+
);
|
|
286
|
+
// Don't throw - password sync failure shouldn't block the update
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
return updatedUser;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Delete user with automatic cleanup of IAM auth data
|
|
295
|
+
*
|
|
296
|
+
* When BetterAuthUserMapper is available, this method also:
|
|
297
|
+
* - Deletes all Better-Auth accounts for this user
|
|
298
|
+
* - Deletes all Better-Auth sessions for this user
|
|
299
|
+
*
|
|
300
|
+
* This ensures no orphaned auth data remains after user deletion.
|
|
301
|
+
*/
|
|
302
|
+
override async delete(id: string, serviceOptions?: ServiceOptions): Promise<TUser> {
|
|
303
|
+
// Get the user before deletion to cleanup IAM data
|
|
304
|
+
const user = (await this.mainDbModel.findById(id).lean().exec()) as null | (TUser & { _id: any });
|
|
305
|
+
|
|
306
|
+
// Perform the deletion
|
|
307
|
+
const deletedUser = await super.delete(id, serviceOptions);
|
|
308
|
+
|
|
309
|
+
// Cleanup IAM data if mapper is available
|
|
310
|
+
if (this.options?.betterAuthUserMapper && user?._id) {
|
|
311
|
+
try {
|
|
312
|
+
const result = await this.options.betterAuthUserMapper.cleanupIamDataForDeletedUser(user._id);
|
|
313
|
+
if (result.accountsDeleted > 0 || result.sessionsDeleted > 0) {
|
|
314
|
+
this.userServiceLogger.debug(
|
|
315
|
+
`Cleaned up IAM data for deleted user ${id}: accounts=${result.accountsDeleted}, sessions=${result.sessionsDeleted}`,
|
|
316
|
+
);
|
|
317
|
+
}
|
|
318
|
+
} catch (error) {
|
|
319
|
+
this.userServiceLogger.error(
|
|
320
|
+
`Failed to cleanup IAM data for deleted user: ${error instanceof Error ? error.message : 'Unknown error'}`,
|
|
321
|
+
);
|
|
322
|
+
// Don't throw - cleanup failure shouldn't block the delete response
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return deletedUser;
|
|
327
|
+
}
|
|
201
328
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { BetterAuthUserMapper } from '../../better-auth/better-auth-user.mapper';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Optional configuration for CoreUserService
|
|
5
|
+
*
|
|
6
|
+
* Use this interface for optional dependencies that may not be available in all projects.
|
|
7
|
+
* This pattern allows adding new optional parameters without breaking existing implementations.
|
|
8
|
+
*/
|
|
9
|
+
export interface CoreUserServiceOptions {
|
|
10
|
+
/**
|
|
11
|
+
* Optional BetterAuthUserMapper for syncing between Legacy and IAM auth systems.
|
|
12
|
+
* When provided, email changes and user deletions are automatically synced.
|
|
13
|
+
*/
|
|
14
|
+
betterAuthUserMapper?: BetterAuthUserMapper;
|
|
15
|
+
}
|