diff --git a/src/App/Abstractions/IPasswordRepromptService.cs b/src/App/Abstractions/IPasswordRepromptService.cs index 8684b91da..2490271c2 100644 --- a/src/App/Abstractions/IPasswordRepromptService.cs +++ b/src/App/Abstractions/IPasswordRepromptService.cs @@ -1,4 +1,5 @@ using System.Threading.Tasks; +using Bit.Core.Enums; namespace Bit.App.Abstractions { @@ -6,7 +7,7 @@ namespace Bit.App.Abstractions { string[] ProtectedFields { get; } - Task ShowPasswordPromptAsync(); + Task PromptAndCheckPasswordIfNeededAsync(CipherRepromptType repromptType = CipherRepromptType.Password); Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync(); } diff --git a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs index aade36dd6..12b4a619b 100644 --- a/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs +++ b/src/App/Pages/Vault/AutofillCiphersPageViewModel.cs @@ -82,7 +82,7 @@ namespace Bit.App.Pages return; } - if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync()) + if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { return; } diff --git a/src/App/Pages/Vault/CipherDetailsPageViewModel.cs b/src/App/Pages/Vault/CipherDetailsPageViewModel.cs index 0336ea4ea..5bfb30129 100644 --- a/src/App/Pages/Vault/CipherDetailsPageViewModel.cs +++ b/src/App/Pages/Vault/CipherDetailsPageViewModel.cs @@ -698,12 +698,12 @@ namespace Bit.App.Pages public async Task PromptPasswordAsync() { - if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted) + if (_passwordReprompted) { return true; } - return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync(); + return _passwordReprompted = await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(Cipher.Reprompt); } private async Task CanCloneAsync() diff --git a/src/App/Pages/Vault/CiphersPageViewModel.cs b/src/App/Pages/Vault/CiphersPageViewModel.cs index 9025c7341..5d4c41833 100644 --- a/src/App/Pages/Vault/CiphersPageViewModel.cs +++ b/src/App/Pages/Vault/CiphersPageViewModel.cs @@ -191,7 +191,7 @@ namespace Bit.App.Pages if (_appOptions?.OtpData != null) { - if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync()) + if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { return; } @@ -208,7 +208,7 @@ namespace Bit.App.Pages } else if (selection == AppResources.Autofill || selection == AppResources.AutofillAndSave) { - if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync()) + if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { return; } diff --git a/src/App/Pages/Vault/OTPCipherSelectionPageViewModel.cs b/src/App/Pages/Vault/OTPCipherSelectionPageViewModel.cs index 537823194..51f5cbedd 100644 --- a/src/App/Pages/Vault/OTPCipherSelectionPageViewModel.cs +++ b/src/App/Pages/Vault/OTPCipherSelectionPageViewModel.cs @@ -60,7 +60,7 @@ namespace Bit.App.Pages var cipher = listItem.Cipher; - if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync()) + if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { return; } diff --git a/src/App/Services/MobilePasswordRepromptService.cs b/src/App/Services/MobilePasswordRepromptService.cs index fdec31e82..c688e6edb 100644 --- a/src/App/Services/MobilePasswordRepromptService.cs +++ b/src/App/Services/MobilePasswordRepromptService.cs @@ -2,6 +2,7 @@ using Bit.App.Abstractions; using Bit.App.Resources; using Bit.Core.Abstractions; +using Bit.Core.Enums; namespace Bit.App.Services { @@ -18,8 +19,13 @@ namespace Bit.App.Services public string[] ProtectedFields { get; } = { "LoginTotp", "LoginPassword", "H_FieldValue", "CardNumber", "CardCode" }; - public async Task ShowPasswordPromptAsync() + public async Task PromptAndCheckPasswordIfNeededAsync(CipherRepromptType repromptType = CipherRepromptType.Password) { + if (repromptType == CipherRepromptType.None || await ShouldByPassMasterPasswordRepromptAsync()) + { + return true; + } + return await _platformUtilsService.ShowPasswordDialogAsync(AppResources.PasswordConfirmation, AppResources.PasswordConfirmationDesc, ValidatePasswordAsync); } @@ -38,5 +44,10 @@ namespace Bit.App.Services return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null); } + + private async Task ShouldByPassMasterPasswordRepromptAsync() + { + return await _cryptoService.GetMasterKeyHashAsync() is null; + } } } diff --git a/src/App/Utilities/AppHelpers.cs b/src/App/Utilities/AppHelpers.cs index 49086a18b..67e152b56 100644 --- a/src/App/Utilities/AppHelpers.cs +++ b/src/App/Utilities/AppHelpers.cs @@ -99,60 +99,55 @@ namespace Bit.App.Utilities { await page.Navigation.PushModalAsync(new NavigationPage(new CipherDetailsPage(cipher.Id))); } - else if (selection == AppResources.Edit) + else if (selection == AppResources.Edit + && + await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { - if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) - { - await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id))); - } + await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id))); } else if (selection == AppResources.CopyUsername) { await clipboardService.CopyTextAsync(cipher.Type == CipherType.Login ? cipher.Login.Username : cipher.Fido2Key.UserName); platformUtilsService.ShowToastForCopiedValue(AppResources.Username); } - else if (selection == AppResources.CopyPassword) + else if (selection == AppResources.CopyPassword + && + await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { - if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) - { - await clipboardService.CopyTextAsync(cipher.Login.Password); - platformUtilsService.ShowToastForCopiedValue(AppResources.Password); - var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id); - } + await clipboardService.CopyTextAsync(cipher.Login.Password); + platformUtilsService.ShowToastForCopiedValue(AppResources.Password); + var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id); } - else if (selection == AppResources.CopyTotp) + else if (selection == AppResources.CopyTotp + && + await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { - if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) + var totpService = ServiceContainer.Resolve("totpService"); + var totp = await totpService.GetCodeAsync(cipher.Login.Totp); + if (!string.IsNullOrWhiteSpace(totp)) { - var totpService = ServiceContainer.Resolve("totpService"); - var totp = await totpService.GetCodeAsync(cipher.Login.Totp); - if (!string.IsNullOrWhiteSpace(totp)) - { - await clipboardService.CopyTextAsync(totp); - platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp); - } + await clipboardService.CopyTextAsync(totp); + platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp); } } else if (selection == AppResources.Launch && cipher.CanLaunch) { platformUtilsService.LaunchUri(cipher.LaunchUri); } - else if (selection == AppResources.CopyNumber) + else if (selection == AppResources.CopyNumber + && + await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { - if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) - { - await clipboardService.CopyTextAsync(cipher.Card.Number); - platformUtilsService.ShowToastForCopiedValue(AppResources.Number); - } + await clipboardService.CopyTextAsync(cipher.Card.Number); + platformUtilsService.ShowToastForCopiedValue(AppResources.Number); } - else if (selection == AppResources.CopySecurityCode) + else if (selection == AppResources.CopySecurityCode + && + await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt)) { - if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync()) - { - await clipboardService.CopyTextAsync(cipher.Card.Code); - platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode); - var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id); - } + await clipboardService.CopyTextAsync(cipher.Card.Code); + platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode); + eventService.CollectAsync(EventType.Cipher_ClientCopiedCardCode, cipher.Id).FireAndForget(); } else if (selection == AppResources.CopyNotes) { diff --git a/src/iOS.Autofill/CredentialProviderViewController.cs b/src/iOS.Autofill/CredentialProviderViewController.cs index e3dbc2d9e..e214835ea 100644 --- a/src/iOS.Autofill/CredentialProviderViewController.cs +++ b/src/iOS.Autofill/CredentialProviderViewController.cs @@ -313,7 +313,7 @@ namespace Bit.iOS.Autofill // Add a timeout to resolve keyboard not always showing up. await Task.Delay(250); var passwordRepromptService = ServiceContainer.Resolve("passwordRepromptService"); - if (!await passwordRepromptService.ShowPasswordPromptAsync()) + if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync()) { var err = new NSError(new NSString("ASExtensionErrorDomain"), Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null); diff --git a/src/iOS.Autofill/Utilities/AutofillHelpers.cs b/src/iOS.Autofill/Utilities/AutofillHelpers.cs index a2da552b4..bd2adca93 100644 --- a/src/iOS.Autofill/Utilities/AutofillHelpers.cs +++ b/src/iOS.Autofill/Utilities/AutofillHelpers.cs @@ -33,7 +33,7 @@ namespace Bit.iOS.Autofill.Utilities return; } - if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await passwordRepromptService.ShowPasswordPromptAsync()) + if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt)) { return; } diff --git a/src/iOS.Extension/LoginListViewController.cs b/src/iOS.Extension/LoginListViewController.cs index 3df4654d4..b10280ec7 100644 --- a/src/iOS.Extension/LoginListViewController.cs +++ b/src/iOS.Extension/LoginListViewController.cs @@ -126,7 +126,7 @@ namespace Bit.iOS.Extension return; } - if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await _controller.PasswordRepromptService.ShowPasswordPromptAsync()) + if (!await _controller.PasswordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt)) { return; }