@nauth-toolkit/core 0.1.37 → 0.1.39

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 (34) hide show
  1. package/dist/dto/index.d.ts +1 -0
  2. package/dist/dto/index.d.ts.map +1 -1
  3. package/dist/dto/index.js +1 -0
  4. package/dist/dto/index.js.map +1 -1
  5. package/dist/dto/update-verified-status-request.dto.d.ts +70 -0
  6. package/dist/dto/update-verified-status-request.dto.d.ts.map +1 -0
  7. package/dist/dto/update-verified-status-request.dto.js +107 -0
  8. package/dist/dto/update-verified-status-request.dto.js.map +1 -0
  9. package/dist/interfaces/hooks.interface.d.ts +129 -0
  10. package/dist/interfaces/hooks.interface.d.ts.map +1 -1
  11. package/dist/services/auth.service.d.ts +37 -0
  12. package/dist/services/auth.service.d.ts.map +1 -1
  13. package/dist/services/auth.service.js +221 -0
  14. package/dist/services/auth.service.js.map +1 -1
  15. package/dist/services/email-verification.service.d.ts +3 -1
  16. package/dist/services/email-verification.service.d.ts.map +1 -1
  17. package/dist/services/email-verification.service.js +77 -1
  18. package/dist/services/email-verification.service.js.map +1 -1
  19. package/dist/services/hook-registry.service.d.ts +23 -1
  20. package/dist/services/hook-registry.service.d.ts.map +1 -1
  21. package/dist/services/hook-registry.service.js +39 -0
  22. package/dist/services/hook-registry.service.js.map +1 -1
  23. package/dist/services/phone-verification.service.d.ts +3 -1
  24. package/dist/services/phone-verification.service.d.ts.map +1 -1
  25. package/dist/services/phone-verification.service.js +80 -1
  26. package/dist/services/phone-verification.service.js.map +1 -1
  27. package/dist/services/social-auth-base.service.d.ts +2 -1
  28. package/dist/services/social-auth-base.service.d.ts.map +1 -1
  29. package/dist/services/social-auth-base.service.js +5 -2
  30. package/dist/services/social-auth-base.service.js.map +1 -1
  31. package/dist/utils/setup/init-services.d.ts.map +1 -1
  32. package/dist/utils/setup/init-services.js +2 -2
  33. package/dist/utils/setup/init-services.js.map +1 -1
  34. package/package.json +1 -1
@@ -47,6 +47,7 @@ const enable_user_dto_1 = require("../dto/enable-user.dto");
47
47
  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
+ const update_verified_status_request_dto_1 = require("../dto/update-verified-status-request.dto");
50
51
  const user_response_dto_1 = require("../dto/user-response.dto");
51
52
  const auth_response_dto_1 = require("../dto/auth-response.dto");
52
53
  const auth_challenge_dto_1 = require("../dto/auth-challenge.dto");
@@ -735,10 +736,16 @@ class AuthService {
735
736
  // Lifecycle Hook: afterSignup
736
737
  // ============================================================================
737
738
  // Execute afterSignup hook immediately after account creation (non-blocking)
739
+ // Extract profile picture from social metadata if available
740
+ const profilePicture = dto.socialMetadata && typeof dto.socialMetadata === 'object' && 'picture' in dto.socialMetadata
741
+ ? dto.socialMetadata.picture
742
+ : null;
738
743
  await this.hookRegistry.executePostSignup(savedUser, {
739
744
  signupType: 'social',
740
745
  provider: dto.provider,
741
746
  adminSignup: true,
747
+ socialMetadata: dto.socialMetadata || null,
748
+ profilePicture,
742
749
  });
743
750
  // ============================================================================
744
751
  // Audit: Record account creation by admin (social import)
@@ -3967,6 +3974,220 @@ class AuthService {
3967
3974
  userId: user.id,
3968
3975
  });
3969
3976
  }
