1
0
mirror of https://github.com/bitwarden/server synced 2025-12-23 03:33:35 +00:00

fix(2fa): [PM-22323] Do not show 2FA warning for 2FA setup and login emails

* Added configuration to not display 2FA setup instruction

* Refactored to new service.

* Linting.

* Dependency injection

* Changed to scoped to have access to ICurrentContext.

* Inverted logic for EmailTotpAction

* Fixed tests.

* Fixed tests.

* More tests.

* Fixed tests.

* Linting.

* Added tests at controller level.

* Linting

* Fixed error in test.

* Review updates.

* Accidentally deleted imports.
This commit is contained in:
Todd Martin
2025-07-07 10:56:59 -04:00
committed by GitHub
parent 240968ef4c
commit 79ad1dbda0
18 changed files with 491 additions and 288 deletions

View File

@@ -7,6 +7,7 @@ using Bit.Core.AdminConsole.Enums.Provider;
using Bit.Core.AdminConsole.Repositories;
using Bit.Core.AdminConsole.Services;
using Bit.Core.Auth.Models.Api.Request.Accounts;
using Bit.Core.Auth.Services;
using Bit.Core.Auth.UserFeatures.TdeOffboardingPassword.Interfaces;
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
using Bit.Core.Auth.UserFeatures.UserMasterPassword.Interfaces;
@@ -34,6 +35,8 @@ public class AccountsController : Controller
private readonly ITdeOffboardingPasswordCommand _tdeOffboardingPasswordCommand;
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery;
private readonly IFeatureService _featureService;
private readonly ITwoFactorEmailService _twoFactorEmailService;
public AccountsController(
IOrganizationService organizationService,
@@ -44,7 +47,8 @@ public class AccountsController : Controller
ISetInitialMasterPasswordCommand setInitialMasterPasswordCommand,
ITdeOffboardingPasswordCommand tdeOffboardingPasswordCommand,
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
IFeatureService featureService
IFeatureService featureService,
ITwoFactorEmailService twoFactorEmailService
)
{
_organizationService = organizationService;
@@ -56,6 +60,8 @@ public class AccountsController : Controller
_tdeOffboardingPasswordCommand = tdeOffboardingPasswordCommand;
_twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
_featureService = featureService;
_twoFactorEmailService = twoFactorEmailService;
}
@@ -619,7 +625,14 @@ public class AccountsController : Controller
[HttpPost("resend-new-device-otp")]
public async Task ResendNewDeviceOtpAsync([FromBody] UnauthenticatedSecretVerificationRequestModel request)
{
await _userService.ResendNewDeviceVerificationEmail(request.Email, request.Secret);
var user = await _userService.GetUserByPrincipalAsync(User) ?? throw new UnauthorizedAccessException();
if (!await _userService.VerifySecretAsync(user, request.Secret))
{
await Task.Delay(2000);
throw new BadRequestException(string.Empty, "User verification failed.");
}
await _twoFactorEmailService.SendNewDeviceVerificationEmailAsync(user);
}
[HttpPost("verify-devices")]

View File

@@ -7,6 +7,7 @@ using Bit.Core.Auth.Enums;
using Bit.Core.Auth.Identity.TokenProviders;
using Bit.Core.Auth.LoginFeatures.PasswordlessLogin.Interfaces;
using Bit.Core.Auth.Models.Business.Tokenables;
using Bit.Core.Auth.Services;
using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Exceptions;
@@ -34,6 +35,7 @@ public class TwoFactorController : Controller
private readonly IDuoUniversalTokenService _duoUniversalTokenService;
private readonly IDataProtectorTokenFactory<TwoFactorAuthenticatorUserVerificationTokenable> _twoFactorAuthenticatorDataProtector;
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _ssoEmailTwoFactorSessionDataProtector;
private readonly ITwoFactorEmailService _twoFactorEmailService;
public TwoFactorController(
IUserService userService,
@@ -44,7 +46,8 @@ public class TwoFactorController : Controller
IVerifyAuthRequestCommand verifyAuthRequestCommand,
IDuoUniversalTokenService duoUniversalConfigService,
IDataProtectorTokenFactory<TwoFactorAuthenticatorUserVerificationTokenable> twoFactorAuthenticatorDataProtector,
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> ssoEmailTwoFactorSessionDataProtector)
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> ssoEmailTwoFactorSessionDataProtector,
ITwoFactorEmailService twoFactorEmailService)
{
_userService = userService;
_organizationRepository = organizationRepository;
@@ -55,6 +58,7 @@ public class TwoFactorController : Controller
_duoUniversalTokenService = duoUniversalConfigService;
_twoFactorAuthenticatorDataProtector = twoFactorAuthenticatorDataProtector;
_ssoEmailTwoFactorSessionDataProtector = ssoEmailTwoFactorSessionDataProtector;
_twoFactorEmailService = twoFactorEmailService;
}
[HttpGet("")]
@@ -297,8 +301,9 @@ public class TwoFactorController : Controller
public async Task SendEmail([FromBody] TwoFactorEmailRequestModel model)
{
var user = await CheckAsync(model, false, true);
// Add email to the user's 2FA providers, with the email address they've provided.
model.ToUser(user);
await _userService.SendTwoFactorEmailAsync(user, false);
await _twoFactorEmailService.SendTwoFactorSetupEmailAsync(user);
}
[AllowAnonymous]
@@ -316,15 +321,14 @@ public class TwoFactorController : Controller
.VerifyAuthRequestAsync(new Guid(requestModel.AuthRequestId),
requestModel.AuthRequestAccessCode))
{
await _userService.SendTwoFactorEmailAsync(user);
return;
await _twoFactorEmailService.SendTwoFactorEmailAsync(user);
}
}
else if (!string.IsNullOrEmpty(requestModel.SsoEmail2FaSessionToken))
{
if (ValidateSsoEmail2FaToken(requestModel.SsoEmail2FaSessionToken, user))
{
await _userService.SendTwoFactorEmailAsync(user);
await _twoFactorEmailService.SendTwoFactorEmailAsync(user);
return;
}
@@ -333,7 +337,7 @@ public class TwoFactorController : Controller
}
else if (await _userService.VerifySecretAsync(user, requestModel.Secret))
{
await _userService.SendTwoFactorEmailAsync(user);
await _twoFactorEmailService.SendTwoFactorEmailAsync(user);
return;
}
}