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

[SG-698] Refactored 2fa send email and identity to cater for passwordless (#2346)

* Allow for auth request validation for sending two factor emails

* Refactored 2fa send email and identity to cater for passwordless

* Refactored 2fa send email and identity to cater for passwordless

Signed-off-by: gbubemismith <gsmithwalter@gmail.com>

* Inform that we track issues outside of Github (#2331)

* Inform that we track issues outside of Github

* Use checkboxes for info acknowledgement

Signed-off-by: gbubemismith <gsmithwalter@gmail.com>

* Refactored 2fa send email and identity to cater for passwordless

* ran dotnet format

Signed-off-by: gbubemismith <gsmithwalter@gmail.com>
Co-authored-by: addison <addisonbeck1@gmail.com>
This commit is contained in:
Gbubemi Smith
2022-10-18 14:50:48 -04:00
committed by GitHub
parent 864ab5231d
commit 4a26c55599
8 changed files with 92 additions and 20 deletions

View File

@ -6,6 +6,7 @@ using Bit.Core.Context;
using Bit.Core.Entities;
using Bit.Core.Enums;
using Bit.Core.Exceptions;
using Bit.Core.LoginFeatures.PasswordlessLogin.Interfaces;
using Bit.Core.Repositories;
using Bit.Core.Services;
using Bit.Core.Settings;
@ -28,6 +29,7 @@ public class TwoFactorController : Controller
private readonly GlobalSettings _globalSettings;
private readonly UserManager<User> _userManager;
private readonly ICurrentContext _currentContext;
private readonly IVerifyAuthRequestCommand _verifyAuthRequestCommand;
public TwoFactorController(
IUserService userService,
@ -35,7 +37,8 @@ public class TwoFactorController : Controller
IOrganizationService organizationService,
GlobalSettings globalSettings,
UserManager<User> userManager,
ICurrentContext currentContext)
ICurrentContext currentContext,
IVerifyAuthRequestCommand verifyAuthRequestCommand)
{
_userService = userService;
_organizationRepository = organizationRepository;
@ -43,6 +46,7 @@ public class TwoFactorController : Controller
_globalSettings = globalSettings;
_userManager = userManager;
_currentContext = currentContext;
_verifyAuthRequestCommand = verifyAuthRequestCommand;
}
[HttpGet("")]
@ -285,19 +289,27 @@ public class TwoFactorController : Controller
var user = await _userManager.FindByEmailAsync(model.Email.ToLowerInvariant());
if (user != null)
{
if (await _userService.VerifySecretAsync(user, model.Secret))
// check if 2FA email is from passwordless
if (!string.IsNullOrEmpty(model.AuthRequestAccessCode))
{
var isBecauseNewDeviceLogin = false;
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
&&
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
if (await _verifyAuthRequestCommand
.VerifyAuthRequestAsync(model.AuthRequestId, model.AuthRequestAccessCode))
{
model.ToUser(user);
isBecauseNewDeviceLogin = true;
}
var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model);
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
return;
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
return;
}
}
else
{
if (await _userService.VerifySecretAsync(user, model.Secret))
{
var isBecauseNewDeviceLogin = await IsNewDeviceLoginAsync(user, model);
await _userService.SendTwoFactorEmailAsync(user, isBecauseNewDeviceLogin);
return;
}
}
}
@ -455,4 +467,17 @@ public class TwoFactorController : Controller
await Task.Delay(500);
}
}
private async Task<bool> IsNewDeviceLoginAsync(User user, TwoFactorEmailRequestModel model)
{
if (user.GetTwoFactorProvider(TwoFactorProviderType.Email) is null
&&
await _userService.Needs2FABecauseNewDeviceAsync(user, model.DeviceIdentifier, null))
{
model.ToUser(user);
return true;
}
return false;
}
}

View File

@ -7,13 +7,14 @@ public class SecretVerificationRequestModel : IValidatableObject
[StringLength(300)]
public string MasterPasswordHash { get; set; }
public string OTP { get; set; }
public string AuthRequestAccessCode { get; set; }
public string Secret => !string.IsNullOrEmpty(MasterPasswordHash) ? MasterPasswordHash : OTP;
public virtual IEnumerable<ValidationResult> Validate(ValidationContext validationContext)
{
if (string.IsNullOrEmpty(Secret))
if (string.IsNullOrEmpty(Secret) && string.IsNullOrEmpty(AuthRequestAccessCode))
{
yield return new ValidationResult("MasterPasswordHash or OTP must be supplied.");
yield return new ValidationResult("MasterPasswordHash, OTP or AccessCode must be supplied.");
}
}
}

View File

@ -204,6 +204,8 @@ public class TwoFactorEmailRequestModel : SecretVerificationRequestModel
public string DeviceIdentifier { get; set; }
public Guid AuthRequestId { get; set; }
public User ToUser(User extistingUser)
{
var providers = extistingUser.GetTwoFactorProviders();