diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 1ed66fc34..f59ba944e 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -31,6 +31,8 @@ namespace Bit.App.Pages private readonly ILogger _logger; private readonly IWatchDeviceService _watchDeviceService; private readonly WeakEventManager _secretEntryFocusWeakEventManager = new WeakEventManager(); + private readonly IPolicyService _policyService; + private readonly IPasswordGenerationService _passwordGenerationService; private string _email; private string _masterPassword; @@ -47,6 +49,8 @@ namespace Bit.App.Pages private bool _isPinProtected; private bool _isPinProtectedWithKey; + private MasterPasswordPolicyOptions _enforcedMasterPasswordOptions; + public LockPageViewModel() { _apiService = ServiceContainer.Resolve("apiService"); @@ -61,6 +65,9 @@ namespace Bit.App.Pages _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); _logger = ServiceContainer.Resolve("logger"); _watchDeviceService = ServiceContainer.Resolve(); + _policyService = ServiceContainer.Resolve("policyService"); + _passwordGenerationService = + ServiceContainer.Resolve("passwordGenerationService"); PageTitle = AppResources.VerifyMasterPassword; TogglePasswordCommand = new Command(TogglePassword); @@ -307,7 +314,8 @@ namespace Bit.App.Pages request.MasterPasswordHash = keyHash; try { - await _apiService.PostAccountVerifyPasswordAsync(request); + var response = await _apiService.PostAccountVerifyPasswordAsync(request); + _enforcedMasterPasswordOptions = response.MasterPasswordPolicy; passwordValid = true; var localKeyHash = await _cryptoService.HashPasswordAsync(MasterPassword, key, HashPurpose.LocalAuthorization); await _cryptoService.SetKeyHashAsync(localKeyHash); @@ -328,6 +336,14 @@ namespace Bit.App.Pages var pinKey = await _cryptoService.MakePinKeyAysnc(decPin, _email, kdfConfig); await _stateService.SetPinProtectedKeyAsync(await _cryptoService.EncryptAsync(key.Key, pinKey)); } + + if (await RequirePasswordChangeAsync()) + { + // Save the ForcePasswordResetReason to force a password reset after unlock + await _stateService.SetForcePasswordResetReasonAsync( + ForcePasswordResetReason.WeakMasterPasswordOnLogin); + } + MasterPassword = string.Empty; await AppHelpers.ResetInvalidUnlockAttemptsAsync(); await SetKeyAndContinueAsync(key); @@ -352,6 +368,30 @@ namespace Bit.App.Pages } } + /// + /// Checks if the master password requires updating to meet the enforced policy requirements + /// + private async Task RequirePasswordChangeAsync() + { + // If we do not have any saved policies, attempt to load them from the service + _enforcedMasterPasswordOptions ??= await _policyService.GetMasterPasswordPolicyOptions(); + + // No policy to enforce on login/unlock + if (!(_enforcedMasterPasswordOptions is { EnforceOnLogin: true })) + { + return false; + } + + var strength = _passwordGenerationService.PasswordStrength( + MasterPassword, _passwordGenerationService.GetPasswordStrengthUserInput(_email)).Score; + + return !await _policyService.EvaluateMasterPassword( + strength, + MasterPassword, + _enforcedMasterPasswordOptions + ); + } + public async Task LogOutAsync() { var confirmed = await _platformUtilsService.ShowDialogAsync(AppResources.LogoutConfirmation,