mirror of
https://github.com/bitwarden/android.git
synced 2024-10-31 23:25:45 +03:00
Fixes for password reprompt (#1416)
This commit is contained in:
parent
33791a03ac
commit
2b8dbde923
7 changed files with 90 additions and 18 deletions
|
@ -229,6 +229,12 @@ namespace Bit.App.Pages
|
||||||
|
|
||||||
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
var selection = await DisplayActionSheet(AppResources.Options, AppResources.Cancel,
|
||||||
AppResources.Delete, options.ToArray());
|
AppResources.Delete, options.ToArray());
|
||||||
|
|
||||||
|
if (!await _vm.PromptPasswordAsync())
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (selection == AppResources.Delete)
|
if (selection == AppResources.Delete)
|
||||||
{
|
{
|
||||||
if (await _vm.DeleteAsync())
|
if (await _vm.DeleteAsync())
|
||||||
|
|
|
@ -176,6 +176,11 @@ namespace Bit.App.Services
|
||||||
var password = await _deviceActionService.DisplayPromptAync(AppResources.PasswordConfirmation,
|
var password = await _deviceActionService.DisplayPromptAync(AppResources.PasswordConfirmation,
|
||||||
AppResources.PasswordConfirmationDesc, null, AppResources.Submit, AppResources.Cancel, password: true);
|
AppResources.PasswordConfirmationDesc, null, AppResources.Submit, AppResources.Cancel, password: true);
|
||||||
|
|
||||||
|
if (password == null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
var valid = await validator(password);
|
var valid = await validator(password);
|
||||||
|
|
||||||
if (!valid)
|
if (!valid)
|
||||||
|
|
|
@ -82,7 +82,10 @@ namespace Bit.App.Utilities
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.Edit)
|
else if (selection == AppResources.Edit)
|
||||||
{
|
{
|
||||||
await page.Navigation.PushModalAsync(new NavigationPage(new AddEditPage(cipher.Id)));
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
|
{
|
||||||
|
await page.Navigation.PushModalAsync(new NavigationPage(new AddEditPage(cipher.Id)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyUsername)
|
else if (selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
|
|
|
@ -41,6 +41,8 @@
|
||||||
public static string PreviousPageKey = "previousPage";
|
public static string PreviousPageKey = "previousPage";
|
||||||
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
public static string InlineAutofillEnabledKey = "inlineAutofillEnabled";
|
||||||
public static string InvalidUnlockAttempts = "invalidUnlockAttempts";
|
public static string InvalidUnlockAttempts = "invalidUnlockAttempts";
|
||||||
|
public static string PasswordRepromptAutofillKey = "passwordRepromptAutofillKey";
|
||||||
|
public static string PasswordVerifiedAutofillKey = "passwordVerifiedAutofillKey";
|
||||||
public const int SelectFileRequestCode = 42;
|
public const int SelectFileRequestCode = 42;
|
||||||
public const int SelectFilePermissionRequestCode = 43;
|
public const int SelectFilePermissionRequestCode = 43;
|
||||||
public const int SaveFileRequestCode = 44;
|
public const int SaveFileRequestCode = 44;
|
||||||
|
|
|
@ -79,6 +79,9 @@ namespace Bit.iOS.Autofill
|
||||||
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
await storageService.SaveAsync(Bit.Core.Constants.PasswordRepromptAutofillKey, false);
|
||||||
|
await storageService.SaveAsync(Bit.Core.Constants.PasswordVerifiedAutofillKey, false);
|
||||||
if (!await IsAuthed() || await IsLocked())
|
if (!await IsAuthed() || await IsLocked())
|
||||||
{
|
{
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
|
@ -87,7 +90,7 @@ namespace Bit.iOS.Autofill
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_context.CredentialIdentity = credentialIdentity;
|
_context.CredentialIdentity = credentialIdentity;
|
||||||
await ProvideCredentialAsync();
|
await ProvideCredentialAsync(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
|
@ -209,7 +212,7 @@ namespace Bit.iOS.Autofill
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProvideCredentialAsync()
|
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
||||||
{
|
{
|
||||||
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
|
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
|
||||||
Bit.Core.Models.Domain.Cipher cipher = null;
|
Bit.Core.Models.Domain.Cipher cipher = null;
|
||||||
|
@ -229,6 +232,30 @@ namespace Bit.iOS.Autofill
|
||||||
|
|
||||||
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
var decCipher = await cipher.DecryptAsync();
|
var decCipher = await cipher.DecryptAsync();
|
||||||
|
if (decCipher.Reprompt != Bit.Core.Enums.CipherRepromptType.None)
|
||||||
|
{
|
||||||
|
// Prompt for password using either the lock screen or dialog unless
|
||||||
|
// already verified the password.
|
||||||
|
if (!userInteraction)
|
||||||
|
{
|
||||||
|
await storageService.SaveAsync(Bit.Core.Constants.PasswordRepromptAutofillKey, true);
|
||||||
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
|
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
|
||||||
|
ExtensionContext?.CancelRequest(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else if (!await storageService.GetAsync<bool>(Bit.Core.Constants.PasswordVerifiedAutofillKey))
|
||||||
|
{
|
||||||
|
var passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
|
if (!await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
|
{
|
||||||
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
|
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
|
||||||
|
ExtensionContext?.CancelRequest(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
string totpCode = null;
|
string totpCode = null;
|
||||||
var disableTotpCopy = await storageService.GetAsync<bool?>(Bit.Core.Constants.DisableAutoTotpCopyKey);
|
var disableTotpCopy = await storageService.GetAsync<bool?>(Bit.Core.Constants.DisableAutoTotpCopyKey);
|
||||||
if (!disableTotpCopy.GetValueOrDefault(false))
|
if (!disableTotpCopy.GetValueOrDefault(false))
|
||||||
|
@ -248,7 +275,8 @@ namespace Bit.iOS.Autofill
|
||||||
|
|
||||||
private async void CheckLock(Action notLockedAction)
|
private async void CheckLock(Action notLockedAction)
|
||||||
{
|
{
|
||||||
if (await IsLocked())
|
var storageService = ServiceContainer.Resolve<IStorageService>("storageService");
|
||||||
|
if (await IsLocked() || await storageService.GetAsync<bool>(Bit.Core.Constants.PasswordRepromptAutofillKey))
|
||||||
{
|
{
|
||||||
PerformSegue("lockPasswordSegue", this);
|
PerformSegue("lockPasswordSegue", this);
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,7 @@ namespace Bit.iOS.Autofill
|
||||||
{
|
{
|
||||||
BiometricIntegrityKey = Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey;
|
BiometricIntegrityKey = Bit.Core.Constants.iOSAutoFillBiometricIntegrityKey;
|
||||||
DismissModalAction = Cancel;
|
DismissModalAction = Cancel;
|
||||||
|
autofillExtension = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public CredentialProviderViewController CPViewController { get; set; }
|
public CredentialProviderViewController CPViewController { get; set; }
|
||||||
|
|
|
@ -28,6 +28,9 @@ namespace Bit.iOS.Core.Controllers
|
||||||
private bool _pinLock;
|
private bool _pinLock;
|
||||||
private bool _biometricLock;
|
private bool _biometricLock;
|
||||||
private bool _biometricIntegrityValid = true;
|
private bool _biometricIntegrityValid = true;
|
||||||
|
private bool _passwordReprompt = false;
|
||||||
|
|
||||||
|
protected bool autofillExtension = false;
|
||||||
|
|
||||||
public LockPasswordViewController(IntPtr handle)
|
public LockPasswordViewController(IntPtr handle)
|
||||||
: base(handle)
|
: base(handle)
|
||||||
|
@ -44,7 +47,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
|
|
||||||
public string BiometricIntegrityKey { get; set; }
|
public string BiometricIntegrityKey { get; set; }
|
||||||
|
|
||||||
public override void ViewDidLoad()
|
public override async void ViewDidLoad()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>("cryptoService");
|
||||||
|
@ -55,12 +58,24 @@ namespace Bit.iOS.Core.Controllers
|
||||||
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
_platformUtilsService = ServiceContainer.Resolve<IPlatformUtilsService>("platformUtilsService");
|
||||||
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
_biometricService = ServiceContainer.Resolve<IBiometricService>("biometricService");
|
||||||
|
|
||||||
_pinSet = _vaultTimeoutService.IsPinLockSetAsync().GetAwaiter().GetResult();
|
// We re-use the lock screen for autofill extension to verify master password
|
||||||
_pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
// when trying to access protected items.
|
||||||
_biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() &&
|
if (autofillExtension && await _storageService.GetAsync<bool>(Bit.Core.Constants.PasswordRepromptAutofillKey))
|
||||||
_cryptoService.HasKeyAsync().GetAwaiter().GetResult();
|
{
|
||||||
_biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter()
|
_passwordReprompt = true;
|
||||||
.GetResult();
|
_pinSet = Tuple.Create(false, false);
|
||||||
|
_pinLock = false;
|
||||||
|
_biometricLock = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pinSet = _vaultTimeoutService.IsPinLockSetAsync().GetAwaiter().GetResult();
|
||||||
|
_pinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2;
|
||||||
|
_biometricLock = _vaultTimeoutService.IsBiometricLockSetAsync().GetAwaiter().GetResult() &&
|
||||||
|
_cryptoService.HasKeyAsync().GetAwaiter().GetResult();
|
||||||
|
_biometricIntegrityValid = _biometricService.ValidateIntegrityAsync(BiometricIntegrityKey).GetAwaiter()
|
||||||
|
.GetResult();
|
||||||
|
}
|
||||||
|
|
||||||
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
|
BaseNavItem.Title = _pinLock ? AppResources.VerifyPIN : AppResources.VerifyMasterPassword;
|
||||||
BaseCancelButton.Title = AppResources.Cancel;
|
BaseCancelButton.Title = AppResources.Cancel;
|
||||||
|
@ -199,7 +214,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key2.Key, pinKey);
|
_vaultTimeoutService.PinProtectedKey = await _cryptoService.EncryptAsync(key2.Key, pinKey);
|
||||||
}
|
}
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await SetKeyAndContinueAsync(key2);
|
await SetKeyAndContinueAsync(key2, true);
|
||||||
|
|
||||||
// Re-enable biometrics
|
// Re-enable biometrics
|
||||||
if (_biometricLock & !_biometricIntegrityValid)
|
if (_biometricLock & !_biometricIntegrityValid)
|
||||||
|
@ -220,18 +235,22 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key)
|
private async Task SetKeyAndContinueAsync(SymmetricCryptoKey key, bool masterPassword = false)
|
||||||
{
|
{
|
||||||
var hasKey = await _cryptoService.HasKeyAsync();
|
var hasKey = await _cryptoService.HasKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
}
|
}
|
||||||
DoContinue();
|
DoContinue(masterPassword);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DoContinue()
|
private async void DoContinue(bool masterPassword = false)
|
||||||
{
|
{
|
||||||
|
if (masterPassword)
|
||||||
|
{
|
||||||
|
await _storageService.SaveAsync(Bit.Core.Constants.PasswordVerifiedAutofillKey, true);
|
||||||
|
}
|
||||||
_vaultTimeoutService.BiometricLocked = false;
|
_vaultTimeoutService.BiometricLocked = false;
|
||||||
MasterPasswordCell.TextField.ResignFirstResponder();
|
MasterPasswordCell.TextField.ResignFirstResponder();
|
||||||
Success();
|
Success();
|
||||||
|
@ -325,7 +344,15 @@ namespace Bit.iOS.Core.Controllers
|
||||||
if (indexPath.Row == 0)
|
if (indexPath.Row == 0)
|
||||||
{
|
{
|
||||||
var cell = new ExtendedUITableViewCell();
|
var cell = new ExtendedUITableViewCell();
|
||||||
if (_controller._biometricIntegrityValid)
|
if (_controller._passwordReprompt)
|
||||||
|
{
|
||||||
|
cell.TextLabel.TextColor = ThemeHelpers.DangerColor;
|
||||||
|
cell.TextLabel.Font = ThemeHelpers.GetDangerFont();
|
||||||
|
cell.TextLabel.Lines = 0;
|
||||||
|
cell.TextLabel.LineBreakMode = UILineBreakMode.WordWrap;
|
||||||
|
cell.TextLabel.Text = AppResources.PasswordConfirmationDesc;
|
||||||
|
}
|
||||||
|
else if (_controller._biometricIntegrityValid)
|
||||||
{
|
{
|
||||||
var biometricButtonText = _controller._deviceActionService.SupportsFaceBiometric() ?
|
var biometricButtonText = _controller._deviceActionService.SupportsFaceBiometric() ?
|
||||||
AppResources.UseFaceIDToUnlock : AppResources.UseFingerprintToUnlock;
|
AppResources.UseFaceIDToUnlock : AppResources.UseFingerprintToUnlock;
|
||||||
|
@ -353,7 +380,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
|
|
||||||
public override nint NumberOfSections(UITableView tableView)
|
public override nint NumberOfSections(UITableView tableView)
|
||||||
{
|
{
|
||||||
return _controller._biometricLock ? 2 : 1;
|
return _controller._biometricLock || _controller._passwordReprompt ? 2 : 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
public override nint RowsInSection(UITableView tableview, nint section)
|
public override nint RowsInSection(UITableView tableview, nint section)
|
||||||
|
|
Loading…
Reference in a new issue