@nauth-toolkit/core 0.1.35 → 0.1.37

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (58) hide show
  1. package/dist/bootstrap.d.ts.map +1 -1
  2. package/dist/bootstrap.js +1 -1
  3. package/dist/bootstrap.js.map +1 -1
  4. package/dist/dto/auth-response.dto.d.ts +60 -54
  5. package/dist/dto/auth-response.dto.d.ts.map +1 -1
  6. package/dist/dto/auth-response.dto.js +23 -11
  7. package/dist/dto/auth-response.dto.js.map +1 -1
  8. package/dist/dto/login.dto.js +1 -1
  9. package/dist/index.d.ts +5 -0
  10. package/dist/index.d.ts.map +1 -1
  11. package/dist/index.js +7 -1
  12. package/dist/index.js.map +1 -1
  13. package/dist/interfaces/config.interface.d.ts +26 -254
  14. package/dist/interfaces/config.interface.d.ts.map +1 -1
  15. package/dist/interfaces/hooks.interface.d.ts +100 -0
  16. package/dist/interfaces/hooks.interface.d.ts.map +1 -0
  17. package/dist/interfaces/hooks.interface.js +12 -0
  18. package/dist/interfaces/hooks.interface.js.map +1 -0
  19. package/dist/interfaces/index.d.ts +1 -0
  20. package/dist/interfaces/index.d.ts.map +1 -1
  21. package/dist/interfaces/index.js +1 -0
  22. package/dist/interfaces/index.js.map +1 -1
  23. package/dist/internal.d.ts +5 -0
  24. package/dist/internal.d.ts.map +1 -1
  25. package/dist/internal.js +10 -1
  26. package/dist/internal.js.map +1 -1
  27. package/dist/schemas/auth-config.schema.d.ts +32 -158
  28. package/dist/schemas/auth-config.schema.d.ts.map +1 -1
  29. package/dist/schemas/auth-config.schema.js +1 -15
  30. package/dist/schemas/auth-config.schema.js.map +1 -1
  31. package/dist/services/adaptive-mfa-decision.service.d.ts.map +1 -1
  32. package/dist/services/adaptive-mfa-decision.service.js +4 -34
  33. package/dist/services/adaptive-mfa-decision.service.js.map +1 -1
  34. package/dist/services/auth-challenge-helper.service.d.ts.map +1 -1
  35. package/dist/services/auth-challenge-helper.service.js +2 -11
  36. package/dist/services/auth-challenge-helper.service.js.map +1 -1
  37. package/dist/services/auth.service.d.ts +3 -1
  38. package/dist/services/auth.service.d.ts.map +1 -1
  39. package/dist/services/auth.service.js +67 -128
  40. package/dist/services/auth.service.js.map +1 -1
  41. package/dist/services/hook-registry.service.d.ts +74 -0
  42. package/dist/services/hook-registry.service.d.ts.map +1 -0
  43. package/dist/services/hook-registry.service.js +125 -0
  44. package/dist/services/hook-registry.service.js.map +1 -0
  45. package/dist/services/social-auth-base.service.d.ts +3 -1
  46. package/dist/services/social-auth-base.service.d.ts.map +1 -1
  47. package/dist/services/social-auth-base.service.js +14 -40
  48. package/dist/services/social-auth-base.service.js.map +1 -1
  49. package/dist/services/social-redirect.handler.d.ts +1 -1
  50. package/dist/utils/setup/init-services.d.ts +2 -1
  51. package/dist/utils/setup/init-services.d.ts.map +1 -1
  52. package/dist/utils/setup/init-services.js +4 -1
  53. package/dist/utils/setup/init-services.js.map +1 -1
  54. package/dist/utils/setup/init-social.d.ts +1 -1
  55. package/dist/utils/setup/init-social.d.ts.map +1 -1
  56. package/dist/utils/setup/init-social.js +4 -4
  57. package/dist/utils/setup/init-social.js.map +1 -1
  58. package/package.json +1 -1
@@ -48,6 +48,7 @@ const login_dto_1 = require("../dto/login.dto");
48
48
  const change_password_request_dto_1 = require("../dto/change-password-request.dto");
