diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 05607ccbb2..59ca8c69dc 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -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(); } diff --git a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs index b4f689af91..86ab1a95ea 100644 --- a/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs +++ b/src/App/Pages/Settings/SettingsPage/SettingsPageViewModel.cs @@ -393,7 +393,7 @@ namespace Bit.App.Pages { await _stateService.SetBiometricUnlockAsync(null); } - _stateService.BiometricLocked = false; + await _stateService.SetBiometricLockedAsync(false); await _cryptoService.ToggleKeyAsync(); BuildList(); } diff --git a/src/App/Utilities/AppHelpers.cs b/src/App/Utilities/AppHelpers.cs index 8dc186a30f..c11c3f2c2e 100644 --- a/src/App/Utilities/AppHelpers.cs +++ b/src/App/Utilities/AppHelpers.cs @@ -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 diff --git a/src/Core/Abstractions/IStateService.cs b/src/Core/Abstractions/IStateService.cs index ca4ee67f07..64e7e47448 100644 --- a/src/Core/Abstractions/IStateService.cs +++ b/src/Core/Abstractions/IStateService.cs @@ -10,7 +10,6 @@ namespace Bit.Core.Abstractions { public interface IStateService { - bool BiometricLocked { get; set; } List AccountViews { get; } Task GetActiveUserIdAsync(); Task SetActiveUserAsync(string userId); @@ -24,6 +23,8 @@ namespace Bit.Core.Abstractions Task GetEnvironmentUrlsAsync(string userId = null); Task GetBiometricUnlockAsync(string userId = null); Task SetBiometricUnlockAsync(bool? value, string userId = null); + Task GetBiometricLockedAsync(string userId = null); + Task SetBiometricLockedAsync(bool value, string userId = null); Task CanAccessPremiumAsync(string userId = null); Task GetProtectedPinAsync(string userId = null); Task SetProtectedPinAsync(string value, string userId = null); diff --git a/src/Core/Models/Domain/Account.cs b/src/Core/Models/Domain/Account.cs index 43360c9cd3..cc65e92fee 100644 --- a/src/Core/Models/Domain/Account.cs +++ b/src/Core/Models/Domain/Account.cs @@ -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; } } } diff --git a/src/Core/Services/AuthService.cs b/src/Core/Services/AuthService.cs index f919f27667..3ead4281ee 100644 --- a/src/Core/Services/AuthService.cs +++ b/src/Core/Services/AuthService.cs @@ -445,7 +445,7 @@ namespace Bit.Core.Services } - _stateService.BiometricLocked = false; + await _stateService.SetBiometricLockedAsync(false); _messagingService.Send("loggedIn"); return result; } diff --git a/src/Core/Services/StateService.cs b/src/Core/Services/StateService.cs index 8c71621911..16a61a341a 100644 --- a/src/Core/Services/StateService.cs +++ b/src/Core/Services/StateService.cs @@ -21,8 +21,6 @@ namespace Bit.Core.Services private State _state; private bool _migrationChecked; - public bool BiometricLocked { get; set; } = true; - public List 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 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 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) diff --git a/src/Core/Services/VaultTimeoutService.cs b/src/Core/Services/VaultTimeoutService.cs index 692cd3ccdb..fa74bdb20e 100644 --- a/src/Core/Services/VaultTimeoutService.cs +++ b/src/Core/Services/VaultTimeoutService.cs @@ -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); diff --git a/src/iOS.Core/Controllers/LockPasswordViewController.cs b/src/iOS.Core/Controllers/LockPasswordViewController.cs index c2f57fd41b..5f4bfcee4d 100644 --- a/src/iOS.Core/Controllers/LockPasswordViewController.cs +++ b/src/iOS.Core/Controllers/LockPasswordViewController.cs @@ -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(); }