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

[EC-400] Code clean up Device Verification (#2601)

* EC-400 Clean up code regarding Unknown Device Verification

* EC-400 Fix formatting
This commit is contained in:
Federico Maccaroni
2023-02-17 10:15:28 -03:00
committed by GitHub
parent 7594ca1122
commit 69511160cb
32 changed files with 6876 additions and 469 deletions

View File

@ -100,24 +100,20 @@ public abstract class BaseRequestValidator<T> where T : class
return;
}
var (isTwoFactorRequired, requires2FABecauseNewDevice, twoFactorOrganization) = await RequiresTwoFactorAsync(user, request);
var (isTwoFactorRequired, twoFactorOrganization) = await RequiresTwoFactorAsync(user, request);
if (isTwoFactorRequired)
{
// Just defaulting it
var twoFactorProviderType = TwoFactorProviderType.Authenticator;
if (!twoFactorRequest || !Enum.TryParse(twoFactorProvider, out twoFactorProviderType))
{
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context, requires2FABecauseNewDevice);
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context);
return;
}
BeforeVerifyTwoFactor(user, twoFactorProviderType, requires2FABecauseNewDevice);
var verified = await VerifyTwoFactor(user, twoFactorOrganization,
twoFactorProviderType, twoFactorToken);
AfterVerifyTwoFactor(user, twoFactorProviderType, requires2FABecauseNewDevice);
if ((!verified || isBot) && twoFactorProviderType != TwoFactorProviderType.Remember)
{
await UpdateFailedAuthDetailsAsync(user, true, !validatorContext.KnownDevice);
@ -128,7 +124,7 @@ public abstract class BaseRequestValidator<T> where T : class
{
// Delay for brute force.
await Task.Delay(2000);
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context, requires2FABecauseNewDevice);
await BuildTwoFactorResultAsync(user, twoFactorOrganization, context);
return;
}
}
@ -201,7 +197,7 @@ public abstract class BaseRequestValidator<T> where T : class
await SetSuccessResult(context, user, claims, customResponse);
}
protected async Task BuildTwoFactorResultAsync(User user, Organization organization, T context, bool requires2FABecauseNewDevice)
protected async Task BuildTwoFactorResultAsync(User user, Organization organization, T context)
{
var providerKeys = new List<byte>();
var providers = new Dictionary<string, Dictionary<string, object>>();
@ -226,23 +222,8 @@ public abstract class BaseRequestValidator<T> where T : class
if (!enabledProviders.Any())
{
if (!requires2FABecauseNewDevice)
{
await BuildErrorResultAsync("No two-step providers enabled.", false, context, user);
return;
}
var emailProvider = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = user.Email.ToLowerInvariant() },
Enabled = true
};
enabledProviders.Add(new KeyValuePair<TwoFactorProviderType, TwoFactorProvider>(
TwoFactorProviderType.Email, emailProvider));
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = emailProvider
});
await BuildErrorResultAsync("No two-step providers enabled.", false, context, user);
return;
}
foreach (var provider in enabledProviders)
@ -262,7 +243,7 @@ public abstract class BaseRequestValidator<T> where T : class
if (enabledProviders.Count() == 1 && enabledProviders.First().Key == TwoFactorProviderType.Email)
{
// Send email now if this is their only 2FA method
await _userService.SendTwoFactorEmailAsync(user, requires2FABecauseNewDevice);
await _userService.SendTwoFactorEmailAsync(user);
}
}
@ -298,12 +279,12 @@ public abstract class BaseRequestValidator<T> where T : class
protected abstract void SetErrorResult(T context, Dictionary<string, object> customResponse);
private async Task<Tuple<bool, bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request)
private async Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request)
{
if (request.GrantType == "client_credentials")
{
// Do not require MFA for api key logins
return new Tuple<bool, bool, Organization>(false, false, null);
return new Tuple<bool, Organization>(false, null);
}
var individualRequired = _userManager.SupportsUserTwoFactor &&
@ -325,17 +306,7 @@ public abstract class BaseRequestValidator<T> where T : class
}
}
var requires2FA = individualRequired || firstEnabledOrg != null;
var requires2FABecauseNewDevice = !requires2FA
&&
await _userService.Needs2FABecauseNewDeviceAsync(
user,
GetDeviceFromRequest(request)?.Identifier,
request.GrantType);
requires2FA = requires2FA || requires2FABecauseNewDevice;
return new Tuple<bool, bool, Organization>(requires2FA, requires2FABecauseNewDevice, firstEnabledOrg);
return new Tuple<bool, Organization>(individualRequired || firstEnabledOrg != null, firstEnabledOrg);
}
private async Task<bool> IsValidAuthTypeAsync(User user, string grantType)
@ -413,29 +384,6 @@ public abstract class BaseRequestValidator<T> where T : class
};
}
private void BeforeVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice)
{
if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice)
{
user.SetTwoFactorProviders(new Dictionary<TwoFactorProviderType, TwoFactorProvider>
{
[TwoFactorProviderType.Email] = new TwoFactorProvider
{
MetaData = new Dictionary<string, object> { ["Email"] = user.Email.ToLowerInvariant() },
Enabled = true
}
});
}
}
private void AfterVerifyTwoFactor(User user, TwoFactorProviderType type, bool requires2FABecauseNewDevice)
{
if (type == TwoFactorProviderType.Email && requires2FABecauseNewDevice)
{
user.ClearTwoFactorProviders();
}
}
private async Task<bool> VerifyTwoFactor(User user, Organization organization, TwoFactorProviderType type,
string token)
{