49
49
  const update_user_attributes_request_dto_1 = require("../dto/update-user-attributes-request.dto");
50
50
  const user_response_dto_1 = require("../dto/user-response.dto");
51
+ const auth_response_dto_1 = require("../dto/auth-response.dto");
51
52
  const auth_challenge_dto_1 = require("../dto/auth-challenge.dto");
52
53
  const respond_challenge_dto_1 = require("../dto/respond-challenge.dto");
53
54
  const get_user_by_email_dto_1 = require("../dto/get-user-by-email.dto");
@@ -92,6 +93,7 @@ class AuthService {
92
93
  accountLockoutStorage;
93
94
  config;
94
95
  logger;
96
+ hookRegistry;
95
97
  auditService;
96
98
  phoneVerificationService;
97
99
  mfaService;
@@ -105,7 +107,7 @@ class AuthService {
105
107
  challengeSessionRepository;
106
108
  authAuditRepository;
107
109
  trustedDeviceRepository;
108
- constructor(userRepository, loginAttemptRepository, passwordService, jwtService, sessionService, challengeService, challengeHelper, emailVerificationService, clientInfoService, accountLockoutStorage, config, logger, auditService, // Optional - audit trail service (enabled via config.auditLogs.enabled)
110
+ constructor(userRepository, loginAttemptRepository, passwordService, jwtService, sessionService, challengeService, challengeHelper, emailVerificationService, clientInfoService, accountLockoutStorage, config, logger, hookRegistry, auditService, // Optional - audit trail service (enabled via config.auditLogs.enabled)
109
111
  phoneVerificationService, // Optional - only available when SMS provider is configured
110
112
  mfaService, // Optional - available when MFA modules are imported
111
113
  mfaDeviceRepository, // Optional - available when MFA modules are imported
@@ -130,6 +132,7 @@ class AuthService {
130
132
  this.accountLockoutStorage = accountLockoutStorage;
131
133
  this.config = config;
132
134
  this.logger = logger;
135
+ this.hookRegistry = hookRegistry;
133
136
  this.auditService = auditService;
134
137
  this.phoneVerificationService = phoneVerificationService;
135
138
  this.mfaService = mfaService;
@@ -229,20 +232,7 @@ class AuthService {
229
232
  // ============================================================================
230
233
  // Execute preSignup hook before user creation
231
234
  // Hook can throw NAuthException with PRESIGNUP_FAILED to block signup with custom message
232
- if (this.config.hooks?.preSignup) {
233
- try {
234
- await this.config.hooks.preSignup(dto, 'password', undefined, false);
235
- }
236
- catch (hookError) {
237
- // If hook throws NAuthException with PRESIGNUP_FAILED, re-throw it
238
- if (hookError instanceof nauth_exception_1.NAuthException && hookError.code === error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED) {
239
- throw hookError;
240
- }
241
- // For other errors, wrap in PRESIGNUP_FAILED
242
- const errorMessage = hookError instanceof Error ? hookError.message : 'Pre-signup validation failed';
243
- throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED, errorMessage);
244
- }
245
- }
235
+ await this.hookRegistry.executePreSignup(dto, 'password', undefined, false);
246
236
  // Determine verification requirements based on verification method
247
237
  const verificationMethod = this.config.signup?.verificationMethod;
248
238
  // Validate required fields based on verification method
@@ -333,20 +323,23 @@ class AuthService {
333
323
  // All verification codes are sent when challenges are created (in AuthChallengeHelperService.createChallengeResponse)
334
324
  // This ensures proper sequential flow: email code first, then phone code after email is verified
335
325
  // This prevents user confusion from receiving multiple codes at once
336
- // Execute afterSignup hook if configured
337
- // Called immediately after account creation, before challenges are created
338
- if (this.config.hooks?.afterSignup) {
339
- try {
340
- await this.config.hooks.afterSignup(savedUser, {
341
- requiresVerification: verificationMethod !== 'none',
342
- signupType: 'password',
343
- });
344
- }
345
- catch (hookError) {
346
- // Non-blocking: auth succeeded; hook errors should not break signup
347
- const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
348
- this.logger?.error?.(`afterSignup hook failed (continuing): ${errorMessage}`, { error: hookError });
349
- }
326
+ // ============================================================================
327
+ // Lifecycle Hook: postSignup
328
+ // ============================================================================
329
+ // Execute postSignup hook immediately after account creation (non-blocking)
330
+ await this.hookRegistry.executePostSignup(savedUser, {
331
+ requiresVerification: verificationMethod !== 'none',
332
+ signupType: 'password',
333
+ adminSignup: false,
334
+ });
335
+ // ============================================================================
336
+ // refresh user data in case post signup hook has modified the user
337
+ // ============================================================================
338
+ const refreshedUser = await this.userRepository.findOne({
339
+ where: { id: savedUser.id },
340
+ });
341
+ if (refreshedUser) {
342
+ savedUser = refreshedUser;
350
343
  }
351
344
  // ============================================================================
352
345
  // Challenge System: Determine if user needs to complete challenges
@@ -476,20 +469,7 @@ class AuthService {
476
469
  // ============================================================================
477
470
  // Execute preSignup hook before user creation (admin signup)
478
471
  // Hook can throw NAuthException with PRESIGNUP_FAILED to block signup with custom message
479
- if (this.config.hooks?.preSignup) {
480
- try {
481
- await this.config.hooks.preSignup(dto, 'password', undefined, true);
482
- }
483
- catch (hookError) {
484
- // If hook throws NAuthException with PRESIGNUP_FAILED, re-throw it
485
- if (hookError instanceof nauth_exception_1.NAuthException && hookError.code === error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED) {
486
- throw hookError;
487
- }
488
- // For other errors, wrap in PRESIGNUP_FAILED
489
- const errorMessage = hookError instanceof Error ? hookError.message : 'Pre-signup validation failed';
490
- throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED, errorMessage);
491
- }
492
- }
472
+ await this.hookRegistry.executePreSignup(dto, 'password', undefined, true);
493
473
  // Create user with override flags
494
474
  this.logger?.debug?.(`Creating admin user record for: ${dto.email} || ${dto.username} || ${dto.phone} (isEmailVerified: ${dto.isEmailVerified || false}, isPhoneVerified: ${dto.isPhoneVerified || false})`);
495
475
  const user = this.userRepository.create({
@@ -570,6 +550,14 @@ class AuthService {
570
550
  this.logger?.error?.(`Admin signup failed - database error: ${errorMessage}`);
571
551
  throw error;
572
552
  }
553
+ // ============================================================================
554
+ // Lifecycle Hook: afterSignup
555
+ // ============================================================================
556
+ // Execute afterSignup hook immediately after account creation (non-blocking)
557
+ await this.hookRegistry.executePostSignup(savedUser, {
558
+ signupType: 'password',
559
+ adminSignup: true,
560
+ });
573
561
  // No tokens, no challenge system, no verification emails - pure user creation
574
562
  // Return sanitized user object (excludes passwordHash and other sensitive fields)
575
563
  const userDto = user_response_dto_1.UserResponseDto.fromEntity(savedUser);
@@ -702,29 +690,16 @@ class AuthService {
702
690
  // ============================================================================
703
691
  // Execute preSignup hook before user creation (admin social signup)
704
692
  // Hook can throw NAuthException with PRESIGNUP_FAILED to block signup with custom message
705
- if (this.config.hooks?.preSignup) {
706
- try {
707
- // Convert AdminSignupSocialDTO to OAuthUserProfile-like structure for hook
708
- const profileData = {
709
- email: dto.email,
710
- id: dto.providerId,
711
- firstName: dto.firstName,
712
- lastName: dto.lastName,
713
- verified: true, // Admin signup always has verified email
714
- raw: dto.socialMetadata,
715
- };
716
- await this.config.hooks.preSignup(profileData, 'social', dto.provider, true);
717
- }
718
- catch (hookError) {
719
- // If hook throws NAuthException with PRESIGNUP_FAILED, re-throw it
720
- if (hookError instanceof nauth_exception_1.NAuthException && hookError.code === error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED) {
721
- throw hookError;
722
- }
723
- // For other errors, wrap in PRESIGNUP_FAILED
724
- const errorMessage = hookError instanceof Error ? hookError.message : 'Pre-signup validation failed';
725
- throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.PRESIGNUP_FAILED, errorMessage);
726
- }
727
- }
693
+ // Convert AdminSignupSocialDTO to profile-like structure for hook
694
+ const profileData = {
695
+ email: dto.email,
696
+ id: dto.providerId,
697
+ firstName: dto.firstName,
698
+ lastName: dto.lastName,
699
+ verified: true, // Admin signup always has verified email
700
+ raw: dto.socialMetadata,
701
+ };
702
+ await this.hookRegistry.executePreSignup(profileData, 'social', dto.provider, true);
728
703
  // Create user with override flags
729
704
  // Note: Email is always verified for social imports (like normal social signup)
730
705
  this.logger?.debug?.(`Creating admin social user record for: ${dto.email} || ${dto.username} || ${dto.phone} (isEmailVerified: true [auto-verified for social], isPhoneVerified: ${dto.isPhoneVerified || false})`);
@@ -759,23 +734,12 @@ class AuthService {
759
734
  // ============================================================================
760
735
  // Lifecycle Hook: afterSignup
761
736
  // ============================================================================
762
- // Execute afterSignup hook immediately after account creation, before returning response
763
- // This allows consumer apps to perform actions like welcome emails, external system sync, etc.
764
- // Called for both normal and social signups, with metadata indicating signup type
765
- if (this.config.hooks?.afterSignup) {
766
- try {
767
- await this.config.hooks.afterSignup(savedUser, {
768
- requiresVerification: !savedUser.isEmailVerified || !savedUser.isPhoneVerified,
769
- signupType: 'social',
770
- provider: dto.provider,
771
- });
772
- }
773
- catch (hookError) {
774
- // Non-blocking: auth succeeded; hook errors should not break signup
775
- const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
776
- this.logger?.error?.(`afterSignup hook failed (continuing): ${errorMessage}`, { error: hookError });
777
- }
778
- }
737
+ // Execute afterSignup hook immediately after account creation (non-blocking)
738
+ await this.hookRegistry.executePostSignup(savedUser, {
739
+ signupType: 'social',
740
+ provider: dto.provider,
741
+ adminSignup: true,
742
+ });
779
743
  // ============================================================================
780
744
  // Audit: Record account creation by admin (social import)
781
745
  // ============================================================================
@@ -1965,18 +1929,10 @@ class AuthService {
1965
1929
  }
1966
1930
  }
1967
1931
  // ============================================================================
1968
- // Lifecycle Hook: afterLogin
1932
+ // Lifecycle Hook: afterLogin (TODO: Implement provider-based hook)
1969
1933
  // ============================================================================
1970
- if (this.config.hooks?.afterLogin) {
1971
- try {
1972
- await this.config.hooks.afterLogin(user, session);
1973
- }
1974
- catch (hookError) {
1975
- const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
1976
- // Non-blocking: auth succeeded; hook errors should not break login
1977
- this.logger?.error?.(`afterLogin hook failed (continuing): ${errorMessage}`, { error: hookError });
1978
- }
1979
- }
1934
+ // TODO: Implement provider-based hook for afterLogin
1935
+ // await this.hookRegistry.executeAfterLogin(user, session);
1980
1936
  // ============================================================================
1981
1937
  // Trusted Device Token Management (Remember Device Feature)
1982
1938
  // ============================================================================
@@ -2015,19 +1971,8 @@ class AuthService {
2015
1971
  // Note: deviceToken inclusion in response body is handled by CookieTokenInterceptor
2016
1972
  // which checks route-level @TokenDelivery decorator and global config
2017
1973
  // to decide whether to set as cookie and/or strip from body
2018
- const userDto = user_response_dto_1.UserResponseDto.fromEntity(user);
2019
1974
  const authResponse = {
2020
- user: {
2021
- sub: userDto.sub,
2022
- email: userDto.email,
2023
- firstName: userDto.firstName,
2024
- lastName: userDto.lastName,
2025
- phone: userDto.phone ?? undefined,
2026
- isEmailVerified: userDto.isEmailVerified,
2027
- isPhoneVerified: userDto.isPhoneVerified ?? undefined,
2028
- socialProviders: userDto.socialProviders && userDto.socialProviders.length > 0 ? userDto.socialProviders : undefined,
2029
- hasPasswordHash: userDto.hasPasswordHash,
2030
- },
1975
+ user: (0, auth_response_dto_1.toAuthResponseUser)(user),
2031
1976
  accessToken: tokens.accessToken,
2032
1977
  refreshToken: tokens.refreshToken,
2033
1978
  accessTokenExpiresAt: accessTokenValidation.payload?.exp || 0,
@@ -3618,22 +3563,24 @@ class AuthService {
3618
3563
  if (!user || !user.passwordHash) {
3619
3564
  throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
3620
3565
  }
3621
- // Execute beforePasswordChange hook (use sub for external API)
3622
- if (this.config.hooks?.beforePasswordChange) {
3623
- const result = await this.config.hooks.beforePasswordChange(dto.sub, dto.oldPassword);
3624
- if (result === false) {
3625
- throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.PASSWORD_CHANGE_NOT_ALLOWED, 'Password change not allowed');
3626
- }
3627
- }
3566
+ // ============================================================================
3567
+ // Lifecycle Hook: beforePasswordChange (TODO: Implement provider-based hook)
3568
+ // ============================================================================
3569
+ // TODO: Implement provider-based hook for beforePasswordChange
3570
+ // const allowed = await this.hookRegistry.executeBeforePasswordChange(dto.sub, dto.oldPassword);
3571
+ // if (!allowed) {
3572
+ // throw new NAuthException(AuthErrorCode.PASSWORD_CHANGE_NOT_ALLOWED, 'Password change not allowed');
3573
+ // }
3628
3574
  // Verify old password
3629
3575
  const isValid = await this.passwordService.verifyPassword(dto.oldPassword, user.passwordHash);
3630
3576
  if (!isValid) {
3631
3577
  throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.PASSWORD_INCORRECT, 'Current password is incorrect');
3632
3578
  }
3633
- // Execute afterPasswordChange hook (use sub for external API)
3634
- if (this.config.hooks?.afterPasswordChange) {
3635
- await this.config.hooks.afterPasswordChange(dto.sub);
3636
- }
3579
+ // ============================================================================
3580
+ // Lifecycle Hook: afterPasswordChange (TODO: Implement provider-based hook)
3581
+ // ============================================================================
3582
+ // TODO: Implement provider-based hook for afterPasswordChange
3583
+ // await this.hookRegistry.executeAfterPasswordChange(dto.sub);
3637
3584
  await this.updateUserPassword({
3638
3585
  user,
3639
3586
  newPassword: dto.newPassword,
@@ -4221,18 +4168,10 @@ class AuthService {
4221
4168
  }
4222
4169
  }
4223
4170
  // ============================================================================
4224
- // Lifecycle Hook: afterLoginFailed
4171
+ // Lifecycle Hook: afterLoginFailed (TODO: Implement provider-based hook)
4225
4172
  // ============================================================================
4226
- if (this.config.hooks?.afterLoginFailed) {
4227
- try {
4228
- await this.config.hooks.afterLoginFailed(identifier, reason || 'unknown');
4229
- }
4230
- catch (hookError) {
4231
- const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
4232
- // Non-blocking: login already failed; do not throw
4233
- this.logger?.error?.(`afterLoginFailed hook failed (continuing): ${errorMessage}`, { error: hookError });
4234
- }
4235
- }
4173
+ // TODO: Implement provider-based hook for afterLoginFailed
4174
+ // await this.hookRegistry.executeAfterLoginFailed(identifier, reason || 'unknown');
4236
4175
  }
4237
4176
  /**
4238
4177
  * Records a login attempt with client context.