mirror of
https://github.com/bitwarden/server.git
synced 2025-06-30 07:36:14 -05:00
[PM-19029][PM-19203] Addressing UserService
tech debt around ITwoFactorIsEnabledQuery
(#5754)
* fix : split out the interface from the TwoFactorAuthenticationValidator into separate file. * fix: replacing IUserService.TwoFactorEnabled with ITwoFactorEnabledQuery * fix: combined logic for both bulk and single user look ups for TwoFactorIsEnabledQuery. * fix: return two factor provider enabled on CanGenerate() method. * tech debt: modfifying MFA providers to call the database less to validate if two factor is enabled. * tech debt: removed unused service from AuthenticatorTokenProvider * doc: added documentation to ITwoFactorProviderUsers * doc: updated comments for TwoFactorIsEnabled impl * test: fixing tests for ITwoFactorIsEnabledQuery * test: updating tests to have correct DI and removing test for automatic email of TOTP. * test: adding better test coverage
This commit is contained in:
@ -4,6 +4,7 @@ using Bit.Core.Auth.Enums;
|
||||
using Bit.Core.Auth.Identity.TokenProviders;
|
||||
using Bit.Core.Auth.Models;
|
||||
using Bit.Core.Auth.Models.Business.Tokenables;
|
||||
using Bit.Core.Auth.UserFeatures.TwoFactorAuth.Interfaces;
|
||||
using Bit.Core.Context;
|
||||
using Bit.Core.Entities;
|
||||
using Bit.Core.Models.Data.Organizations;
|
||||
@ -16,56 +17,25 @@ using Microsoft.AspNetCore.Identity;
|
||||
|
||||
namespace Bit.Identity.IdentityServer.RequestValidators;
|
||||
|
||||
public interface ITwoFactorAuthenticationValidator
|
||||
{
|
||||
/// <summary>
|
||||
/// Check if the user is required to use two-factor authentication to login. This is based on the user's
|
||||
/// enabled two-factor providers, the user's organizations enabled two-factor providers, and the grant type.
|
||||
/// Client credentials and webauthn grant types do not require two-factor authentication.
|
||||
/// </summary>
|
||||
/// <param name="user">the active user for the request</param>
|
||||
/// <param name="request">the request that contains the grant types</param>
|
||||
/// <returns>boolean</returns>
|
||||
Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request);
|
||||
/// <summary>
|
||||
/// Builds the two-factor authentication result for the user based on the available two-factor providers
|
||||
/// from either their user account or Organization.
|
||||
/// </summary>
|
||||
/// <param name="user">user trying to login</param>
|
||||
/// <param name="organization">organization associated with the user; Can be null</param>
|
||||
/// <returns>Dictionary with the TwoFactorProviderType as the Key and the Provider Metadata as the Value</returns>
|
||||
Task<Dictionary<string, object>> BuildTwoFactorResultAsync(User user, Organization organization);
|
||||
/// <summary>
|
||||
/// Uses the built in userManager methods to verify the two-factor token for the user. If the organization uses
|
||||
/// organization duo, it will use the organization duo token provider to verify the token.
|
||||
/// </summary>
|
||||
/// <param name="user">the active User</param>
|
||||
/// <param name="organization">organization of user; can be null</param>
|
||||
/// <param name="twoFactorProviderType">Two Factor Provider to use to verify the token</param>
|
||||
/// <param name="token">secret passed from the user and consumed by the two-factor provider's verify method</param>
|
||||
/// <returns>boolean</returns>
|
||||
Task<bool> VerifyTwoFactorAsync(User user, Organization organization, TwoFactorProviderType twoFactorProviderType, string token);
|
||||
}
|
||||
|
||||
public class TwoFactorAuthenticationValidator(
|
||||
IUserService userService,
|
||||
UserManager<User> userManager,
|
||||
IOrganizationDuoUniversalTokenProvider organizationDuoWebTokenProvider,
|
||||
IFeatureService featureService,
|
||||
IApplicationCacheService applicationCacheService,
|
||||
IOrganizationUserRepository organizationUserRepository,
|
||||
IOrganizationRepository organizationRepository,
|
||||
IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> ssoEmail2faSessionTokeFactory,
|
||||
ITwoFactorIsEnabledQuery twoFactorIsEnabledQuery,
|
||||
ICurrentContext currentContext) : ITwoFactorAuthenticationValidator
|
||||
{
|
||||
private readonly IUserService _userService = userService;
|
||||
private readonly UserManager<User> _userManager = userManager;
|
||||
private readonly IOrganizationDuoUniversalTokenProvider _organizationDuoUniversalTokenProvider = organizationDuoWebTokenProvider;
|
||||
private readonly IFeatureService _featureService = featureService;
|
||||
private readonly IApplicationCacheService _applicationCacheService = applicationCacheService;
|
||||
private readonly IOrganizationUserRepository _organizationUserRepository = organizationUserRepository;
|
||||
private readonly IOrganizationRepository _organizationRepository = organizationRepository;
|
||||
private readonly IDataProtectorTokenFactory<SsoEmail2faSessionTokenable> _ssoEmail2faSessionTokeFactory = ssoEmail2faSessionTokeFactory;
|
||||
private readonly ITwoFactorIsEnabledQuery _twoFactorIsEnabledQuery = twoFactorIsEnabledQuery;
|
||||
private readonly ICurrentContext _currentContext = currentContext;
|
||||
|
||||
public async Task<Tuple<bool, Organization>> RequiresTwoFactorAsync(User user, ValidatedTokenRequest request)
|
||||
@ -161,7 +131,7 @@ public class TwoFactorAuthenticationValidator(
|
||||
|
||||
// These cases we want to always return false, U2f is deprecated and OrganizationDuo
|
||||
// uses a different flow than the other two factor providers, it follows the same
|
||||
// structure of a UserTokenProvider but has it's logic ran outside the usual token
|
||||
// structure of a UserTokenProvider but has it's logic runs outside the usual token
|
||||
// provider flow. See IOrganizationDuoUniversalTokenProvider.cs
|
||||
if (type is TwoFactorProviderType.U2f or TwoFactorProviderType.OrganizationDuo)
|
||||
{
|
||||
@ -171,12 +141,12 @@ public class TwoFactorAuthenticationValidator(
|
||||
// Now we are concerning the rest of the Two Factor Provider Types
|
||||
|
||||
// The intent of this check is to make sure that the user is using a 2FA provider that
|
||||
// is enabled and allowed by their premium status. The exception for Remember
|
||||
// is because it is a "special" 2FA type that isn't ever explicitly
|
||||
// is enabled and allowed by their premium status.
|
||||
// The exception for Remember is because it is a "special" 2FA type that isn't ever explicitly
|
||||
// enabled by a user, so we can't check the user's 2FA providers to see if they're
|
||||
// enabled. We just have to check if the token is valid.
|
||||
if (type != TwoFactorProviderType.Remember &&
|
||||
!await _userService.TwoFactorProviderIsEnabledAsync(type, user))
|
||||
user.GetTwoFactorProvider(type) == null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
Reference in New Issue
Block a user