diff --git a/src/Core/Auth/UserFeatures/Registration/Implementations/SendVerificationEmailForRegistrationCommand.cs b/src/Core/Auth/UserFeatures/Registration/Implementations/SendVerificationEmailForRegistrationCommand.cs index 21a421b9d0..3f89e9ad0e 100644 --- a/src/Core/Auth/UserFeatures/Registration/Implementations/SendVerificationEmailForRegistrationCommand.cs +++ b/src/Core/Auth/UserFeatures/Registration/Implementations/SendVerificationEmailForRegistrationCommand.cs @@ -53,23 +53,10 @@ public class SendVerificationEmailForRegistrationCommand : ISendVerificationEmai var user = await _userRepository.GetByEmailAsync(email); var userExists = user != null; - // Delays enabled by default; flag must be enabled to remove the delays. - var delaysEnabled = !_featureService.IsEnabled(FeatureFlagKeys.EmailVerificationDisableTimingDelays); - if (!_globalSettings.EnableEmailVerification) { - if (userExists) { - - if (delaysEnabled) - { - // Add delay to prevent timing attacks - // Note: sub 140 ms feels responsive to users so we are using a random value between 100 - 130 ms - // as it should be long enough to prevent timing attacks but not too long to be noticeable to the user. - await Task.Delay(Random.Shared.Next(100, 130)); - } - throw new BadRequestException($"Email {email} is already taken"); } @@ -87,11 +74,6 @@ public class SendVerificationEmailForRegistrationCommand : ISendVerificationEmai await _mailService.SendRegistrationVerificationEmailAsync(email, token); } - if (delaysEnabled) - { - // Add random delay between 100ms-130ms to prevent timing attacks - await Task.Delay(Random.Shared.Next(100, 130)); - } // User exists but we will return a 200 regardless of whether the email was sent or not; so return null return null; } diff --git a/src/Core/Constants.cs b/src/Core/Constants.cs index 9ba30786d1..969c064c05 100644 --- a/src/Core/Constants.cs +++ b/src/Core/Constants.cs @@ -115,7 +115,6 @@ public static class FeatureFlagKeys public const string TwoFactorExtensionDataPersistence = "pm-9115-two-factor-extension-data-persistence"; public const string DuoRedirect = "duo-redirect"; public const string EmailVerification = "email-verification"; - public const string EmailVerificationDisableTimingDelays = "email-verification-disable-timing-delays"; public const string DeviceTrustLogging = "pm-8285-device-trust-logging"; public const string AuthenticatorTwoFactorToken = "authenticator-2fa-token"; public const string UnauthenticatedExtensionUIRefresh = "unauth-ui-refresh"; diff --git a/src/Identity/Controllers/AccountsController.cs b/src/Identity/Controllers/AccountsController.cs index c840a7ddc5..9360da586c 100644 --- a/src/Identity/Controllers/AccountsController.cs +++ b/src/Identity/Controllers/AccountsController.cs @@ -121,8 +121,7 @@ public class AccountsController : Controller var user = model.ToUser(); var identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash, model.Token, model.OrganizationUserId); - // delaysEnabled false is only for the new registration with email verification process - return await ProcessRegistrationResult(identityResult, user, delaysEnabled: true); + return ProcessRegistrationResult(identityResult, user); } [HttpPost("register/send-verification-email")] @@ -188,7 +187,6 @@ public class AccountsController : Controller // Users will either have an emailed token or an email verification token - not both. IdentityResult identityResult = null; - var delaysEnabled = !_featureService.IsEnabled(FeatureFlagKeys.EmailVerificationDisableTimingDelays); switch (model.GetTokenType()) { @@ -197,32 +195,32 @@ public class AccountsController : Controller await _registerUserCommand.RegisterUserViaEmailVerificationToken(user, model.MasterPasswordHash, model.EmailVerificationToken); - return await ProcessRegistrationResult(identityResult, user, delaysEnabled); + return ProcessRegistrationResult(identityResult, user); break; case RegisterFinishTokenType.OrganizationInvite: identityResult = await _registerUserCommand.RegisterUserViaOrganizationInviteToken(user, model.MasterPasswordHash, model.OrgInviteToken, model.OrganizationUserId); - return await ProcessRegistrationResult(identityResult, user, delaysEnabled); + return ProcessRegistrationResult(identityResult, user); break; case RegisterFinishTokenType.OrgSponsoredFreeFamilyPlan: identityResult = await _registerUserCommand.RegisterUserViaOrganizationSponsoredFreeFamilyPlanInviteToken(user, model.MasterPasswordHash, model.OrgSponsoredFreeFamilyPlanToken); - return await ProcessRegistrationResult(identityResult, user, delaysEnabled); + return ProcessRegistrationResult(identityResult, user); break; case RegisterFinishTokenType.EmergencyAccessInvite: Debug.Assert(model.AcceptEmergencyAccessId.HasValue); identityResult = await _registerUserCommand.RegisterUserViaAcceptEmergencyAccessInviteToken(user, model.MasterPasswordHash, model.AcceptEmergencyAccessInviteToken, model.AcceptEmergencyAccessId.Value); - return await ProcessRegistrationResult(identityResult, user, delaysEnabled); + return ProcessRegistrationResult(identityResult, user); break; case RegisterFinishTokenType.ProviderInvite: Debug.Assert(model.ProviderUserId.HasValue); identityResult = await _registerUserCommand.RegisterUserViaProviderInviteToken(user, model.MasterPasswordHash, model.ProviderInviteToken, model.ProviderUserId.Value); - return await ProcessRegistrationResult(identityResult, user, delaysEnabled); + return ProcessRegistrationResult(identityResult, user); break; default: @@ -230,7 +228,7 @@ public class AccountsController : Controller } } - private async Task ProcessRegistrationResult(IdentityResult result, User user, bool delaysEnabled) + private RegisterResponseModel ProcessRegistrationResult(IdentityResult result, User user) { if (result.Succeeded) { @@ -243,10 +241,6 @@ public class AccountsController : Controller ModelState.AddModelError(string.Empty, error.Description); } - if (delaysEnabled) - { - await Task.Delay(Random.Shared.Next(100, 130)); - } throw new BadRequestException(ModelState); }