mirror of
https://github.com/bitwarden/mobile.git
synced 2024-12-31 17:47:43 +01:00
EC-312 Fix crash on entering invalid credentials five times on Autofill (#1988)
This commit is contained in:
parent
2d2a883b96
commit
d2fbf5bdea
@ -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;
|
||||||
@ -36,88 +38,128 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
public override void ViewDidLoad()
|
public override void ViewDidLoad()
|
||||||
{
|
{
|
||||||
InitApp();
|
try
|
||||||
base.ViewDidLoad();
|
|
||||||
Logo.Image = new UIImage(ThemeHelpers.LightTheme ? "logo.png" : "logo_white.png");
|
|
||||||
View.BackgroundColor = ThemeHelpers.SplashBackgroundColor;
|
|
||||||
_context = new Context
|
|
||||||
{
|
{
|
||||||
ExtContext = ExtensionContext
|
InitApp();
|
||||||
};
|
base.ViewDidLoad();
|
||||||
|
Logo.Image = new UIImage(ThemeHelpers.LightTheme ? "logo.png" : "logo_white.png");
|
||||||
|
View.BackgroundColor = ThemeHelpers.SplashBackgroundColor;
|
||||||
|
_context = new Context
|
||||||
|
{
|
||||||
|
ExtContext = ExtensionContext
|
||||||
|
};
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
|
public override async void PrepareCredentialList(ASCredentialServiceIdentifier[] serviceIdentifiers)
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
try
|
||||||
_context.ServiceIdentifiers = serviceIdentifiers;
|
|
||||||
if (serviceIdentifiers.Length > 0)
|
|
||||||
{
|
{
|
||||||
var uri = serviceIdentifiers[0].Identifier;
|
InitAppIfNeeded();
|
||||||
if (serviceIdentifiers[0].Type == ASCredentialServiceIdentifierType.Domain)
|
_context.ServiceIdentifiers = serviceIdentifiers;
|
||||||
|
if (serviceIdentifiers.Length > 0)
|
||||||
{
|
{
|
||||||
uri = string.Concat("https://", uri);
|
var uri = serviceIdentifiers[0].Identifier;
|
||||||
|
if (serviceIdentifiers[0].Type == ASCredentialServiceIdentifierType.Domain)
|
||||||
|
{
|
||||||
|
uri = string.Concat("https://", uri);
|
||||||
|
}
|
||||||
|
_context.UrlString = uri;
|
||||||
}
|
}
|
||||||
_context.UrlString = uri;
|
if (!await IsAuthed())
|
||||||
}
|
|
||||||
if (!await IsAuthed())
|
|
||||||
{
|
|
||||||
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
|
||||||
}
|
|
||||||
else if (await IsLocked())
|
|
||||||
{
|
|
||||||
PerformSegue("lockPasswordSegue", this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
|
|
||||||
{
|
{
|
||||||
PerformSegue("loginSearchSegue", this);
|
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
||||||
|
}
|
||||||
|
else if (await IsLocked())
|
||||||
|
{
|
||||||
|
PerformSegue("lockPasswordSegue", this);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
PerformSegue("loginListSegue", this);
|
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
|
||||||
|
{
|
||||||
|
PerformSegue("loginSearchSegue", this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PerformSegue("loginListSegue", this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void ProvideCredentialWithoutUserInteraction(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
try
|
||||||
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
|
||||||
await _stateService.Value.SetPasswordVerifiedAutofillAsync(false);
|
|
||||||
if (!await IsAuthed() || await IsLocked())
|
|
||||||
{
|
{
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
InitAppIfNeeded();
|
||||||
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
|
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
||||||
ExtensionContext.CancelRequest(err);
|
await _stateService.Value.SetPasswordVerifiedAutofillAsync(false);
|
||||||
return;
|
if (!await IsAuthed() || await IsLocked())
|
||||||
|
{
|
||||||
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
|
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
|
||||||
|
ExtensionContext.CancelRequest(err);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_context.CredentialIdentity = credentialIdentity;
|
||||||
|
await ProvideCredentialAsync(false);
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
_context.CredentialIdentity = credentialIdentity;
|
|
||||||
await ProvideCredentialAsync(false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
public override async void PrepareInterfaceToProvideCredential(ASPasswordCredentialIdentity credentialIdentity)
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
try
|
||||||
if (!await IsAuthed())
|
|
||||||
{
|
{
|
||||||
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
InitAppIfNeeded();
|
||||||
return;
|
if (!await IsAuthed())
|
||||||
|
{
|
||||||
|
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_context.CredentialIdentity = credentialIdentity;
|
||||||
|
await CheckLockAsync(async () => await ProvideCredentialAsync());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
_context.CredentialIdentity = credentialIdentity;
|
|
||||||
CheckLock(async () => await ProvideCredentialAsync());
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public override async void PrepareInterfaceForExtensionConfiguration()
|
public override async void PrepareInterfaceForExtensionConfiguration()
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
try
|
||||||
_context.Configuring = true;
|
|
||||||
if (!await IsAuthed())
|
|
||||||
{
|
{
|
||||||
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
InitAppIfNeeded();
|
||||||
return;
|
_context.Configuring = true;
|
||||||
|
if (!await IsAuthed())
|
||||||
|
{
|
||||||
|
await _accountsManager.NavigateOnAccountChangeAsync(false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
await CheckLockAsync(() => PerformSegue("setupSegue", this));
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
CheckLock(() => PerformSegue("setupSegue", this));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void CompleteRequest(string id = null, string username = null,
|
public void CompleteRequest(string id = null, string username = null,
|
||||||
@ -159,34 +201,43 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
|
public override void PrepareForSegue(UIStoryboardSegue segue, NSObject sender)
|
||||||
{
|
{
|
||||||
if (segue.DestinationViewController is UINavigationController navController)
|
try
|
||||||
{
|
{
|
||||||
if (navController.TopViewController is LoginListViewController listLoginController)
|
if (segue.DestinationViewController is UINavigationController navController)
|
||||||
{
|
{
|
||||||
listLoginController.Context = _context;
|
if (navController.TopViewController is LoginListViewController listLoginController)
|
||||||
listLoginController.CPViewController = this;
|
{
|
||||||
segue.DestinationViewController.PresentationController.Delegate =
|
listLoginController.Context = _context;
|
||||||
new CustomPresentationControllerDelegate(listLoginController.DismissModalAction);
|
listLoginController.CPViewController = this;
|
||||||
}
|
segue.DestinationViewController.PresentationController.Delegate =
|
||||||
else if (navController.TopViewController is LoginSearchViewController listSearchController)
|
new CustomPresentationControllerDelegate(listLoginController.DismissModalAction);
|
||||||
{
|
}
|
||||||
listSearchController.Context = _context;
|
else if (navController.TopViewController is LoginSearchViewController listSearchController)
|
||||||
listSearchController.CPViewController = this;
|
{
|
||||||
segue.DestinationViewController.PresentationController.Delegate =
|
listSearchController.Context = _context;
|
||||||
new CustomPresentationControllerDelegate(listSearchController.DismissModalAction);
|
listSearchController.CPViewController = this;
|
||||||
}
|
segue.DestinationViewController.PresentationController.Delegate =
|
||||||
else if (navController.TopViewController is LockPasswordViewController passwordViewController)
|
new CustomPresentationControllerDelegate(listSearchController.DismissModalAction);
|
||||||
{
|
}
|
||||||
passwordViewController.CPViewController = this;
|
else if (navController.TopViewController is LockPasswordViewController passwordViewController)
|
||||||
segue.DestinationViewController.PresentationController.Delegate =
|
{
|
||||||
new CustomPresentationControllerDelegate(passwordViewController.DismissModalAction);
|
passwordViewController.CPViewController = this;
|
||||||
}
|
segue.DestinationViewController.PresentationController.Delegate =
|
||||||
else if (navController.TopViewController is SetupViewController setupViewController)
|
new CustomPresentationControllerDelegate(passwordViewController.DismissModalAction);
|
||||||
{
|
}
|
||||||
setupViewController.CPViewController = this;
|
else if (navController.TopViewController is SetupViewController setupViewController)
|
||||||
segue.DestinationViewController.PresentationController.Delegate =
|
{
|
||||||
new CustomPresentationControllerDelegate(setupViewController.DismissModalAction);
|
setupViewController.CPViewController = this;
|
||||||
|
segue.DestinationViewController.PresentationController.Delegate =
|
||||||
|
new CustomPresentationControllerDelegate(setupViewController.DismissModalAction);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
|
throw;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -194,93 +245,109 @@ namespace Bit.iOS.Autofill
|
|||||||
{
|
{
|
||||||
DismissViewController(false, async () =>
|
DismissViewController(false, async () =>
|
||||||
{
|
{
|
||||||
if (_context.CredentialIdentity != null)
|
try
|
||||||
{
|
{
|
||||||
await ProvideCredentialAsync();
|
if (_context.CredentialIdentity != null)
|
||||||
return;
|
{
|
||||||
|
await ProvideCredentialAsync();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_context.Configuring)
|
||||||
|
{
|
||||||
|
PerformSegue("setupSegue", this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
|
||||||
|
{
|
||||||
|
PerformSegue("loginSearchSegue", this);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
PerformSegue("loginListSegue", this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if (_context.Configuring)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
PerformSegue("setupSegue", this);
|
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||||
return;
|
throw;
|
||||||
}
|
|
||||||
if (_context.ServiceIdentifiers == null || _context.ServiceIdentifiers.Length == 0)
|
|
||||||
{
|
|
||||||
PerformSegue("loginSearchSegue", this);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
PerformSegue("loginListSegue", this);
|
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
||||||
{
|
{
|
||||||
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
|
try
|
||||||
Bit.Core.Models.Domain.Cipher cipher = null;
|
|
||||||
var cancel = cipherService == null || _context.CredentialIdentity?.RecordIdentifier == null;
|
|
||||||
if (!cancel)
|
|
||||||
{
|
{
|
||||||
cipher = await cipherService.GetAsync(_context.CredentialIdentity.RecordIdentifier);
|
var cipherService = ServiceContainer.Resolve<ICipherService>("cipherService", true);
|
||||||
cancel = cipher == null || cipher.Type != Bit.Core.Enums.CipherType.Login || cipher.Login == null;
|
Bit.Core.Models.Domain.Cipher cipher = null;
|
||||||
}
|
var cancel = cipherService == null || _context.CredentialIdentity?.RecordIdentifier == null;
|
||||||
if (cancel)
|
if (!cancel)
|
||||||
{
|
{
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
cipher = await cipherService.GetAsync(_context.CredentialIdentity.RecordIdentifier);
|
||||||
Convert.ToInt32(ASExtensionErrorCode.CredentialIdentityNotFound), null);
|
cancel = cipher == null || cipher.Type != Bit.Core.Enums.CipherType.Login || cipher.Login == null;
|
||||||
ExtensionContext?.CancelRequest(err);
|
}
|
||||||
return;
|
if (cancel)
|
||||||
}
|
|
||||||
|
|
||||||
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 _stateService.Value.SetPasswordRepromptAutofillAsync(true);
|
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
|
Convert.ToInt32(ASExtensionErrorCode.CredentialIdentityNotFound), null);
|
||||||
ExtensionContext?.CancelRequest(err);
|
ExtensionContext?.CancelRequest(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
else if (!await _stateService.Value.GetPasswordVerifiedAutofillAsync())
|
|
||||||
|
var decCipher = await cipher.DecryptAsync();
|
||||||
|
if (decCipher.Reprompt != Bit.Core.Enums.CipherRepromptType.None)
|
||||||
{
|
{
|
||||||
// Add a timeout to resolve keyboard not always showing up.
|
// Prompt for password using either the lock screen or dialog unless
|
||||||
await Task.Delay(250);
|
// already verified the password.
|
||||||
var passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
if (!userInteraction)
|
||||||
if (!await passwordRepromptService.ShowPasswordPromptAsync())
|
|
||||||
{
|
{
|
||||||
|
await _stateService.Value.SetPasswordRepromptAutofillAsync(true);
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
|
Convert.ToInt32(ASExtensionErrorCode.UserInteractionRequired), null);
|
||||||
ExtensionContext?.CancelRequest(err);
|
ExtensionContext?.CancelRequest(err);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
else if (!await _stateService.Value.GetPasswordVerifiedAutofillAsync())
|
||||||
|
{
|
||||||
|
// Add a timeout to resolve keyboard not always showing up.
|
||||||
|
await Task.Delay(250);
|
||||||
|
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 _stateService.Value.GetDisableAutoTotpCopyAsync();
|
||||||
var disableTotpCopy = await _stateService.Value.GetDisableAutoTotpCopyAsync();
|
if (!disableTotpCopy.GetValueOrDefault(false))
|
||||||
if (!disableTotpCopy.GetValueOrDefault(false))
|
|
||||||
{
|
|
||||||
var canAccessPremiumAsync = await _stateService.Value.CanAccessPremiumAsync();
|
|
||||||
if (!string.IsNullOrWhiteSpace(decCipher.Login.Totp) &&
|
|
||||||
(canAccessPremiumAsync || cipher.OrganizationUseTotp))
|
|
||||||
{
|
{
|
||||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
var canAccessPremiumAsync = await _stateService.Value.CanAccessPremiumAsync();
|
||||||
totpCode = await totpService.GetCodeAsync(decCipher.Login.Totp);
|
if (!string.IsNullOrWhiteSpace(decCipher.Login.Totp) &&
|
||||||
|
(canAccessPremiumAsync || cipher.OrganizationUseTotp))
|
||||||
|
{
|
||||||
|
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
|
totpCode = await totpService.GetCodeAsync(decCipher.Login.Totp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
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
|
||||||
{
|
{
|
||||||
@ -303,15 +370,21 @@ namespace Bit.iOS.Autofill
|
|||||||
{
|
{
|
||||||
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
NSRunLoop.Main.BeginInvokeOnMainThread(async () =>
|
||||||
{
|
{
|
||||||
if (await IsAuthed())
|
try
|
||||||
{
|
{
|
||||||
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
if (await IsAuthed())
|
||||||
var deviceActionService = ServiceContainer.Resolve<IDeviceActionService>("deviceActionService");
|
|
||||||
if (deviceActionService.SystemMajorVersion() >= 12)
|
|
||||||
{
|
{
|
||||||
await ASCredentialIdentityStore.SharedStore?.RemoveAllCredentialIdentitiesAsync();
|
await AppHelpers.LogOutAsync(await _stateService.Value.GetActiveUserIdAsync());
|
||||||
|
if (UIDevice.CurrentDevice.CheckSystemVersion(12, 0))
|
||||||
|
{
|
||||||
|
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;
|
||||||
@ -84,7 +85,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
|
|
||||||
public abstract UITableView TableView { get; }
|
public abstract UITableView TableView { get; }
|
||||||
|
|
||||||
public override async void ViewDidLoad()
|
public override async void ViewDidLoad()
|
||||||
{
|
{
|
||||||
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
_vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
|
||||||
@ -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
|
||||||
@ -306,17 +302,22 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
await HandleFailedCredentialsAsync();
|
||||||
if (invalidUnlockAttempts >= 5)
|
|
||||||
{
|
|
||||||
await LogOutAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
InvalidValue();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task HandleFailedCredentialsAsync()
|
||||||
|
{
|
||||||
|
var invalidUnlockAttempts = await AppHelpers.IncrementInvalidUnlockAttemptsAsync();
|
||||||
|
if (invalidUnlockAttempts >= 5)
|
||||||
|
{
|
||||||
|
await _accountManager.LogOutAsync(await _stateService.GetActiveUserIdAsync(), false, false);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
InvalidValue();
|
||||||
|
}
|
||||||
|
|
||||||
public async Task PromptBiometricAsync()
|
public async Task PromptBiometricAsync()
|
||||||
{
|
{
|
||||||
if (!_biometricLock || !_biometricIntegrityValid)
|
if (!_biometricLock || !_biometricIntegrityValid)
|
||||||
@ -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 New Issue
Block a user