From 721db0974de3d4bfde78d7920ca051bd1e774a3c Mon Sep 17 00:00:00 2001 From: Shane Melton Date: Tue, 7 Mar 2023 13:23:59 -0800 Subject: [PATCH] [AC-1070] Introduce logic to evaluate master password after successful login --- src/Core/Services/AuthService.cs | 36 ++++++++++++++++++++++++++++++-- 1 file changed, 34 insertions(+), 2 deletions(-) diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index 4092fbace..099d31610 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -6,6 +6,7 @@ using System.Threading.Tasks; using Bit.Core.Abstractions; using Bit.Core.Enums; using Bit.Core.Exceptions; +using Bit.Core.Models.Data; using Bit.Core.Models.Domain; using Bit.Core.Models.Request; using Bit.Core.Models.Response; @@ -31,6 +32,7 @@ namespace Bit.Core.Services private readonly LazyResolve _watchDeviceService = new LazyResolve(); private SymmetricCryptoKey _key; + private MasterPasswordPolicyOptions _masterPasswordPolicy { get; set; } public AuthService( ICryptoService cryptoService, @@ -141,8 +143,36 @@ namespace Bit.Core.Services var key = await MakePreloginKeyAsync(masterPassword, email); var hashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key); var localHashedPassword = await _cryptoService.HashPasswordAsync(masterPassword, key, HashPurpose.LocalAuthorization); - return await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, - null, captchaToken); + var result = await LogInHelperAsync(email, hashedPassword, localHashedPassword, null, null, null, key, null, null, null, captchaToken); + + if (await RequirePasswordChange(email, masterPassword)) + { + result.ForcePasswordReset = true; + } + + return result; + } + + /// + /// Evaluates the supplied master password against the master password policy provided by the Identity response. + /// + /// + /// + /// True if the master password does NOT meet any policy requirements, false otherwise (or if no policy present) + private async Task RequirePasswordChange(string email, string masterPassword) + { + // No policy with EnforceOnLogin enabled, we're done. + if (!(_masterPasswordPolicy is { EnforceOnLogin: true })) + { + return false; + } + + var passwordStrength = _passwordGenerationService.PasswordStrength( + masterPassword, + _passwordGenerationService.GetPasswordStrengthUserInput(email) + ).Score; + + return !await _policyService.EvaluateMasterPassword(passwordStrength, masterPassword, _masterPasswordPolicy); } public async Task LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered) @@ -369,6 +399,7 @@ namespace Bit.Core.Services TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2; result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2; CaptchaToken = response.TwoFactorResponse.CaptchaToken; + _masterPasswordPolicy = response.TwoFactorResponse.MasterPasswordPolicy; await _tokenService.ClearTwoFactorTokenAsync(email); return result; } @@ -376,6 +407,7 @@ namespace Bit.Core.Services var tokenResponse = response.TokenResponse; result.ResetMasterPassword = tokenResponse.ResetMasterPassword; result.ForcePasswordReset = tokenResponse.ForcePasswordReset; + _masterPasswordPolicy = tokenResponse.MasterPasswordPolicy; if (tokenResponse.TwoFactorToken != null) { await _tokenService.SetTwoFactorTokenAsync(tokenResponse.TwoFactorToken, email);