1
0
mirror of https://github.com/bitwarden/server synced 2025-12-25 20:53:16 +00:00

feat(2fa): [PM-24425] Add email on failed 2FA attempt

* Added email on failed 2FA attempt.

* Added tests.

* Adjusted email verbiage.

* Added feature flag.

* Undid accidental change.

* Undid unintentional change to clean up PR.

* Linting

* Added attempted method to email.

* Changes to email templates.

* Linting.

* Email format changes.

* Email formatting changes.
This commit is contained in:
Todd Martin
2025-08-11 16:39:43 -04:00
committed by GitHub
parent 5b67abba31
commit 3c5de319d1
13 changed files with 212 additions and 19 deletions

View File

@@ -36,6 +36,7 @@ public abstract class BaseRequestValidator<T> where T : class
private readonly GlobalSettings _globalSettings;
private readonly IUserRepository _userRepository;
private readonly IAuthRequestRepository _authRequestRepository;
private readonly IMailService _mailService;
protected ICurrentContext CurrentContext { get; }
protected IPolicyService PolicyService { get; }
@@ -61,7 +62,8 @@ public abstract class BaseRequestValidator<T> where T : class
ISsoConfigRepository ssoConfigRepository,
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
IPolicyRequirementQuery policyRequirementQuery,
IAuthRequestRepository authRequestRepository
IAuthRequestRepository authRequestRepository,
IMailService mailService
)
{
_userManager = userManager;
@@ -80,6 +82,7 @@ public abstract class BaseRequestValidator<T> where T : class
UserDecryptionOptionsBuilder = userDecryptionOptionsBuilder;
PolicyRequirementQuery = policyRequirementQuery;
_authRequestRepository = authRequestRepository;
_mailService = mailService;
}
protected async Task ValidateAsync(T context, ValidatedTokenRequest request,
@@ -160,6 +163,7 @@ public abstract class BaseRequestValidator<T> where T : class
}
else
{
await SendFailedTwoFactorEmail(user, twoFactorProviderType);
await UpdateFailedAuthDetailsAsync(user);
await BuildErrorResultAsync("Two-step token is invalid. Try again.", true, context, user);
}
@@ -373,6 +377,14 @@ public abstract class BaseRequestValidator<T> where T : class
await _userRepository.ReplaceAsync(user);
}
private async Task SendFailedTwoFactorEmail(User user, TwoFactorProviderType failedAttemptType)
{
if (FeatureService.IsEnabled(FeatureFlagKeys.FailedTwoFactorEmail))
{
await _mailService.SendFailedTwoFactorAttemptEmailAsync(user.Email, failedAttemptType, DateTime.UtcNow, CurrentContext.IpAddress);
}
}
private async Task<MasterPasswordPolicyResponseModel> GetMasterPasswordPolicyAsync(User user)
{
// Check current context/cache to see if user is in any organizations, avoids extra DB call if not

View File

@@ -46,7 +46,8 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
IUpdateInstallationCommand updateInstallationCommand,
IPolicyRequirementQuery policyRequirementQuery,
IAuthRequestRepository authRequestRepository)
IAuthRequestRepository authRequestRepository,
IMailService mailService)
: base(
userManager,
userService,
@@ -63,7 +64,8 @@ public class CustomTokenRequestValidator : BaseRequestValidator<CustomTokenReque
ssoConfigRepository,
userDecryptionOptionsBuilder,
policyRequirementQuery,
authRequestRepository)
authRequestRepository,
mailService)
{
_userManager = userManager;
_updateInstallationCommand = updateInstallationCommand;

View File

@@ -40,7 +40,8 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
IFeatureService featureService,
ISsoConfigRepository ssoConfigRepository,
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
IPolicyRequirementQuery policyRequirementQuery)
IPolicyRequirementQuery policyRequirementQuery,
IMailService mailService)
: base(
userManager,
userService,
@@ -57,7 +58,8 @@ public class ResourceOwnerPasswordValidator : BaseRequestValidator<ResourceOwner
ssoConfigRepository,
userDecryptionOptionsBuilder,
policyRequirementQuery,
authRequestRepository)
authRequestRepository,
mailService)
{
_userManager = userManager;
_currentContext = currentContext;

View File

@@ -49,7 +49,8 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
IUserDecryptionOptionsBuilder userDecryptionOptionsBuilder,
IAssertWebAuthnLoginCredentialCommand assertWebAuthnLoginCredentialCommand,
IPolicyRequirementQuery policyRequirementQuery,
IAuthRequestRepository authRequestRepository)
IAuthRequestRepository authRequestRepository,
IMailService mailService)
: base(
userManager,
userService,
@@ -66,7 +67,8 @@ public class WebAuthnGrantValidator : BaseRequestValidator<ExtensionGrantValidat
ssoConfigRepository,
userDecryptionOptionsBuilder,
policyRequirementQuery,
authRequestRepository)
authRequestRepository,
mailService)
{
_assertionOptionsDataProtector = assertionOptionsDataProtector;
_assertWebAuthnLoginCredentialCommand = assertWebAuthnLoginCredentialCommand;