mirror of
https://github.com/bitwarden/mobile.git
synced 2024-11-21 11:25:56 +01:00
support for per-user biometric state tracking (#1820)
This commit is contained in:
parent
2076c11cbd
commit
34d0ecf64b
@ -374,7 +374,7 @@ namespace Bit.App.Pages
|
||||
page.MasterPasswordEntry.Focus();
|
||||
}
|
||||
});
|
||||
_stateService.BiometricLocked = !success;
|
||||
await _stateService.SetBiometricLockedAsync(!success);
|
||||
if (success)
|
||||
{
|
||||
await DoContinueAsync();
|
||||
@ -393,7 +393,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private async Task DoContinueAsync()
|
||||
{
|
||||
_stateService.BiometricLocked = false;
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
_messagingService.Send("unlocked");
|
||||
UnlockedAction?.Invoke();
|
||||
}
|
||||
|
@ -393,7 +393,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _stateService.SetBiometricUnlockAsync(null);
|
||||
}
|
||||
_stateService.BiometricLocked = false;
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
await _cryptoService.ToggleKeyAsync();
|
||||
BuildList();
|
||||
}
|
||||
|
@ -476,7 +476,6 @@ namespace Bit.App.Utilities
|
||||
policyService.ClearAsync(userId),
|
||||
stateService.LogoutAccountAsync(userId, userInitiated));
|
||||
|
||||
stateService.BiometricLocked = true;
|
||||
searchService.ClearIndex();
|
||||
|
||||
// check if we switched accounts automatically
|
||||
|
@ -10,7 +10,6 @@ namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IStateService
|
||||
{
|
||||
bool BiometricLocked { get; set; }
|
||||
List<AccountView> AccountViews { get; }
|
||||
Task<string> GetActiveUserIdAsync();
|
||||
Task SetActiveUserAsync(string userId);
|
||||
@ -24,6 +23,8 @@ namespace Bit.Core.Abstractions
|
||||
Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null);
|
||||
Task<bool?> GetBiometricUnlockAsync(string userId = null);
|
||||
Task SetBiometricUnlockAsync(bool? value, string userId = null);
|
||||
Task<bool> GetBiometricLockedAsync(string userId = null);
|
||||
Task SetBiometricLockedAsync(bool value, string userId = null);
|
||||
Task<bool> CanAccessPremiumAsync(string userId = null);
|
||||
Task<string> GetProtectedPinAsync(string userId = null);
|
||||
Task SetProtectedPinAsync(string value, string userId = null);
|
||||
|
@ -8,7 +8,7 @@ namespace Bit.Core.Models.Domain
|
||||
public AccountProfile Profile;
|
||||
public AccountTokens Tokens;
|
||||
public AccountSettings Settings;
|
||||
public AccountKeys Keys;
|
||||
public AccountVolatileData VolatileData;
|
||||
|
||||
public Account() { }
|
||||
|
||||
@ -17,12 +17,12 @@ namespace Bit.Core.Models.Domain
|
||||
Profile = profile;
|
||||
Tokens = tokens;
|
||||
Settings = new AccountSettings();
|
||||
Keys = new AccountKeys();
|
||||
VolatileData = new AccountVolatileData();
|
||||
}
|
||||
|
||||
public Account(Account account)
|
||||
{
|
||||
// Copy constructor excludes Keys (for storage)
|
||||
// Copy constructor excludes VolatileData (for storage)
|
||||
Profile = new AccountProfile(account.Profile);
|
||||
Tokens = new AccountTokens(account.Tokens);
|
||||
Settings = new AccountSettings(account.Settings);
|
||||
@ -101,10 +101,11 @@ namespace Bit.Core.Models.Domain
|
||||
public VaultTimeoutAction? VaultTimeoutAction;
|
||||
}
|
||||
|
||||
public class AccountKeys
|
||||
public class AccountVolatileData
|
||||
{
|
||||
public SymmetricCryptoKey Key;
|
||||
public EncString PinProtectedKey;
|
||||
public bool? BiometricLocked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -445,7 +445,7 @@ namespace Bit.Core.Services
|
||||
|
||||
}
|
||||
|
||||
_stateService.BiometricLocked = false;
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
_messagingService.Send("loggedIn");
|
||||
return result;
|
||||
}
|
||||
|
@ -21,8 +21,6 @@ namespace Bit.Core.Services
|
||||
private State _state;
|
||||
private bool _migrationChecked;
|
||||
|
||||
public bool BiometricLocked { get; set; } = true;
|
||||
|
||||
public List<AccountView> AccountViews { get; set; }
|
||||
|
||||
public StateService(IStorageService storageService, IStorageService secureStorageService)
|
||||
@ -204,6 +202,22 @@ namespace Bit.Core.Services
|
||||
var key = Constants.BiometricUnlockKey(reconciledOptions.UserId);
|
||||
await SetValueAsync(key, value, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<bool> GetBiometricLockedAsync(string userId = null)
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.VolatileData?.BiometricLocked ?? true;
|
||||
}
|
||||
|
||||
public async Task SetBiometricLockedAsync(bool value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.VolatileData.BiometricLocked = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||
{
|
||||
@ -264,7 +278,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.Keys?.PinProtectedKey;
|
||||
))?.VolatileData?.PinProtectedKey;
|
||||
}
|
||||
|
||||
public async Task SetPinProtectedKeyAsync(EncString value, string userId = null)
|
||||
@ -272,7 +286,7 @@ namespace Bit.Core.Services
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.Keys.PinProtectedKey = value;
|
||||
account.VolatileData.PinProtectedKey = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
@ -328,7 +342,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
return (await GetAccountAsync(
|
||||
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
|
||||
))?.Keys?.Key;
|
||||
))?.VolatileData?.Key;
|
||||
}
|
||||
|
||||
public async Task SetKeyDecryptedAsync(SymmetricCryptoKey value, string userId = null)
|
||||
@ -336,7 +350,7 @@ namespace Bit.Core.Services
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
var account = await GetAccountAsync(reconciledOptions);
|
||||
account.Keys.Key = value;
|
||||
account.VolatileData.Key = value;
|
||||
await SaveAccountAsync(account, reconciledOptions);
|
||||
}
|
||||
|
||||
@ -1207,9 +1221,9 @@ namespace Bit.Core.Services
|
||||
// Memory
|
||||
if (_state?.Accounts?.ContainsKey(options.UserId) ?? false)
|
||||
{
|
||||
if (_state.Accounts[options.UserId].Keys == null)
|
||||
if (_state.Accounts[options.UserId].VolatileData == null)
|
||||
{
|
||||
_state.Accounts[options.UserId].Keys = new Account.AccountKeys();
|
||||
_state.Accounts[options.UserId].VolatileData = new Account.AccountVolatileData();
|
||||
}
|
||||
return _state.Accounts[options.UserId];
|
||||
}
|
||||
@ -1218,9 +1232,9 @@ namespace Bit.Core.Services
|
||||
_state = await GetStateFromStorageAsync();
|
||||
if (_state?.Accounts?.ContainsKey(options.UserId) ?? false)
|
||||
{
|
||||
if (_state.Accounts[options.UserId].Keys == null)
|
||||
if (_state.Accounts[options.UserId].VolatileData == null)
|
||||
{
|
||||
_state.Accounts[options.UserId].Keys = new Account.AccountKeys();
|
||||
_state.Accounts[options.UserId].VolatileData = new Account.AccountVolatileData();
|
||||
}
|
||||
return _state.Accounts[options.UserId];
|
||||
}
|
||||
@ -1290,7 +1304,8 @@ namespace Bit.Core.Services
|
||||
{
|
||||
_state.Accounts[userId].Tokens.AccessToken = null;
|
||||
_state.Accounts[userId].Tokens.RefreshToken = null;
|
||||
_state.Accounts[userId].Keys.Key = null;
|
||||
_state.Accounts[userId].VolatileData.Key = null;
|
||||
_state.Accounts[userId].VolatileData.BiometricLocked = null;
|
||||
}
|
||||
}
|
||||
if (userInitiated && _state?.ActiveUserId == userId)
|
||||
|
@ -60,7 +60,7 @@ namespace Bit.Core.Services
|
||||
if (hasKey)
|
||||
{
|
||||
var biometricSet = await IsBiometricLockSetAsync(userId);
|
||||
if (biometricSet && _stateService.BiometricLocked)
|
||||
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
@ -158,8 +158,9 @@ namespace Bit.Core.Services
|
||||
|
||||
if (allowSoftLock)
|
||||
{
|
||||
_stateService.BiometricLocked = await IsBiometricLockSetAsync();
|
||||
if (_stateService.BiometricLocked)
|
||||
var isBiometricLockSet = await IsBiometricLockSetAsync(userId);
|
||||
await _stateService.SetBiometricLockedAsync(isBiometricLockSet, userId);
|
||||
if (isBiometricLockSet)
|
||||
{
|
||||
_messagingService.Send("locked", userInitiated);
|
||||
_lockedCallback?.Invoke(userInitiated);
|
||||
|
@ -314,7 +314,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
var success = await _platformUtilsService.AuthenticateBiometricAsync(null,
|
||||
_pinLock ? AppResources.PIN : AppResources.MasterPassword,
|
||||
() => MasterPasswordCell.TextField.BecomeFirstResponder());
|
||||
_stateService.BiometricLocked = !success;
|
||||
await _stateService.SetBiometricLockedAsync(!success);
|
||||
if (success)
|
||||
{
|
||||
DoContinue();
|
||||
@ -356,7 +356,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
await _stateService.SetPasswordVerifiedAutofillAsync(true);
|
||||
}
|
||||
await EnableBiometricsIfNeeded();
|
||||
_stateService.BiometricLocked = false;
|
||||
await _stateService.SetBiometricLockedAsync(false);
|
||||
MasterPasswordCell.TextField.ResignFirstResponder();
|
||||
Success();
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user