1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-15 15:53:44 +00:00
Files
mobile/src/Core/Services/StateService.cs
Todd Martin bfcfd367dd Trusted Device Encryption feature (#2656)
* [PM-1208] Add Device approval options screen. View model waiting for additional logic to be added.

* [PM-1208] Add device related api endpoint. Add AccoundDecryptOptions model and property to user Account.

* [PM-1208] Add continue button and not you option

* [PM-1379] add DeviceTrustCryptoService with establish trust logic (#2535)

* [PM-1379] add DeviceCryptoService with establish trust logic

* PM-1379 update api location and other minor refactors

* pm-1379 fix encoding

* update trusted device keys api call to Put

* [PM-1379] rename DeviceCryptoService to DeviceTrustCryptoService
- refactors to prevent side effects

* [PM-1379] rearrange methods in DeviceTrustCryptoService

* [PM-1379] rearrange methods in abstraction

* [PM-1379] deconstruct tuples

* [PM-1379] remove extra tasks

* [PM-2583] Answer auth request with mp field as null if doesn't have it. (#2609)

* [PM-2287][PM-2289][PM-2293] Approval Options (#2608)

* [PM-2293] Add AuthRequestType to PasswordlessLoginPage.

* [PM-2293] Add Actions to ApproveWithDevicePage

* [PM-2293] Change screen text based on AuthRequestType

* [PM-2293] Refactor AuthRequestType enum. Add label. Remove unnecessary actions.

* [PM-2293] Change boolean variable expression.

* [PM-2293] Trust device after admin request login.

* code format

* [PM-2287] Add trust device to master password unlock. Change trust device method. Remove email from SSO login page.

* [PM-2293] Fix state variable get set.

* [PM-2287][PM-2289][PM-2293] Rename method

* [PM-1201] Change timeout actions available based on hasMasterPassword (#2610)

* [PM-1201] Change timeout actions available based on hasMasterPassword

* [PM-2731] add user key and master key types

* [PM-2713] add new state for new keys and obsolete old ones
- UserKey
- MasterKey
- UserKeyMasterKey (enc UserKey from User Table)

* [PM-271] add UserKey and MasterKey support to crypto service

* [PM-2713] rename key hash to password hash & begin add methods to crypto service

* [PM-2713] continue organizing crypto service

* [PM-2713] more updates to crypto service

* [PM-2713] add new pin methods to state service

* [PM-2713] fix signature of GetUserKeyPin

* [PM-2713] add make user key method to crypto service

* [PM-2713] refresh pin key when setting user key

* [PM-2713] use new MakeMasterKey method

* [PM-2713] add toggle method to crypto service for keys

* [PM-2713] converting calls to new crypto service api

* [PM-2713] add migration for pin on lock screens

* [PM-2713] more conversions to new crypto service api

* [PM-2713] convert cipher service and others to crypto service api

* [PM-2713] More conversions to crypto api

* [PM-2713] use new crypto service api in auth service

* [PM-2713] remove unused cached values in crypto service

* [PM-2713] set decrypt and set user key in login helper

* fix bad merge

* Update crypto service api call to fix build

* [PM-1208] Fix app resource file

* [PM-1208] Fix merge

* [PM-1208] Fix merge

* [PM-2713] optimize async code in crypto service

* [PM-2713] rename password hash to master key hash

* [PM-2713] fix casting issues and pin

* [PM-2713] remove extra comment

* [PM-2713] remove broken casting

* [PM-2297] Login with trusted device (Flow 2) (#2623)

* [PM-2297] Add DecryptUserKeyWithDeviceKey method

* [PM-2297] Add methods to DeviceTrustCryptoService update decryption options model

* [PM-2297] Update account decryption options model

* [PM-2297] Fix TrustedDeviceOption and DeviceResponse model. Change StateService device key get set to have default user id

* [PM-2297] Update navigation to decryption options

* [PM-2297] Add missing action navigations to iOS extensions

* [PM-2297] Fix trust device bug/typo

* [PM-2297] Fix model bug

* [PM-2297] Fix state var crash

* [PM-2297] Add trust device login logic to auth service

* [PM-2297] Refactor auth service key connector code

* [PM-2297] Remove reconciledOptions for deviceKey in state service

* [PM-2297] Remove unnecessary user id params

* [PM-2289] [PM-2293] TDE Login with device Admin Request (#2642)

* [PM-2713] deconstruct new key pair

* [PM-2713] rename PrivateKey methods to UserPrivateKey on crypto service

* [PM-2713] rename PinLockEnum to PinLockType

* [PM-2713] don't pass user key as param when encrypting

* [PM-2713] rename toggle method, don't reset enc user key

* [PM-2713] pr feedback

* [PM-2713] PR feedback

* [PM-2713] rename get pin lock type method

* [PM-2713] revert feedback for build

* [PM-2713] rename state methods

* [PM-2713] combine makeDataEncKey methods

* [PM-2713] consolidate attachment key creation
- also fix ios files missed during symbol rename

* [PM-2713] replace generic with inherited class

* rename account keys to be more descriptive

* [PM-2713] add auto unlock key to mobile

* [PM-1208] Add TDE flows for new users (#2655)

* [PM-1208] Create new user on SSO. Logout if not password is setup or has pending admin auth request.

* [PM-1208] Fix new user UserKey decryption.

* [PM-1208] Add new user continue to vault logic. Auto enrol user on continue.

* [PM-1208] Trust device only if needed

* [PM-1208] Add logic for New User SSO.

* [PM-1208] Add logic for New User SSO (missing file).

* [PM-2713] set user key on set password page

* [PM-2713] set enc user key during kc onboarding

* fix formatting

* [PM-2713] make method async again
- returning null from a task thats not async throws

* [PM-2713] clear service cache when adding new account

* Fix build after merge

* [PM-3313] Fix Android SSO Login (#2663)

* [PM-3313] Catch exception on AuthPendingRequest

* [PM-3313] Fix lock timeout action if user doesn't have a master password.

* code format

* [PM-3313] Null email in Approval Options screen (#2664)

* [PM-3313] Fix null email in approval options screen

* [PM-3320][PM-3321] Fix labels and UI tweaks (#2666)

* [PM-3320] Fix UI copy and remember me default ON.

* [PM-3321] Fix UI on Log in with device screen.

* [PM-3337] Fix admin request deny error (#2669)

* [PM-3342] Not you button logs user out. (#2672)

* [PM-3319] Check for admin request in Lock page (#2668)

* [PM-3319] Ignore admin auth request when choosing mp as decryption option.

* [PM-2289] Change header title based on auth request type (#2670)

* [PM-2289] Change header title based on auth request type

* [PM-3333] Check for purged admin auth requests (#2671)

* [PM-3333] Check for purged admin auth requests

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-3341] Vault Timeout Action not persisted correctly (#2673)

* [PM-3341] Fix timeout action change when navigating

* [PM-3357] Fix copy for Login Initiated (#2674)

* [PM-3362] Fix auth request approval (#2675)

* [PM-3362] Fix auth request approval

* [PM-3362] Add new exception type

* [PM-3102] Update Master password reprompt to be based on MP instead of Key Connector (#2653)

* PM-3102 Added check to see if a user has master password set replacing previous usage of key connector.

* PM-3102 Fix formatting

* [PM-2713] Final merge from Key Migration branch to TDE Feature branch (#2667)

* [PM-2713] add async to key connector service methods

* [PM-2713] rename ephemeral pin key

* add state for biometric key and accept UserKey instead of string for auto key

* Get UserKey from bio state on unlock

* PM-2713 Fix auto-migrating EncKeyEncrypted into MasterKey encrypted UserKey when requesting DecryptUserKeyWithMasterKeyAsync is called

* renaming bio key and fix build

* PM-3194 Fix biometrics button to be shown on upgrade when no UserKey is present yet

* revert removal of key connector service from auth service

* PM-2713 set user key when using KC

* clear enc user key after migration

* use is true for nullable bool

* PR feedback, refactor kc service

---------

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* Fix app fresh install user login with master password. (#2676)

* [PM-3303] Fix biometric login after key migration (#2679)

* [PM-3303] Add condition to biometric unlock

* [PM-3381] Fix TDE login 2FA flow (#2678)

* [PM-3381] Check for vault lock on 2FA screen

* [PM-3381] Move logic to ViewModel

* [PM-3381] Fix null vm error

* [PM-3379] Fix key rotation on trusted device. (#2680)

* [PM-3381] Update login flows (#2683)

* [PM-3381] Update login flows

* [PM-3381] Remove _authingWithSso parameter

* PM-3385 Fix MP reprompt item level when no MP hash is stored like logging in with TDE. Also refactor code to be more maintainable (#2687)

* PM-3386 Fix MP reprompt / OTP decision to be also based on the master key hash. (#2688)

* PM-3450 Fix has master password with mp key hash check (#2689)

* [PM-3394] Fix login with device for passwordless approvals (#2686)

* set activeUserId to null when logging in a new account
- Also stop the user key from being set in inactive accounts

* get token for login with device if approving device doesn't have master key

* add comment

* simplify logic

* check for route instead of using isAuthenticated
- we don't clear the user id when logging in new account
- this means we can't trust the state service, so we have to base our logic off the route in login with device

* use authenticated auth request for tde login with device

* [PM-3394] Add authingWithSso parameter to LoginPasswordlessRequestPage.

* pr feedback

* [PM-3394] Refactor condition

Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>

* [PM-3462] Handle force password reset on mobile with TDE (#2694)

* [PM-3462] Handle force password reset on mobile with TDE

* [PM-3462] update references to refactored crypto method
- fix kc bug, we were sending private key instead of user key to server
- rename kc service method to be correct

* [PM-3462] Update TwoFactorPage login logic

* [PM-3462] Added pending admin request check to TwoFactorPage

* [PM-3462] Added new exception types for null keys

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>

* [PM-1029] Fix Async suffix in ApiService. Add UserKeyNullExceptions.

* [PM 3513] Fix passwordless 2fa login with device on mobile (#2700)

* [PM-3513] Fix 2FA for normal login with device with users without mp

* move _userKey

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>

* clear encrypted pin on logout (#2699)

---------

Co-authored-by: André Bispo <abispo@bitwarden.com>
Co-authored-by: Jake Fink <jfink@bitwarden.com>
Co-authored-by: Federico Maccaroni <fedemkr@gmail.com>
2023-08-17 15:19:35 -04:00

1823 lines
80 KiB
C#

using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Data;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Response;
using Bit.Core.Models.View;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
public class StateService : IStateService
{
// TODO: Refactor this removing all storage services and use the IStorageMediatorService instead
private readonly IStorageService _storageService;
private readonly IStorageService _secureStorageService;
private readonly IStorageMediatorService _storageMediatorService;
private readonly IMessagingService _messagingService;
private State _state;
private bool _migrationChecked;
public List<AccountView> AccountViews { get; set; }
public StateService(IStorageService storageService,
IStorageService secureStorageService,
IStorageMediatorService storageMediatorService,
IMessagingService messagingService)
{
_storageService = storageService;
_secureStorageService = secureStorageService;
_storageMediatorService = storageMediatorService;
_messagingService = messagingService;
}
public async Task<string> GetActiveUserIdAsync()
{
await CheckStateAsync();
var activeUserId = _state?.ActiveUserId;
if (activeUserId == null)
{
var state = await GetStateFromStorageAsync();
activeUserId = state?.ActiveUserId;
}
return activeUserId;
}
public async Task<string> GetActiveUserEmailAsync()
{
var activeUserId = await GetActiveUserIdAsync();
return await GetEmailAsync(activeUserId);
}
public async Task<T> GetActiveUserCustomDataAsync<T>(Func<Account, T> dataMapper)
{
var userId = await GetActiveUserIdAsync();
var account = await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
);
return dataMapper(account);
}
public async Task<bool> IsActiveAccountAsync(string userId = null)
{
if (userId == null)
{
return true;
}
return userId == await GetActiveUserIdAsync();
}
public async Task SetActiveUserAsync(string userId)
{
if (userId != null)
{
await ValidateUserAsync(userId);
}
await CheckStateAsync();
var state = await GetStateFromStorageAsync();
state.ActiveUserId = userId;
await SaveStateToStorageAsync(state);
_state.ActiveUserId = userId;
// Update pre-auth settings based on now-active user
await SetRememberedOrgIdentifierAsync(await GetRememberedOrgIdentifierAsync());
await SetPreAuthEnvironmentUrlsAsync(await GetEnvironmentUrlsAsync());
await SetLastUserShouldConnectToWatchAsync();
}
public async Task CheckExtensionActiveUserAndSwitchIfNeededAsync()
{
var extensionUserId = await GetExtensionActiveUserIdFromStorageAsync();
if (string.IsNullOrEmpty(extensionUserId))
{
return;
}
if (_state?.ActiveUserId == extensionUserId)
{
// Clear the value in case the user changes the active user from the app
// so if that happens and the user sends the app to background and comes back
// the user is not changed again.
await SaveExtensionActiveUserIdToStorageAsync(null);
return;
}
await SetActiveUserAsync(extensionUserId);
await SaveExtensionActiveUserIdToStorageAsync(null);
_messagingService.Send(AccountsManagerMessageCommands.SWITCHED_ACCOUNT);
}
public async Task<bool> IsAuthenticatedAsync(string userId = null)
{
return await GetAccessTokenAsync(userId) != null;
}
public async Task<string> GetUserIdAsync(string email)
{
if (string.IsNullOrWhiteSpace(email))
{
throw new ArgumentNullException(nameof(email));
}
await CheckStateAsync();
if (_state?.Accounts != null)
{
foreach (var account in _state.Accounts)
{
var accountEmail = account.Value?.Profile?.Email;
if (accountEmail == email)
{
return account.Value.Profile.UserId;
}
}
}
return null;
}
public async Task RefreshAccountViewsAsync(bool allowAddAccountRow)
{
await CheckStateAsync();
if (AccountViews == null)
{
AccountViews = new List<AccountView>();
}
else
{
AccountViews.Clear();
}
var accountList = _state?.Accounts?.Values.ToList();
if (accountList == null)
{
return;
}
var vaultTimeoutService = ServiceContainer.Resolve<IVaultTimeoutService>("vaultTimeoutService");
foreach (var account in accountList)
{
var isActiveAccount = account.Profile.UserId == _state.ActiveUserId;
var accountView = new AccountView(account, isActiveAccount);
if (await vaultTimeoutService.IsLoggedOutByTimeoutAsync(accountView.UserId) ||
await vaultTimeoutService.ShouldLogOutByTimeoutAsync(accountView.UserId))
{
accountView.AuthStatus = AuthenticationStatus.LoggedOut;
}
else if (await vaultTimeoutService.IsLockedAsync(accountView.UserId) ||
await vaultTimeoutService.ShouldLockAsync(accountView.UserId))
{
accountView.AuthStatus = AuthenticationStatus.Locked;
}
else
{
accountView.AuthStatus = AuthenticationStatus.Unlocked;
}
AccountViews.Add(accountView);
}
if (allowAddAccountRow && AccountViews.Count < Constants.MaxAccounts)
{
AccountViews.Add(new AccountView());
}
}
public async Task AddAccountAsync(Account account)
{
await ScaffoldNewAccountAsync(account);
await SetActiveUserAsync(account.Profile.UserId);
await RefreshAccountViewsAsync(true);
}
public async Task LogoutAccountAsync(string userId, bool userInitiated)
{
if (string.IsNullOrWhiteSpace(userId))
{
throw new ArgumentNullException(nameof(userId));
}
await CheckStateAsync();
await RemoveAccountAsync(userId, userInitiated);
// If user initiated logout (not vault timeout) and ActiveUserId is null after account removal, find the
// next user to make active, if any
if (userInitiated && _state?.ActiveUserId == null && _state?.Accounts != null)
{
foreach (var account in _state.Accounts)
{
var uid = account.Value?.Profile?.UserId;
if (uid == null)
{
continue;
}
await SetActiveUserAsync(uid);
break;
}
}
}
public async Task<EnvironmentUrlData> GetPreAuthEnvironmentUrlsAsync()
{
return await GetValueAsync<EnvironmentUrlData>(
Constants.PreAuthEnvironmentUrlsKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetPreAuthEnvironmentUrlsAsync(EnvironmentUrlData value)
{
await SetValueAsync(
Constants.PreAuthEnvironmentUrlsKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<EnvironmentUrlData> GetEnvironmentUrlsAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Settings?.EnvironmentUrls;
}
public async Task<UserKey> GetUserKeyBiometricUnlockAsync(string userId = null)
{
var keyB64 = await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.UserKeyBiometricUnlockKey, userId), true);
return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64));
}
public async Task SetUserKeyBiometricUnlockAsync(UserKey value, string userId = null)
{
await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.UserKeyBiometricUnlockKey, userId), value?.KeyB64, true);
}
public async Task<bool?> GetBiometricUnlockAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.BiometricUnlockKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetBiometricUnlockAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.BiometricUnlockKey(reconciledOptions.UserId), 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<string> GetSystemBiometricIntegrityState(string bioIntegritySrcKey)
{
return await GetValueAsync<string>(bioIntegritySrcKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetSystemBiometricIntegrityState(string bioIntegritySrcKey, string systemBioIntegrityState)
{
await SetValueAsync(bioIntegritySrcKey, systemBioIntegrityState, await GetDefaultStorageOptionsAsync());
}
public async Task<bool> IsAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null)
{
var systemBioIntegrityState = await GetSystemBiometricIntegrityState(bioIntegritySrcKey);
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(
Constants.AccountBiometricIntegrityValidKey(reconciledOptions.UserId, systemBioIntegrityState),
reconciledOptions) ?? false;
}
public async Task SetAccountBiometricIntegrityValidAsync(string bioIntegritySrcKey, string userId = null)
{
var systemBioIntegrityState = await GetSystemBiometricIntegrityState(bioIntegritySrcKey);
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(
Constants.AccountBiometricIntegrityValidKey(reconciledOptions.UserId, systemBioIntegrityState),
true, reconciledOptions);
}
public async Task<UserKey> GetUserKeyAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.UserKey;
}
public async Task SetUserKeyAsync(UserKey value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.VolatileData.UserKey = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<MasterKey> GetMasterKeyAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.MasterKey;
}
public async Task SetMasterKeyAsync(MasterKey value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.VolatileData.MasterKey = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetMasterKeyEncryptedUserKeyAsync(string userId = null)
{
return await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), false);
}
public async Task SetMasterKeyEncryptedUserKeyAsync(string value, string userId = null)
{
await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.MasterKeyEncryptedUserKeyKey, userId), value, false);
}
public async Task<UserKey> GetUserKeyAutoUnlockAsync(string userId = null)
{
var keyB64 = await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), true);
return keyB64 == null ? null : new UserKey(Convert.FromBase64String(keyB64));
}
public async Task SetUserKeyAutoUnlockAsync(UserKey value, string userId = null)
{
await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.UserKeyAutoUnlockKey, userId), value?.KeyB64, true);
}
public async Task<bool> CanAccessPremiumAsync(string userId = null)
{
if (userId == null)
{
userId = await GetActiveUserIdAsync();
}
if (!await IsAuthenticatedAsync(userId))
{
return false;
}
var account = await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync()));
if (account?.Profile?.HasPremiumPersonally.GetValueOrDefault() ?? false)
{
return true;
}
var organizationService = ServiceContainer.Resolve<IOrganizationService>("organizationService");
var organizations = await organizationService.GetAllAsync(userId);
return organizations?.Any(o => o.UsersGetPremium && o.Enabled) ?? false;
}
public async Task SetPersonalPremiumAsync(bool value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
if (account?.Profile == null || account.Profile.HasPremiumPersonally.GetValueOrDefault() == value)
{
return;
}
account.Profile.HasPremiumPersonally = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetProtectedPinAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.ProtectedPinKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetProtectedPinAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.ProtectedPinKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<EncString> GetPinKeyEncryptedUserKeyAsync(string userId = null)
{
var key = await _storageMediatorService.GetAsync<string>(
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), false);
return key != null ? new EncString(key) : null;
}
public async Task SetPinKeyEncryptedUserKeyAsync(EncString value, string userId = null)
{
await _storageMediatorService.SaveAsync(
await ComposeKeyAsync(Constants.PinKeyEncryptedUserKeyKey, userId), value?.EncryptedString, false);
}
public async Task<EncString> GetPinKeyEncryptedUserKeyEphemeralAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.PinKeyEncryptedUserKeyEphemeral;
}
public async Task SetPinKeyEncryptedUserKeyEphemeralAsync(EncString value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.VolatileData.PinKeyEncryptedUserKeyEphemeral = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task SetKdfConfigurationAsync(KdfConfig config, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.KdfType = config.Type;
account.Profile.KdfIterations = config.Iterations;
account.Profile.KdfMemory = config.Memory;
account.Profile.KdfParallelism = config.Parallelism;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetKeyHashAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.KeyHashKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetKeyHashAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.KeyHashKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, string>> GetOrgKeysEncryptedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, string>>(Constants.EncOrgKeysKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.EncOrgKeysKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetPrivateKeyEncryptedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.EncPrivateKeyKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetPrivateKeyEncryptedAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.EncPrivateKeyKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var deviceKeyB64 = await _storageMediatorService.GetAsync<string>(Constants.DeviceKeyKey(reconciledOptions.UserId), true);
if (string.IsNullOrEmpty(deviceKeyB64))
{
return null;
}
return new SymmetricCryptoKey(Convert.FromBase64String(deviceKeyB64));
}
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await _storageMediatorService.SaveAsync(Constants.DeviceKeyKey(reconciledOptions.UserId), value?.KeyB64, true);
}
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<List<string>>(Constants.AutofillBlacklistedUrisKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.AutofillBlacklistedUrisKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<bool?> GetAutofillTileAddedAsync()
{
return await GetValueAsync<bool?>(Constants.AutofillTileAdded, await GetDefaultStorageOptionsAsync());
}
public async Task SetAutofillTileAddedAsync(bool? value)
{
await SetValueAsync(Constants.AutofillTileAdded, value, await GetDefaultStorageOptionsAsync());
}
public async Task<string> GetEmailAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.Email;
}
public async Task<string> GetNameAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.Name;
}
public async Task SetNameAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.Name = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetOrgIdentifierAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.OrgIdentifier;
}
public async Task<long?> GetLastActiveTimeAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<long?>(Constants.LastActiveTimeKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetLastActiveTimeAsync(long? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.LastActiveTimeKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<int?> GetVaultTimeoutAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<int?>(Constants.VaultTimeoutKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetVaultTimeoutAsync(int? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.VaultTimeoutKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<VaultTimeoutAction?> GetVaultTimeoutActionAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<VaultTimeoutAction?>(Constants.VaultTimeoutActionKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetVaultTimeoutActionAsync(VaultTimeoutAction? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.VaultTimeoutActionKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<bool> GetScreenCaptureAllowedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.ScreenCaptureAllowedKey(reconciledOptions.UserId),
reconciledOptions) ?? CoreHelpers.InDebugMode();
}
public async Task SetScreenCaptureAllowedAsync(bool value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.ScreenCaptureAllowedKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<DateTime?> GetLastFileCacheClearAsync()
{
return await GetValueAsync<DateTime?>(Constants.LastFileCacheClearKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetLastFileCacheClearAsync(DateTime? value)
{
await SetValueAsync(Constants.LastFileCacheClearKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<PreviousPageInfo> GetPreviousPageInfoAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<PreviousPageInfo>(Constants.PreviousPageKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetPreviousPageInfoAsync(PreviousPageInfo value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PreviousPageKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<int> GetInvalidUnlockAttemptsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<int>(Constants.InvalidUnlockAttemptsKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetInvalidUnlockAttemptsAsync(int? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.InvalidUnlockAttemptsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetLastBuildAsync()
{
return await GetValueAsync<string>(Constants.LastBuildKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetLastBuildAsync(string value)
{
await SetValueAsync(Constants.LastBuildKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<bool?> GetDisableFaviconAsync()
{
return await GetValueAsync<bool?>(Constants.DisableFaviconKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetDisableFaviconAsync(bool? value)
{
await SetValueAsync(Constants.DisableFaviconKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<bool?> GetDisableAutoTotpCopyAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.DisableAutoTotpCopyKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetDisableAutoTotpCopyAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.DisableAutoTotpCopyKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<bool?> GetInlineAutofillEnabledAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.InlineAutofillEnabledKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetInlineAutofillEnabledAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.InlineAutofillEnabledKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<bool?> GetAutofillDisableSavePromptAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.AutofillDisableSavePromptKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetAutofillDisableSavePromptAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.AutofillDisableSavePromptKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<Dictionary<string, Dictionary<string, object>>> GetLocalDataAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, Dictionary<string, object>>>(
Constants.LocalDataKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetLocalDataAsync(Dictionary<string, Dictionary<string, object>> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.LocalDataKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, CipherData>> GetEncryptedCiphersAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, CipherData>>(Constants.CiphersKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetEncryptedCiphersAsync(Dictionary<string, CipherData> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.CiphersKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<int?> GetDefaultUriMatchAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<int?>(Constants.DefaultUriMatchKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetDefaultUriMatchAsync(int? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.DefaultUriMatchKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<HashSet<string>> GetNeverDomainsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<HashSet<string>>(Constants.NeverDomainsKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetNeverDomainsAsync(HashSet<string> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.NeverDomainsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<int?> GetClearClipboardAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<int?>(Constants.ClearClipboardKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetClearClipboardAsync(int? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.ClearClipboardKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, CollectionData>> GetEncryptedCollectionsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, CollectionData>>(
Constants.CollectionsKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetEncryptedCollectionsAsync(Dictionary<string, CollectionData> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.CollectionsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<bool> GetPasswordRepromptAutofillAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.PasswordRepromptAutofillKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetPasswordRepromptAutofillAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PasswordRepromptAutofillKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<bool> GetPasswordVerifiedAutofillAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.PasswordVerifiedAutofillKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetPasswordVerifiedAutofillAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PasswordVerifiedAutofillKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<DateTime?> GetLastSyncAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<DateTime?>(Constants.LastSyncKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetLastSyncAsync(DateTime? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.LastSyncKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetSecurityStampAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.Stamp;
}
public async Task SetSecurityStampAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.Stamp = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<bool> GetEmailVerifiedAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.EmailVerified ?? false;
}
public async Task SetEmailVerifiedAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.EmailVerified = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<bool> GetSyncOnRefreshAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.SyncOnRefreshKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetSyncOnRefreshAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.SyncOnRefreshKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetRememberedEmailAsync()
{
return await GetValueAsync<string>(Constants.RememberedEmailKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetRememberedEmailAsync(string value)
{
await SetValueAsync(Constants.RememberedEmailKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<string> GetRememberedOrgIdentifierAsync()
{
return await GetValueAsync<string>(Constants.RememberedOrgIdentifierKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetRememberedOrgIdentifierAsync(string value)
{
await SetValueAsync(Constants.RememberedOrgIdentifierKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<string> GetThemeAsync()
{
return await GetValueAsync<string>(Constants.ThemeKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetThemeAsync(string value)
{
await SetValueAsync(Constants.ThemeKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<string> GetAutoDarkThemeAsync()
{
return await GetValueAsync<string>(Constants.AutoDarkThemeKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetAutoDarkThemeAsync(string value)
{
await SetValueAsync(Constants.AutoDarkThemeKey, value, await GetDefaultStorageOptionsAsync());
}
public string GetLocale()
{
return _storageMediatorService.Get<string>(Constants.AppLocaleKey);
}
public void SetLocale(string locale)
{
_storageMediatorService.Save(Constants.AppLocaleKey, locale);
}
public async Task<bool?> GetAddSitePromptShownAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.AddSitePromptShownKey, reconciledOptions);
}
public async Task SetAddSitePromptShownAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.AddSitePromptShownKey, value, reconciledOptions);
}
public async Task<bool?> GetPushInitialPromptShownAsync()
{
return await GetValueAsync<bool?>(Constants.PushInitialPromptShownKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetPushInitialPromptShownAsync(bool? value)
{
await SetValueAsync(Constants.PushInitialPromptShownKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<DateTime?> GetPushLastRegistrationDateAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<DateTime?>(Constants.PushLastRegistrationDateKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetPushLastRegistrationDateAsync(DateTime? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PushLastRegistrationDateKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<string> GetPushInstallationRegistrationErrorAsync()
{
return await GetValueAsync<string>(Constants.PushInstallationRegistrationErrorKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetPushInstallationRegistrationErrorAsync(string value)
{
await SetValueAsync(Constants.PushInstallationRegistrationErrorKey, value,
await GetDefaultStorageOptionsAsync());
}
public async Task<string> GetPushCurrentTokenAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.PushCurrentTokenKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetPushCurrentTokenAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PushCurrentTokenKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<List<EventData>> GetEventCollectionAsync()
{
return await GetValueAsync<List<EventData>>(Constants.EventCollectionKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetEventCollectionAsync(List<EventData> value)
{
await SetValueAsync(Constants.EventCollectionKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<Dictionary<string, FolderData>> GetEncryptedFoldersAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, FolderData>>(Constants.FoldersKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetEncryptedFoldersAsync(Dictionary<string, FolderData> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.FoldersKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, PolicyData>> GetEncryptedPoliciesAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, PolicyData>>(Constants.PoliciesKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetEncryptedPoliciesAsync(Dictionary<string, PolicyData> value, string userId)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PoliciesKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetPushRegisteredTokenAsync()
{
return await GetValueAsync<string>(Constants.PushRegisteredTokenKey, await GetDefaultStorageOptionsAsync());
}
public async Task SetPushRegisteredTokenAsync(string value)
{
await SetValueAsync(Constants.PushRegisteredTokenKey, value, await GetDefaultStorageOptionsAsync());
}
public async Task<bool> GetUsesKeyConnectorAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.UsesKeyConnectorKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetUsesKeyConnectorAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.UsesKeyConnectorKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<ForcePasswordResetReason?> GetForcePasswordResetReasonAsync(string userId = null)
{
var reconcileOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return (await GetAccountAsync(reconcileOptions))?.Profile?.ForcePasswordResetReason;
}
public async Task SetForcePasswordResetReasonAsync(ForcePasswordResetReason? value, string userId = null)
{
var reconcileOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconcileOptions);
account.Profile.ForcePasswordResetReason = value;
await SaveAccountAsync(account, reconcileOptions);
}
public async Task<Dictionary<string, OrganizationData>> GetOrganizationsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, OrganizationData>>(
Constants.OrganizationsKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetOrganizationsAsync(Dictionary<string, OrganizationData> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.OrganizationsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<PasswordGenerationOptions> GetPasswordGenerationOptionsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<PasswordGenerationOptions>(Constants.PassGenOptionsKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetPasswordGenerationOptionsAsync(PasswordGenerationOptions value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PassGenOptionsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<UsernameGenerationOptions> GetUsernameGenerationOptionsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<UsernameGenerationOptions>(
Constants.UsernameGenOptionsKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetUsernameGenerationOptionsAsync(UsernameGenerationOptions value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.UsernameGenOptionsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<List<GeneratedPasswordHistory>> GetEncryptedPasswordGenerationHistory(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<List<GeneratedPasswordHistory>>(
Constants.PassGenHistoryKey(reconciledOptions.UserId), reconciledOptions);
}
public async Task SetEncryptedPasswordGenerationHistoryAsync(List<GeneratedPasswordHistory> value,
string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PassGenHistoryKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, SendData>> GetEncryptedSendsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, SendData>>(Constants.SendsKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetEncryptedSendsAsync(Dictionary<string, SendData> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.SendsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<Dictionary<string, object>> GetSettingsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<Dictionary<string, object>>(Constants.SettingsKey(reconciledOptions.UserId),
reconciledOptions);
}
public async Task SetSettingsAsync(Dictionary<string, object> value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.SettingsKey(reconciledOptions.UserId), value, reconciledOptions);
}
public async Task<string> GetAccessTokenAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Tokens?.AccessToken;
}
public async Task SetAccessTokenAsync(string value, bool skipTokenStorage, string userId = null)
{
var reconciledOptions = ReconcileOptions(
new StorageOptions { UserId = userId, SkipTokenStorage = skipTokenStorage },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Tokens.AccessToken = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetRefreshTokenAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Tokens?.RefreshToken;
}
public async Task SetRefreshTokenAsync(string value, bool skipTokenStorage, string userId = null)
{
var reconciledOptions = ReconcileOptions(
new StorageOptions { UserId = userId, SkipTokenStorage = skipTokenStorage },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Tokens.RefreshToken = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetTwoFactorTokenAsync(string email = null)
{
var reconciledOptions =
ReconcileOptions(new StorageOptions { Email = email }, await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.TwoFactorTokenKey(reconciledOptions.Email), reconciledOptions);
}
public async Task SetTwoFactorTokenAsync(string value, string email = null)
{
var reconciledOptions =
ReconcileOptions(new StorageOptions { Email = email }, await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.TwoFactorTokenKey(reconciledOptions.Email), value, reconciledOptions);
}
public async Task<bool> GetApprovePasswordlessLoginsAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.ApprovePasswordlessLoginsKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetApprovePasswordlessLoginsAsync(bool? value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.ApprovePasswordlessLoginsKey(reconciledOptions.UserId), value,
reconciledOptions);
}
public async Task<PasswordlessRequestNotification> GetPasswordlessLoginNotificationAsync()
{
return await GetValueAsync<PasswordlessRequestNotification>(Constants.PasswordlessLoginNotificationKey,
await GetDefaultStorageOptionsAsync());
}
public async Task SetPasswordlessLoginNotificationAsync(PasswordlessRequestNotification value)
{
await SetValueAsync(Constants.PasswordlessLoginNotificationKey, value,
await GetDefaultStorageOptionsAsync());
}
public async Task SetAvatarColorAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.Profile.AvatarColor = value;
await SaveAccountAsync(account, reconciledOptions);
}
public async Task<string> GetAvatarColorAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.AvatarColor;
}
public async Task<string> GetPreLoginEmailAsync()
{
var options = await GetDefaultStorageOptionsAsync();
return await GetValueAsync<string>(Constants.PreLoginEmailKey, options);
}
public async Task SetPreLoginEmailAsync(string value)
{
var options = await GetDefaultStorageOptionsAsync();
await SetValueAsync(Constants.PreLoginEmailKey, value, options);
}
public async Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync())
))?.Profile?.UserDecryptionOptions;
}
public async Task<bool> GetShouldTrustDeviceAsync()
{
return await _storageMediatorService.GetAsync<bool>(Constants.ShouldTrustDevice);
}
public async Task SetShouldTrustDeviceAsync(bool value)
{
await _storageMediatorService.SaveAsync(Constants.ShouldTrustDevice, value);
}
public async Task<PendingAdminAuthRequest> GetPendingAdminAuthRequestAsync(string userId = null)
{
try
{
// GetAsync will throw an ArgumentException exception if there isn't a value to deserialize
return await _storageMediatorService.GetAsync<PendingAdminAuthRequest>(await ComposeKeyAsync(Constants.PendingAdminAuthRequest, userId), true);
}
catch (ArgumentException)
{
return null;
}
}
public async Task SetPendingAdminAuthRequestAsync(PendingAdminAuthRequest value, string userId = null)
{
await _storageMediatorService.SaveAsync(await ComposeKeyAsync(Constants.PendingAdminAuthRequest, userId), value, true);
}
public ConfigResponse GetConfigs()
{
return _storageMediatorService.Get<ConfigResponse>(Constants.ConfigsKey);
}
public void SetConfigs(ConfigResponse value)
{
_storageMediatorService.Save(Constants.ConfigsKey, value);
}
// Helpers
[Obsolete("Use IStorageMediatorService instead")]
private async Task<T> GetValueAsync<T>(string key, StorageOptions options)
{
return await GetStorageService(options).GetAsync<T>(key);
}
[Obsolete("Use IStorageMediatorService instead")]
private async Task SetValueAsync<T>(string key, T value, StorageOptions options)
{
if (value == null)
{
await GetStorageService(options).RemoveAsync(key);
return;
}
await GetStorageService(options).SaveAsync(key, value);
}
[Obsolete("Use IStorageMediatorService instead")]
private IStorageService GetStorageService(StorageOptions options)
{
return options.UseSecureStorage.GetValueOrDefault(false) ? _secureStorageService : _storageService;
}
private async Task<string> ComposeKeyAsync(Func<string, string> userDependantKey, string userId = null)
{
return userDependantKey(userId ?? await GetActiveUserIdAsync());
}
private async Task<Account> GetAccountAsync(StorageOptions options)
{
await CheckStateAsync();
if (options?.UserId == null)
{
return null;
}
// Memory
if (_state?.Accounts?.ContainsKey(options.UserId) ?? false)
{
if (_state.Accounts[options.UserId].VolatileData == null)
{
_state.Accounts[options.UserId].VolatileData = new Account.AccountVolatileData();
}
return _state.Accounts[options.UserId];
}
// Storage
var state = await GetStateFromStorageAsync();
if (state?.Accounts?.ContainsKey(options.UserId) ?? false)
{
state.Accounts[options.UserId].VolatileData = new Account.AccountVolatileData();
return state.Accounts[options.UserId];
}
return null;
}
private async Task SaveAccountAsync(Account account, StorageOptions options = null)
{
if (account?.Profile?.UserId == null)
{
throw new Exception("account?.Profile?.UserId cannot be null");
}
await CheckStateAsync();
// Memory
if (UseMemory(options))
{
if (_state.Accounts == null)
{
_state.Accounts = new Dictionary<string, Account>();
}
_state.Accounts[account.Profile.UserId] = account;
}
// Storage
if (UseDisk(options))
{
var state = await GetStateFromStorageAsync() ?? new State();
if (state.Accounts == null)
{
state.Accounts = new Dictionary<string, Account>();
}
// Use Account copy constructor to clone with keys excluded (for storage)
state.Accounts[account.Profile.UserId] = new Account(account);
// If we have a vault timeout and the action is log out, don't store token
if (options?.SkipTokenStorage.GetValueOrDefault() ?? false)
{
state.Accounts[account.Profile.UserId].Tokens.AccessToken = null;
state.Accounts[account.Profile.UserId].Tokens.RefreshToken = null;
}
await SaveStateToStorageAsync(state);
}
}
private async Task RemoveAccountAsync(string userId, bool userInitiated)
{
if (string.IsNullOrWhiteSpace(userId))
{
throw new ArgumentNullException(nameof(userId));
}
var email = await GetEmailAsync(userId);
// Memory
if (_state?.Accounts?.ContainsKey(userId) ?? false)
{
if (userInitiated)
{
_state.Accounts.Remove(userId);
}
else
{
_state.Accounts[userId].Tokens.AccessToken = null;
_state.Accounts[userId].Tokens.RefreshToken = null;
_state.Accounts[userId].VolatileData = null;
}
}
if (userInitiated && _state?.ActiveUserId == userId)
{
_state.ActiveUserId = null;
}
// Storage
var stateModified = false;
var state = await GetStateFromStorageAsync();
if (state?.Accounts?.ContainsKey(userId) ?? false)
{
if (userInitiated)
{
state.Accounts.Remove(userId);
}
else
{
state.Accounts[userId].Tokens.AccessToken = null;
state.Accounts[userId].Tokens.RefreshToken = null;
}
stateModified = true;
}
if (userInitiated && state?.ActiveUserId == userId)
{
state.ActiveUserId = null;
stateModified = true;
}
if (stateModified)
{
await SaveStateToStorageAsync(state);
}
// Non-state storage
await Task.WhenAll(
SetUserKeyAutoUnlockAsync(null, userId),
SetUserKeyBiometricUnlockAsync(null, userId),
SetProtectedPinAsync(null, userId),
SetPinKeyEncryptedUserKeyAsync(null, userId),
SetKeyHashAsync(null, userId),
SetOrgKeysEncryptedAsync(null, userId),
SetPrivateKeyEncryptedAsync(null, userId),
SetLastActiveTimeAsync(null, userId),
SetPreviousPageInfoAsync(null, userId),
SetInvalidUnlockAttemptsAsync(null, userId),
SetLocalDataAsync(null, userId),
SetEncryptedCiphersAsync(null, userId),
SetEncryptedCollectionsAsync(null, userId),
SetLastSyncAsync(null, userId),
SetEncryptedFoldersAsync(null, userId),
SetEncryptedPoliciesAsync(null, userId),
SetUsesKeyConnectorAsync(null, userId),
SetOrganizationsAsync(null, userId),
SetEncryptedPasswordGenerationHistoryAsync(null, userId),
SetEncryptedSendsAsync(null, userId),
SetSettingsAsync(null, userId),
SetApprovePasswordlessLoginsAsync(null, userId),
SetEncKeyEncryptedAsync(null, userId),
SetKeyEncryptedAsync(null, userId),
SetPinProtectedAsync(null, userId));
}
private async Task ScaffoldNewAccountAsync(Account account)
{
await CheckStateAsync();
account.Settings.EnvironmentUrls = await GetPreAuthEnvironmentUrlsAsync();
// Storage
var state = await GetStateFromStorageAsync() ?? new State();
if (state.Accounts == null)
{
state.Accounts = new Dictionary<string, Account>();
}
state.Accounts[account.Profile.UserId] = account;
await SaveStateToStorageAsync(state);
// Memory
if (_state == null)
{
_state = state;
}
else
{
if (_state.Accounts == null)
{
_state.Accounts = new Dictionary<string, Account>();
}
_state.Accounts[account.Profile.UserId] = account;
}
// Check if account has logged in before by checking a guaranteed non-null pref
if (await GetVaultTimeoutActionAsync(account.Profile.UserId) == null)
{
// Account has never logged in, set defaults
await SetVaultTimeoutAsync(Constants.VaultTimeoutDefault, account.Profile.UserId);
await SetVaultTimeoutActionAsync(VaultTimeoutAction.Lock, account.Profile.UserId);
}
}
private StorageOptions ReconcileOptions(StorageOptions requestedOptions, StorageOptions defaultOptions)
{
if (requestedOptions == null)
{
return defaultOptions;
}
requestedOptions.StorageLocation = requestedOptions.StorageLocation ?? defaultOptions.StorageLocation;
requestedOptions.UseSecureStorage = requestedOptions.UseSecureStorage ?? defaultOptions.UseSecureStorage;
requestedOptions.UserId = requestedOptions.UserId ?? defaultOptions.UserId;
requestedOptions.Email = requestedOptions.Email ?? defaultOptions.Email;
requestedOptions.SkipTokenStorage = requestedOptions.SkipTokenStorage ?? defaultOptions.SkipTokenStorage;
return requestedOptions;
}
/// <summary>
/// Gets the default options for storage.
/// If it's only used for composing the constant key with the user id
/// then use <see cref="ComposeKeyAsync(Func{string, string}, string)"/> instead
/// which saves time if the user id is already known
/// </summary>
private async Task<StorageOptions> GetDefaultStorageOptionsAsync()
{
return new StorageOptions()
{
StorageLocation = StorageLocation.Both,
UserId = await GetActiveUserIdAsync(),
};
}
private async Task<StorageOptions> GetDefaultSecureStorageOptionsAsync()
{
return new StorageOptions()
{
StorageLocation = StorageLocation.Disk,
UseSecureStorage = true,
UserId = await GetActiveUserIdAsync(),
};
}
private async Task<StorageOptions> GetDefaultInMemoryOptionsAsync()
{
return new StorageOptions()
{
StorageLocation = StorageLocation.Memory,
UserId = await GetActiveUserIdAsync(),
};
}
private bool UseMemory(StorageOptions options)
{
return options?.StorageLocation == StorageLocation.Memory ||
options?.StorageLocation == StorageLocation.Both;
}
private bool UseDisk(StorageOptions options)
{
return options?.StorageLocation == StorageLocation.Disk ||
options?.StorageLocation == StorageLocation.Both;
}
private async Task<State> GetStateFromStorageAsync()
{
return await _storageService.GetAsync<State>(Constants.StateKey);
}
private async Task SaveStateToStorageAsync(State state)
{
await _storageService.SaveAsync(Constants.StateKey, state);
}
private async Task<string> GetExtensionActiveUserIdFromStorageAsync()
{
return await _storageService.GetAsync<string>(Constants.iOSExtensionActiveUserIdKey);
}
public async Task SaveExtensionActiveUserIdToStorageAsync(string userId)
{
await _storageService.SaveAsync(Constants.iOSExtensionActiveUserIdKey, userId);
}
private async Task CheckStateAsync()
{
if (!_migrationChecked)
{
var migrationService = ServiceContainer.Resolve<IStateMigrationService>();
await migrationService.MigrateIfNeededAsync();
_migrationChecked = true;
}
if (_state == null)
{
_state = await GetStateFromStorageAsync() ?? new State();
}
}
private async Task ValidateUserAsync(string userId)
{
if (string.IsNullOrWhiteSpace(userId))
{
throw new ArgumentNullException(nameof(userId));
}
await CheckStateAsync();
var accounts = _state?.Accounts;
if (accounts == null || !accounts.Any())
{
throw new Exception("At least one account required to validate user");
}
foreach (var account in accounts)
{
if (account.Key == userId)
{
// found match, user is valid
return;
}
}
throw new Exception("User does not exist in account list");
}
public async Task<bool> GetShouldConnectToWatchAsync(string userId = null)
{
var reconciledOptions =
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync());
return await GetValueAsync<bool?>(Constants.ShouldConnectToWatchKey(reconciledOptions.UserId),
reconciledOptions) ?? false;
}
public async Task SetShouldConnectToWatchAsync(bool shouldConnect, string userId = null)
{
var reconciledOptions =
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.ShouldConnectToWatchKey(reconciledOptions.UserId), shouldConnect,
reconciledOptions);
await SetLastUserShouldConnectToWatchAsync(shouldConnect);
}
public async Task<bool> GetLastUserShouldConnectToWatchAsync()
{
return await GetValueAsync<bool?>(Constants.LastUserShouldConnectToWatchKey,
await GetDefaultStorageOptionsAsync()) ?? false;
}
private async Task SetLastUserShouldConnectToWatchAsync(bool? shouldConnect = null)
{
await SetValueAsync(Constants.LastUserShouldConnectToWatchKey,
shouldConnect ?? await GetShouldConnectToWatchAsync(), await GetDefaultStorageOptionsAsync());
}
[Obsolete("Use GetPinKeyEncryptedUserKeyAsync instead, left for migration purposes")]
public async Task<string> GetPinProtectedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.PinProtectedKey(reconciledOptions.UserId), reconciledOptions);
}
[Obsolete("Use SetPinKeyEncryptedUserKeyAsync instead")]
public async Task SetPinProtectedAsync(string value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.PinProtectedKey(reconciledOptions.UserId), value, reconciledOptions);
}
[Obsolete("Use GetPinKeyEncryptedUserKeyEphemeralAsync instead, left for migration purposes")]
public async Task<EncString> GetPinProtectedKeyAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.PinProtectedKey;
}
[Obsolete("Use SetPinKeyEncryptedUserKeyEphemeralAsync instead")]
public async Task SetPinProtectedKeyAsync(EncString value, string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultInMemoryOptionsAsync());
var account = await GetAccountAsync(reconciledOptions);
account.VolatileData.PinProtectedKey = value;
await SaveAccountAsync(account, reconciledOptions);
}
[Obsolete("Use GetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
public async Task<string> GetEncKeyEncryptedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
return await GetValueAsync<string>(Constants.EncKeyKey(reconciledOptions.UserId), reconciledOptions);
}
[Obsolete("Use SetMasterKeyEncryptedUserKeyAsync instead, left for migration purposes")]
public async Task SetEncKeyEncryptedAsync(string value, string userId)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultStorageOptionsAsync());
await SetValueAsync(Constants.EncKeyKey(reconciledOptions.UserId), value, reconciledOptions);
}
[Obsolete("Left for migration purposes")]
public async Task SetKeyEncryptedAsync(string value, string userId)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultSecureStorageOptionsAsync());
await SetValueAsync(Constants.KeyKey(reconciledOptions.UserId), value, reconciledOptions);
}
[Obsolete("Use GetUserKeyAutoUnlock instead, left for migration purposes")]
public async Task<string> GetKeyEncryptedAsync(string userId = null)
{
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
await GetDefaultSecureStorageOptionsAsync());
return await GetValueAsync<string>(Constants.KeyKey(reconciledOptions.UserId), reconciledOptions);
}
[Obsolete("Use GetMasterKeyAsync instead, left for migration purposes")]
public async Task<SymmetricCryptoKey> GetKeyDecryptedAsync(string userId = null)
{
return (await GetAccountAsync(
ReconcileOptions(new StorageOptions { UserId = userId }, await GetDefaultInMemoryOptionsAsync())
))?.VolatileData?.Key;
}
}
}