mirror of
https://github.com/bitwarden/android.git
synced 2024-12-25 18:38:27 +03:00
EC-312 Fix crash on entering invalid credentials five times on Autofill (#1988)
This commit is contained in:
parent
2d2a883b96
commit
d2fbf5bdea
6 changed files with 236 additions and 170 deletions
|
@ -8,5 +8,6 @@ namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
|
void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost);
|
||||||
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
|
Task NavigateOnAccountChangeAsync(bool? isAuthed = null);
|
||||||
|
Task LogOutAsync(string userId, bool userInitiated, bool expired);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -301,7 +301,7 @@ namespace Bit.App
|
||||||
UpdateThemeAsync();
|
UpdateThemeAsync();
|
||||||
};
|
};
|
||||||
Current.MainPage = new NavigationPage(new HomePage(Options));
|
Current.MainPage = new NavigationPage(new HomePage(Options));
|
||||||
var mainPageTask = _accountsManager.NavigateOnAccountChangeAsync();
|
_accountsManager.NavigateOnAccountChangeAsync().FireAndForget();
|
||||||
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
ServiceContainer.Resolve<MobilePlatformUtilsService>("platformUtilsService").Init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -123,7 +123,11 @@ namespace Bit.App.Utilities.AccountManagement
|
||||||
await _vaultTimeoutService.LockAsync(true);
|
await _vaultTimeoutService.LockAsync(true);
|
||||||
break;
|
break;
|
||||||
case AccountsManagerMessageCommands.LOGOUT:
|
case AccountsManagerMessageCommands.LOGOUT:
|
||||||
await Device.InvokeOnMainThreadAsync(() => LogOutAsync(message.Data as Tuple<string, bool, bool>));
|
var extras = message.Data as Tuple<string, bool, bool>;
|
||||||
|
var userId = extras?.Item1;
|
||||||
|
var userInitiated = extras?.Item2 ?? true;
|
||||||
|
var expired = extras?.Item3 ?? false;
|
||||||
|
await Device.InvokeOnMainThreadAsync(() => LogOutAsync(userId, userInitiated, expired));
|
||||||
break;
|
break;
|
||||||
case AccountsManagerMessageCommands.LOGGED_OUT:
|
case AccountsManagerMessageCommands.LOGGED_OUT:
|
||||||
// Clean up old migrated key if they ever log out.
|
// Clean up old migrated key if they ever log out.
|
||||||
|
@ -181,12 +185,8 @@ namespace Bit.App.Utilities.AccountManagement
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogOutAsync(Tuple<string, bool, bool> extras)
|
public async Task LogOutAsync(string userId, bool userInitiated, bool expired)
|
||||||
{
|
{
|
||||||
var userId = extras?.Item1;
|
|
||||||
var userInitiated = extras?.Item2 ?? true;
|
|
||||||
var expired = extras?.Item3 ?? false;
|
|
||||||
|
|
||||||
await AppHelpers.LogOutAsync(userId, userInitiated);
|
await AppHelpers.LogOutAsync(userId, userInitiated);
|
||||||
await NavigateOnAccountChangeAsync();
|
await NavigateOnAccountChangeAsync();
|
||||||
_authService.LogOut(() =>
|
_authService.LogOut(() =>
|
||||||
|
|
|
@ -6,6 +6,7 @@ using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
|
||||||
namespace Bit.Core.Services
|
namespace Bit.Core.Services
|
||||||
{
|
{
|
||||||
|
@ -173,7 +174,7 @@ namespace Bit.Core.Services
|
||||||
public void LogOut(Action callback)
|
public void LogOut(Action callback)
|
||||||
{
|
{
|
||||||
callback.Invoke();
|
callback.Invoke();
|
||||||
_messagingService.Send("loggedOut");
|
_messagingService.Send(AccountsManagerMessageCommands.LOGGED_OUT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<TwoFactorProvider> GetSupportedTwoFactorProviders()
|
public List<TwoFactorProvider> GetSupportedTwoFactorProviders()
|
||||||
|
|
|
@ -8,10 +8,12 @@ using Bit.App.Utilities;
|
||||||
using Bit.App.Utilities.AccountManagement;
|
using Bit.App.Utilities.AccountManagement;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Bit.iOS.Autofill.Models;
|
using Bit.iOS.Autofill.Models;
|
||||||
using Bit.iOS.Core.Utilities;
|
using Bit.iOS.Core.Utilities;
|
||||||
using Bit.iOS.Core.Views;
|
using Bit.iOS.Core.Views;
|
||||||
|
using CoreFoundation;
|
||||||
using CoreNFC;
|
using CoreNFC;
|
||||||
using Foundation;
|
using Foundation;
|
||||||
using UIKit;
|
using UIKit;
|
||||||
|
@ -35,6 +37,8 @@ namespace Bit.iOS.Autofill
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void ViewDidLoad()
|
public override void ViewDidLoad()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitApp();
|
InitApp();
|
||||||
base.ViewDidLoad();
|
base.ViewDidLoad();
|
||||||
|
@ -45,8 +49,16 @@ namespace Bit.iOS.Autofill
|
||||||
ExtContext = ExtensionContext
|
ExtContext = ExtensionContext
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
|
public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
_context.ServiceIdentifiers = serviceIdentifiers;
|
_context.ServiceIdentifiers = serviceIdentifiers;
|
||||||
|
@ -79,8 +91,16 @@ namespace Bit.iOS.Autofill
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
||||||
|
@ -95,8 +115,16 @@ namespace Bit.iOS.Autofill
|
||||||
_context.CredentialIdentity = credentialIdentity;
|
_context.CredentialIdentity = credentialIdentity;
|
||||||
await ProvideCredentialAsync(false);
|
await ProvideCredentialAsync(false);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
if (!await IsAuthed())
|
if (!await IsAuthed())
|
||||||
|
@ -105,10 +133,18 @@ namespace Bit.iOS.Autofill
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
_context.CredentialIdentity = credentialIdentity;
|
_context.CredentialIdentity = credentialIdentity;
|
||||||
CheckLock(async () => await ProvideCredentialAsync());
|
await CheckLockAsync(async () => await ProvideCredentialAsync());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void PrepareInterfaceForExtensionConfiguration()
|
public override async void PrepareInterfaceForExtensionConfiguration()
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
_context.Configuring = true;
|
_context.Configuring = true;
|
||||||
|
@ -117,7 +153,13 @@ namespace Bit.iOS.Autofill
|
||||||
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
CheckLock(() => PerformSegue("setupSegue", this));
|
await CheckLockAsync(() => PerformSegue("setupSegue", this));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CompleteRequest(string id = null, string username = null,
|
public void CompleteRequest(string id = null, string username = null,
|
||||||
|
@ -158,6 +200,8 @@ namespace Bit.iOS.Autofill
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
|
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (segue.DestinationViewController is UINavigationController navController)
|
if (segue.DestinationViewController is UINavigationController navController)
|
||||||
{
|
{
|
||||||
|
@ -188,11 +232,20 @@ namespace Bit.iOS.Autofill
|
||||||
new CustomPresentationControllerDelegate(setupViewController.DismissModalAction);
|
new CustomPresentationControllerDelegate(setupViewController.DismissModalAction);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void DismissLockAndContinue()
|
public void DismissLockAndContinue()
|
||||||
{
|
{
|
||||||
DismissViewController(false, async () =>
|
DismissViewController(false, async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (_context.CredentialIdentity != null)
|
if (_context.CredentialIdentity != null)
|
||||||
{
|
{
|
||||||
|
@ -212,10 +265,18 @@ namespace Bit.iOS.Autofill
|
||||||
{
|
{
|
||||||
PerformSegue("loginListSegue", this);
|
PerformSegue("loginListSegue", this);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
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;
|
||||||
|
@ -275,12 +336,18 @@ namespace Bit.iOS.Autofill
|
||||||
|
|
||||||
CompleteRequest(decCipher.Id, decCipher.Login.Username, decCipher.Login.Password, totpCode);
|
CompleteRequest(decCipher.Id, decCipher.Login.Username, decCipher.Login.Password, totpCode);
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private async void CheckLock(Action notLockedAction)
|
private async Task CheckLockAsync(Action notLockedAction)
|
||||||
{
|
{
|
||||||
if (await IsLocked() || await _stateService.Value.GetPasswordRepromptAutofillAsync())
|
if (await IsLocked() || await _stateService.Value.GetPasswordRepromptAutofillAsync())
|
||||||
{
|
{
|
||||||
PerformSegue("lockPasswordSegue", this);
|
DispatchQueue.MainQueue.DispatchAsync(() => PerformSegue("lockPasswordSegue", this));
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
@ -302,16 +369,22 @@ namespace Bit.iOS.Autofill
|
||||||
private void LogoutIfAuthed()
|
private void LogoutIfAuthed()
|
||||||
{
|
{
|
||||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||||
|
{
|
||||||
|
try
|
||||||
{
|
{
|
||||||
if (await IsAuthed())
|
if (await IsAuthed())
|
||||||
{
|
{
|
||||||
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
||||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||||
if (deviceActionService.SystemMajorVersion() >= 12)
|
|
||||||
{
|
{
|
||||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,6 +28,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
private IPlatformUtilsService _platformUtilsService;
|
private IPlatformUtilsService _platformUtilsService;
|
||||||
private IBiometricService _biometricService;
|
private IBiometricService _biometricService;
|
||||||
private IKeyConnectorService _keyConnectorService;
|
private IKeyConnectorService _keyConnectorService;
|
||||||
|
private IAccountsManager _accountManager;
|
||||||
private bool _isPinProtected;
|
private bool _isPinProtected;
|
||||||
private bool _isPinProtectedWithKey;
|
private bool _isPinProtectedWithKey;
|
||||||
private bool _pinLock;
|
private bool _pinLock;
|
||||||
|
@ -95,6 +96,7 @@ 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");
|
||||||
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
_keyConnectorService = ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService");
|
||||||
|
_accountManager = ServiceContainer.Resolve<IAccountsManager>("accountsManager");
|
||||||
|
|
||||||
// We re-use the lock screen for autofill extension to verify master password
|
// We re-use the lock screen for autofill extension to verify master password
|
||||||
// when trying to access protected items.
|
// when trying to access protected items.
|
||||||
|
@ -265,13 +267,7 @@ namespace Bit.iOS.Core.Controllers
|
||||||
}
|
}
|
||||||
if (failed)
|
if (failed)
|
||||||
{
|
{
|
||||||
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
await HandleFailedCredentialsAsync();
|
||||||
if (invalidUnlockAttempts >= 5)
|
|
||||||
{
|
|
||||||
await LogOutAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InvalidValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
@ -305,17 +301,22 @@ namespace Bit.iOS.Core.Controllers
|
||||||
await SetKeyAndContinueAsync(key2, true);
|
await SetKeyAndContinueAsync(key2, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
|
{
|
||||||
|
await HandleFailedCredentialsAsync();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task HandleFailedCredentialsAsync()
|
||||||
{
|
{
|
||||||
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
if (invalidUnlockAttempts >= 5)
|
if (invalidUnlockAttempts >= 5)
|
||||||
{
|
{
|
||||||
await LogOutAsync();
|
await _accountManager.LogOutAsync(await _stateService.GetActiveUserIdAsync(), false, false);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
InvalidValue();
|
InvalidValue();
|
||||||
}
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
|
@ -395,16 +396,6 @@ namespace Bit.iOS.Core.Controllers
|
||||||
PresentViewController(alert, true, null);
|
PresentViewController(alert, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task LogOutAsync()
|
|
||||||
{
|
|
||||||
await AppHelpers.LogOutAsync(await _stateService.GetActiveUserIdAsync());
|
|
||||||
var authService = ServiceContainer.Resolve<IAuthService>("authService");
|
|
||||||
authService.LogOut(() =>
|
|
||||||
{
|
|
||||||
Cancel?.Invoke();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
{
|
{
|
||||||
base.Dispose(disposing);
|
base.Dispose(disposing);
|
||||||
|
|
Loading…
Reference in a new issue