1
0
mirror of https://github.com/bitwarden/server.git synced 2025-05-12 07:02:16 -05:00

[PM-21281] Email TOTP sent twice when user only has Email MFA enabled (#5782)

* fix: addressed bug where email token is sent twice,

* test: updating tests to have correct DI and removing test for automatic email of TOTP.
This commit is contained in:
Ike 2025-05-09 12:13:01 -04:00 committed by GitHub
parent 3f95513d11
commit ead5bbdd2a
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 11 additions and 15 deletions

View File

@ -4,9 +4,10 @@ using Bit.Core.Tokens;
namespace Bit.Core.Auth.Models.Business.Tokenables; namespace Bit.Core.Auth.Models.Business.Tokenables;
// This token just provides a verifiable authN mechanism for the API service /// <summary>
// TwoFactorController.cs SendEmailLogin anonymous endpoint so it cannot be /// This token provides a verifiable authN mechanism for the TwoFactorController.SendEmailLoginAsync
// used maliciously. /// anonymous endpoint so it cannot used maliciously.
/// </summary>
public class SsoEmail2faSessionTokenable : ExpiringTokenable public class SsoEmail2faSessionTokenable : ExpiringTokenable
{ {
// Just over 2 min expiration (client expires session after 2 min) // Just over 2 min expiration (client expires session after 2 min)

View File

@ -91,7 +91,10 @@ public class TwoFactorAuthenticationValidator(
{ "TwoFactorProviders2", providers }, { "TwoFactorProviders2", providers },
}; };
// If we have email as a 2FA provider, we might need an SsoEmail2fa Session Token // If we have an Email 2FA provider we need this session token so SSO users
// can re-request an email TOTP. The TwoFactorController.SendEmailLoginAsync
// endpoint requires a way to authenticate the user before sending another email with
// a TOTP, this token acts as the authentication mechanism.
if (enabledProviders.Any(p => p.Key == TwoFactorProviderType.Email)) if (enabledProviders.Any(p => p.Key == TwoFactorProviderType.Email))
{ {
twoFactorResultDict.Add("SsoEmail2faSessionToken", twoFactorResultDict.Add("SsoEmail2faSessionToken",
@ -100,12 +103,6 @@ public class TwoFactorAuthenticationValidator(
twoFactorResultDict.Add("Email", user.Email); twoFactorResultDict.Add("Email", user.Email);
} }
if (enabledProviders.Count == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email)
{
// Send email now if this is their only 2FA method
await _userService.SendTwoFactorEmailAsync(user);
}
return twoFactorResultDict; return twoFactorResultDict;
} }

View File

@ -252,9 +252,9 @@ public class TwoFactorAuthenticationValidatorTests
[Theory] [Theory]
[BitAutoData(TwoFactorProviderType.Email)] [BitAutoData(TwoFactorProviderType.Email)]
public async void BuildTwoFactorResultAsync_IndividualEmailProvider_SendsEmail_SetsSsoToken_ReturnsNotNull( public async void BuildTwoFactorResultAsync_SetsSsoToken_ReturnsNotNull(
TwoFactorProviderType providerType, TwoFactorProviderType providerType,
User user) User user)
{ {
// Arrange // Arrange
var providerTypeInt = (int)providerType; var providerTypeInt = (int)providerType;
@ -276,8 +276,6 @@ public class TwoFactorAuthenticationValidatorTests
Assert.True(providers.ContainsKey(providerTypeInt.ToString())); Assert.True(providers.ContainsKey(providerTypeInt.ToString()));
Assert.True(result.ContainsKey("SsoEmail2faSessionToken")); Assert.True(result.ContainsKey("SsoEmail2faSessionToken"));
Assert.True(result.ContainsKey("Email")); Assert.True(result.ContainsKey("Email"));
await _userService.Received(1).SendTwoFactorEmailAsync(Arg.Any<User>());
} }
[Theory] [Theory]