mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 07:43:37 +00:00
SG-210 Account Switching in Autofill (iOS) (#1909)
* SG-210 Set up account switching on Autofill iOS * SG-210 Fix refresh after sync on autofill ciphers, also added account switching on lock view on autofill. Also fix possible crash when scrolling when no items were displayed and also fixed navigation when login in on an automatically logged out account. * SG-210 Added reference on iOS.Core project * Fix formatting on AccountManager * SG-210 Fix background color for dark theme
This commit is contained in:
committed by
GitHub
parent
88b406544b
commit
fba407f3b6
227
src/App/Utilities/AccountManagement/AccountsManager.cs
Normal file
227
src/App/Utilities/AccountManagement/AccountsManager.cs
Normal file
@@ -0,0 +1,227 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Models;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Xamarin.Forms;
|
||||
|
||||
namespace Bit.App.Utilities.AccountManagement
|
||||
{
|
||||
public static class AccountsManagerMessageCommands
|
||||
{
|
||||
public const string LOCKED = "locked";
|
||||
public const string LOCK_VAULT = "lockVault";
|
||||
public const string LOGOUT = "logout";
|
||||
public const string LOGGED_OUT = "loggedOut";
|
||||
public const string ADD_ACCOUNT = "addAccount";
|
||||
public const string ACCOUNT_ADDED = "accountAdded";
|
||||
public const string SWITCHED_ACCOUNT = "switchedAccount";
|
||||
}
|
||||
|
||||
public class AccountsManager : IAccountsManager
|
||||
{
|
||||
private readonly IBroadcasterService _broadcasterService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly IStorageService _secureStorageService;
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IPlatformUtilsService _platformUtilsService;
|
||||
private readonly IAuthService _authService;
|
||||
|
||||
Func<AppOptions> _getOptionsFunc;
|
||||
private IAccountsManagerHost _accountsManagerHost;
|
||||
|
||||
public AccountsManager(IBroadcasterService broadcasterService,
|
||||
IVaultTimeoutService vaultTimeoutService,
|
||||
IStorageService secureStorageService,
|
||||
IStateService stateService,
|
||||
IPlatformUtilsService platformUtilsService,
|
||||
IAuthService authService)
|
||||
{
|
||||
_broadcasterService = broadcasterService;
|
||||
_vaultTimeoutService = vaultTimeoutService;
|
||||
_secureStorageService = secureStorageService;
|
||||
_stateService = stateService;
|
||||
_platformUtilsService = platformUtilsService;
|
||||
_authService = authService;
|
||||
}
|
||||
|
||||
private AppOptions Options => _getOptionsFunc?.Invoke() ?? new AppOptions { IosExtension = true };
|
||||
|
||||
public void Init(Func<AppOptions> getOptionsFunc, IAccountsManagerHost accountsManagerHost)
|
||||
{
|
||||
_getOptionsFunc = getOptionsFunc;
|
||||
_accountsManagerHost = accountsManagerHost;
|
||||
|
||||
_broadcasterService.Subscribe(nameof(AccountsManager), OnMessage);
|
||||
}
|
||||
|
||||
public async Task NavigateOnAccountChangeAsync(bool? isAuthed = null)
|
||||
{
|
||||
// TODO: this could be improved by doing chain of responsability pattern
|
||||
// but for now it may be an overkill, if logic gets more complex consider refactoring it
|
||||
|
||||
var authed = isAuthed ?? await _stateService.IsAuthenticatedAsync();
|
||||
if (authed)
|
||||
{
|
||||
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
||||
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
||||
{
|
||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||
_accountsManagerHost.Navigate(NavigationTarget.Login, new LoginNavigationParams(email));
|
||||
}
|
||||
else if (await _vaultTimeoutService.IsLockedAsync() ||
|
||||
await _vaultTimeoutService.ShouldLockAsync())
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.Lock);
|
||||
}
|
||||
else if (Options.FromAutofillFramework && Options.SaveType.HasValue)
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.AddEditCipher);
|
||||
}
|
||||
else if (Options.Uri != null)
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.AutofillCiphers);
|
||||
}
|
||||
else if (Options.CreateSend != null)
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.SendAddEdit);
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.Home);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Options.HideAccountSwitcher = await _stateService.GetActiveUserIdAsync() == null;
|
||||
if (await _vaultTimeoutService.IsLoggedOutByTimeoutAsync() ||
|
||||
await _vaultTimeoutService.ShouldLogOutByTimeoutAsync())
|
||||
{
|
||||
// TODO implement orgIdentifier flow to SSO Login page, same as email flow below
|
||||
// var orgIdentifier = await _stateService.GetOrgIdentifierAsync();
|
||||
|
||||
var email = await _stateService.GetEmailAsync();
|
||||
_accountsManagerHost.Navigate(NavigationTarget.Login, new LoginNavigationParams(email));
|
||||
}
|
||||
else
|
||||
{
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void OnMessage(Message message)
|
||||
{
|
||||
switch (message.Command)
|
||||
{
|
||||
case AccountsManagerMessageCommands.LOCKED:
|
||||
Locked(message.Data as Tuple<string, bool>);
|
||||
break;
|
||||
case AccountsManagerMessageCommands.LOCK_VAULT:
|
||||
await _vaultTimeoutService.LockAsync(true);
|
||||
break;
|
||||
case AccountsManagerMessageCommands.LOGOUT:
|
||||
LogOut(message.Data as Tuple<string, bool, bool>);
|
||||
break;
|
||||
case AccountsManagerMessageCommands.LOGGED_OUT:
|
||||
// Clean up old migrated key if they ever log out.
|
||||
await _secureStorageService.RemoveAsync("oldKey");
|
||||
break;
|
||||
case AccountsManagerMessageCommands.ADD_ACCOUNT:
|
||||
AddAccount();
|
||||
break;
|
||||
case AccountsManagerMessageCommands.ACCOUNT_ADDED:
|
||||
await _accountsManagerHost.UpdateThemeAsync();
|
||||
break;
|
||||
case AccountsManagerMessageCommands.SWITCHED_ACCOUNT:
|
||||
await SwitchedAccountAsync();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void Locked(Tuple<string, bool> extras)
|
||||
{
|
||||
var userId = extras?.Item1;
|
||||
var userInitiated = extras?.Item2 ?? false;
|
||||
Device.BeginInvokeOnMainThread(async () => await LockedAsync(userId, userInitiated));
|
||||
}
|
||||
|
||||
private async Task LockedAsync(string userId, bool userInitiated)
|
||||
{
|
||||
if (!await _stateService.IsActiveAccountAsync(userId))
|
||||
{
|
||||
_platformUtilsService.ShowToast("info", null, AppResources.AccountLockedSuccessfully);
|
||||
return;
|
||||
}
|
||||
|
||||
var autoPromptBiometric = !userInitiated;
|
||||
if (autoPromptBiometric && Device.RuntimePlatform == Device.iOS)
|
||||
{
|
||||
var vaultTimeout = await _stateService.GetVaultTimeoutAsync();
|
||||
if (vaultTimeout == 0)
|
||||
{
|
||||
autoPromptBiometric = false;
|
||||
}
|
||||
}
|
||||
|
||||
await _accountsManagerHost.SetPreviousPageInfoAsync();
|
||||
|
||||
Device.BeginInvokeOnMainThread(() => _accountsManagerHost.Navigate(NavigationTarget.Lock, new LockNavigationParams(autoPromptBiometric)));
|
||||
}
|
||||
|
||||
private void AddAccount()
|
||||
{
|
||||
Device.BeginInvokeOnMainThread(() =>
|
||||
{
|
||||
Options.HideAccountSwitcher = false;
|
||||
_accountsManagerHost.Navigate(NavigationTarget.HomeLogin);
|
||||
});
|
||||
}
|
||||
|
||||
private void LogOut(Tuple<string, bool, bool> extras)
|
||||
{
|
||||
var userId = extras?.Item1;
|
||||
var userInitiated = extras?.Item2 ?? true;
|
||||
var expired = extras?.Item3 ?? false;
|
||||
Device.BeginInvokeOnMainThread(async () => await LogOutAsync(userId, userInitiated, expired));
|
||||
}
|
||||
|
||||
private async Task LogOutAsync(string userId, bool userInitiated, bool expired)
|
||||
{
|
||||
await AppHelpers.LogOutAsync(userId, userInitiated);
|
||||
await NavigateOnAccountChangeAsync();
|
||||
_authService.LogOut(() =>
|
||||
{
|
||||
if (expired)
|
||||
{
|
||||
_platformUtilsService.ShowToast("warning", null, AppResources.LoginExpired);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private async Task SwitchedAccountAsync()
|
||||
{
|
||||
await AppHelpers.OnAccountSwitchAsync();
|
||||
Device.BeginInvokeOnMainThread(async () =>
|
||||
{
|
||||
if (await _vaultTimeoutService.ShouldTimeoutAsync())
|
||||
{
|
||||
await _vaultTimeoutService.ExecuteTimeoutActionAsync();
|
||||
}
|
||||
else
|
||||
{
|
||||
await NavigateOnAccountChangeAsync();
|
||||
}
|
||||
await Task.Delay(50);
|
||||
await _accountsManagerHost.UpdateThemeAsync();
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user