3977
+ // ============================================================================
3978
+ // Hook: Execute user profile updated hooks
3979
+ // ============================================================================
3980
+ try {
3981
+ // Build changed fields array with old/new values
3982
+ const changedFields = [];
3983
+ // Track all fields that were in updateFields
3984
+ for (const fieldName of Object.keys(updateFields)) {
3985
+ changedFields.push({
3986
+ fieldName,
3987
+ oldValue: user[fieldName],
3988
+ newValue: updateFields[fieldName],
3989
+ });
3990
+ }
3991
+ // Get client info from ClientInfoService
3992
+ const clientInfo = this.clientInfoService.get();
3993
+ // Execute hooks (non-blocking)
3994
+ await this.hookRegistry.executeUserProfileUpdated({
3995
+ user: updatedUser,
3996
+ changedFields,
3997
+ updateSource: 'user_request',
3998
+ clientInfo: {
3999
+ ipAddress: clientInfo.ipAddress,
4000
+ userAgent: clientInfo.userAgent,
4001
+ ipCountry: clientInfo.ipCountry,
4002
+ ipCity: clientInfo.ipCity,
4003
+ },
4004
+ });
4005
+ }
4006
+ catch (hookError) {
4007
+ // Non-blocking: Log but continue
4008
+ const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
4009
+ this.logger?.error?.(`Failed to execute userProfileUpdated hooks: ${errorMessage}`, {
4010
+ error: hookError,
4011
+ userId: user.id,
4012
+ });
4013
+ }
4014
+ // Return user response DTO
4015
+ return user_response_dto_1.UserResponseDto.fromEntity(updatedUser);
4016
+ }
4017
+ /**
4018
+ * Update email and/or phone verification status.
4019
+ *
4020
+ * Intended for admin use cases such as migration or offline validation.
4021
+ * Updates verification status without requiring actual verification codes.
4022
+ *
4023
+ * Validation:
4024
+ * - Cannot set verified=true if email/phone doesn't exist
4025
+ * - Can set verified=false even if email/phone doesn't exist (default state)
4026
+ * - Only updates provided fields (partial update)
4027
+ *
4028
+ * Audit:
4029
+ * - Records EMAIL_VERIFIED or PHONE_VERIFIED audit events
4030
+ * - Includes performedBy from authenticated admin context
4031
+ *
4032
+ * @param dto - Request DTO containing sub and verification status flags
4033
+ * @returns Updated user object
4034
+ * @throws {NAuthException} If user not found or trying to verify non-existent email/phone
4035
+ *
4036
+ * @example
4037
+ * ```typescript
4038
+ * // Update email verification only
4039
+ * await authService.updateVerifiedStatus({
4040
+ * sub: 'user-uuid',
4041
+ * isEmailVerified: true
4042
+ * });
4043
+ *
4044
+ * // Update both email and phone verification
4045
+ * await authService.updateVerifiedStatus({
4046
+ * sub: 'user-uuid',
4047
+ * isEmailVerified: true,
4048
+ * isPhoneVerified: false
4049
+ * });
4050
+ * ```
4051
+ */
4052
+ async updateVerifiedStatus(dto) {
4053
+ // Ensure DTO is validated (supports direct usage without framework validation)
4054
+ dto = await (0, dto_validator_1.ensureValidatedDto)(update_verified_status_request_dto_1.UpdateVerifiedStatusRequestDTO, dto);
4055
+ // Find user by sub (external identifier)
4056
+ const user = (await this.userRepository.findOne({ where: { sub: dto.sub } }));
4057
+ if (!user) {
4058
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.NOT_FOUND, 'User not found');
4059
+ }
4060
+ // Validate that email exists if trying to set isEmailVerified to true
4061
+ if (dto.isEmailVerified === true && !user.email) {
4062
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'Cannot set email verification to true: user does not have an email address');
4063
+ }
4064
+ // Validate that phone exists if trying to set isPhoneVerified to true
4065
+ if (dto.isPhoneVerified === true && !user.phone) {
4066
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.VALIDATION_FAILED, 'Cannot set phone verification to true: user does not have a phone number');
4067
+ }
4068
+ // Prepare update object - only include fields that were provided
4069
+ const updateFields = {};
4070
+ if (dto.isEmailVerified !== undefined) {
4071
+ updateFields.isEmailVerified = dto.isEmailVerified;
4072
+ }
4073
+ if (dto.isPhoneVerified !== undefined) {
4074
+ updateFields.isPhoneVerified = dto.isPhoneVerified;
4075
+ }
4076
+ // If no fields to update, return current user
4077
+ if (Object.keys(updateFields).length === 0) {
4078
+ return user_response_dto_1.UserResponseDto.fromEntity(user);
4079
+ }
4080
+ // Update user - use internal id for database update
4081
+ await this.userRepository.update(user.id, updateFields);
4082
+ // Reload user to get updated values
4083
+ const updatedUser = (await this.userRepository.findOne({ where: { id: user.id } }));
4084
+ if (!updatedUser) {
4085
+ throw new nauth_exception_1.NAuthException(error_codes_enum_1.AuthErrorCode.INTERNAL_ERROR, 'Failed to reload user after update');
4086
+ }
4087
+ // ============================================================================
4088
+ // Audit: Record verification status changes
4089
+ // ============================================================================
4090
+ if (this.auditService) {
4091
+ // Record email verification change if provided
4092
+ if (dto.isEmailVerified !== undefined) {
4093
+ try {
4094
+ await this.auditService.recordEvent({
4095
+ userId: user.id,
4096
+ eventType: auth_audit_event_type_enum_1.AuthAuditEventType.EMAIL_VERIFIED,
4097
+ eventStatus: dto.isEmailVerified ? 'SUCCESS' : 'INFO',
4098
+ description: dto.isEmailVerified
4099
+ ? 'Email verification status set to verified (admin action)'
4100
+ : 'Email verification status set to unverified (admin action)',
4101
+ reason: 'admin_verification_update',
4102
+ metadata: {
4103
+ previousStatus: user.isEmailVerified,
4104
+ newStatus: dto.isEmailVerified,
4105
+ updateMethod: 'admin_direct',
4106
+ // Client info automatically included from context (performedBy auto-populated)
4107
+ },
4108
+ });
4109
+ }
4110
+ catch (auditError) {
4111
+ // Non-blocking: Log but continue
4112
+ const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
4113
+ this.logger?.error?.(`Failed to record EMAIL_VERIFIED audit event: ${errorMessage}`, {
4114
+ error: auditError,
4115
+ userId: user.id,
4116
+ });
4117
+ }
4118
+ }
4119
+ // Record phone verification change if provided
4120
+ if (dto.isPhoneVerified !== undefined) {
4121
+ try {
4122
+ await this.auditService.recordEvent({
4123
+ userId: user.id,
4124
+ eventType: auth_audit_event_type_enum_1.AuthAuditEventType.PHONE_VERIFIED,
4125
+ eventStatus: dto.isPhoneVerified ? 'SUCCESS' : 'INFO',
4126
+ description: dto.isPhoneVerified
4127
+ ? 'Phone verification status set to verified (admin action)'
4128
+ : 'Phone verification status set to unverified (admin action)',
4129
+ reason: 'admin_verification_update',
4130
+ metadata: {
4131
+ previousStatus: user.isPhoneVerified,
4132
+ newStatus: dto.isPhoneVerified,
4133
+ updateMethod: 'admin_direct',
4134
+ // Client info automatically included from context (performedBy auto-populated)
4135
+ },
4136
+ });
4137
+ }
4138
+ catch (auditError) {
4139
+ // Non-blocking: Log but continue
4140
+ const errorMessage = auditError instanceof Error ? auditError.message : 'Unknown error';
4141
+ this.logger?.error?.(`Failed to record PHONE_VERIFIED audit event: ${errorMessage}`, {
4142
+ error: auditError,
4143
+ userId: user.id,
4144
+ });
4145
+ }
4146
+ }
4147
+ }
4148
+ // ============================================================================
4149
+ // Hook: Execute user profile updated hooks
4150
+ // ============================================================================
4151
+ try {
4152
+ // Build changed fields array with old/new values
4153
+ const changedFields = [];
4154
+ if (dto.isEmailVerified !== undefined) {
4155
+ changedFields.push({
4156
+ fieldName: 'isEmailVerified',
4157
+ oldValue: user.isEmailVerified,
4158
+ newValue: dto.isEmailVerified,
4159
+ });
4160
+ }
4161
+ if (dto.isPhoneVerified !== undefined) {
4162
+ changedFields.push({
4163
+ fieldName: 'isPhoneVerified',
4164
+ oldValue: user.isPhoneVerified,
4165
+ newValue: dto.isPhoneVerified,
4166
+ });
4167
+ }
4168
+ // Get client info from ClientInfoService
4169
+ const clientInfo = this.clientInfoService.get();
4170
+ // Execute hooks (non-blocking)
4171
+ await this.hookRegistry.executeUserProfileUpdated({
4172
+ user: updatedUser,
4173
+ changedFields,
4174
+ updateSource: 'admin_action',
4175
+ clientInfo: {
4176
+ ipAddress: clientInfo.ipAddress,
4177
+ userAgent: clientInfo.userAgent,
4178
+ ipCountry: clientInfo.ipCountry,
4179
+ ipCity: clientInfo.ipCity,
4180
+ },
4181
+ });
4182
+ }
4183
+ catch (hookError) {
4184
+ // Non-blocking: Log but continue
4185
+ const errorMessage = hookError instanceof Error ? hookError.message : 'Unknown error';
4186
+ this.logger?.error?.(`Failed to execute userProfileUpdated hooks: ${errorMessage}`, {
4187
+ error: hookError,
4188
+ userId: user.id,
4189
+ });
4190
+ }
3970
4191
  // Return user response DTO
3971
4192
  return user_response_dto_1.UserResponseDto.fromEntity(updatedUser);
3972
4193
  }