mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 07:43:37 +00:00
[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
This commit is contained in:
@@ -19,7 +19,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_vm = BindingContext as LoginApproveDeviceViewModel;
|
_vm = BindingContext as LoginApproveDeviceViewModel;
|
||||||
_vm.LogInWithMasterPassword = () => StartLogInWithMasterPassword().FireAndForget();
|
_vm.LogInWithMasterPasswordAction = () => StartLogInWithMasterPassword().FireAndForget();
|
||||||
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
|
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
|
||||||
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
|
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
|
||||||
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
|
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ namespace Bit.App.Pages
|
|||||||
public ICommand ApproveWithMasterPasswordCommand { get; }
|
public ICommand ApproveWithMasterPasswordCommand { get; }
|
||||||
public ICommand ContinueCommand { get; }
|
public ICommand ContinueCommand { get; }
|
||||||
|
|
||||||
public Action LogInWithMasterPassword { get; set; }
|
public Action LogInWithMasterPasswordAction { get; set; }
|
||||||
public Action LogInWithDeviceAction { get; set; }
|
public Action LogInWithDeviceAction { get; set; }
|
||||||
public Action RequestAdminApprovalAction { get; set; }
|
public Action RequestAdminApprovalAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
@@ -53,7 +53,7 @@ namespace Bit.App.Pages
|
|||||||
onException: ex => HandleException(ex),
|
onException: ex => HandleException(ex),
|
||||||
allowsMultipleExecutions: false);
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
ApproveWithMasterPasswordCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithMasterPassword),
|
ApproveWithMasterPasswordCommand = new AsyncCommand(() => SetDeviceTrustAndInvokeAsync(LogInWithMasterPasswordAction),
|
||||||
onException: ex => HandleException(ex),
|
onException: ex => HandleException(ex),
|
||||||
allowsMultipleExecutions: false);
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
@@ -111,15 +111,7 @@ namespace Bit.App.Pages
|
|||||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
RequestAdminApprovalEnabled = decryptOptions?.TrustedDeviceOption?.HasAdminApproval ?? false;
|
RequestAdminApprovalEnabled = decryptOptions?.TrustedDeviceOption?.HasAdminApproval ?? false;
|
||||||
ApproveWithMasterPasswordEnabled = decryptOptions?.HasMasterPassword ?? false;
|
ApproveWithMasterPasswordEnabled = decryptOptions?.HasMasterPassword ?? false;
|
||||||
}
|
ApproveWithMyOtherDeviceEnabled = decryptOptions?.TrustedDeviceOption?.HasLoginApprovingDevice ?? false;
|
||||||
catch (Exception ex)
|
|
||||||
{
|
|
||||||
HandleException(ex);
|
|
||||||
}
|
|
||||||
|
|
||||||
try
|
|
||||||
{
|
|
||||||
ApproveWithMyOtherDeviceEnabled = await _apiService.GetDevicesExistenceByTypes(DeviceTypeExtensions.GetDesktopAndMobileTypes().ToArray());
|
|
||||||
}
|
}
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -29,6 +29,8 @@ namespace Bit.App.Pages
|
|||||||
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
|
_vm.SsoAuthSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await SsoAuthSuccessAsync());
|
||||||
_vm.UpdateTempPasswordAction =
|
_vm.UpdateTempPasswordAction =
|
||||||
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
() => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
|
_vm.StartDeviceApprovalOptionsAction =
|
||||||
|
() => Device.BeginInvokeOnMainThread(async () => await StartDeviceApprovalOptionsAsync());
|
||||||
_vm.CloseAction = async () =>
|
_vm.CloseAction = async () =>
|
||||||
{
|
{
|
||||||
await Navigation.PopModalAsync();
|
await Navigation.PopModalAsync();
|
||||||
@@ -106,15 +108,17 @@ namespace Bit.App.Pages
|
|||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private async Task StartDeviceApprovalOptionsAsync()
|
||||||
|
{
|
||||||
|
var page = new LoginApproveDevicePage();
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
private async Task SsoAuthSuccessAsync()
|
private async Task SsoAuthSuccessAsync()
|
||||||
{
|
{
|
||||||
RestoreAppOptionsFromCopy();
|
RestoreAppOptionsFromCopy();
|
||||||
await AppHelpers.ClearPreviousPage();
|
await AppHelpers.ClearPreviousPage();
|
||||||
|
|
||||||
// Just for testing the screen
|
|
||||||
Application.Current.MainPage = new NavigationPage(new LoginApproveDevicePage(_appOptions));
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (await _vaultTimeoutService.IsLockedAsync())
|
if (await _vaultTimeoutService.IsLockedAsync())
|
||||||
{
|
{
|
||||||
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions));
|
||||||
|
|||||||
@@ -9,6 +9,7 @@ using Bit.Core.Abstractions;
|
|||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
using Bit.Core.Exceptions;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
using Xamarin.Essentials;
|
using Xamarin.Essentials;
|
||||||
@@ -29,6 +30,8 @@ namespace Bit.App.Pages
|
|||||||
private readonly IStateService _stateService;
|
private readonly IStateService _stateService;
|
||||||
private readonly ILogger _logger;
|
private readonly ILogger _logger;
|
||||||
private readonly IOrganizationService _organizationService;
|
private readonly IOrganizationService _organizationService;
|
||||||
|
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
|
||||||
private string _orgIdentifier;
|
private string _orgIdentifier;
|
||||||
|
|
||||||
@@ -45,7 +48,8 @@ namespace Bit.App.Pages
|
|||||||
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
_stateService = ServiceContainer.Resolve<IStateService>("stateService");
|
||||||
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
_logger = ServiceContainer.Resolve<ILogger>("logger");
|
||||||
_organizationService = ServiceContainer.Resolve<IOrganizationService>();
|
_organizationService = ServiceContainer.Resolve<IOrganizationService>();
|
||||||
|
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||||
|
_cryptoService = ServiceContainer.Resolve<ICryptoService>();
|
||||||
|
|
||||||
PageTitle = AppResources.Bitwarden;
|
PageTitle = AppResources.Bitwarden;
|
||||||
LogInCommand = new AsyncCommand(LogInAsync, allowsMultipleExecutions: false);
|
LogInCommand = new AsyncCommand(LogInAsync, allowsMultipleExecutions: false);
|
||||||
@@ -61,6 +65,7 @@ namespace Bit.App.Pages
|
|||||||
public Action StartTwoFactorAction { get; set; }
|
public Action StartTwoFactorAction { get; set; }
|
||||||
public Action StartSetPasswordAction { get; set; }
|
public Action StartSetPasswordAction { get; set; }
|
||||||
public Action SsoAuthSuccessAction { get; set; }
|
public Action SsoAuthSuccessAction { get; set; }
|
||||||
|
public Action StartDeviceApprovalOptionsAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
|
|
||||||
@@ -197,6 +202,7 @@ namespace Bit.App.Pages
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var response = await _authService.LogInSsoAsync(code, codeVerifier, REDIRECT_URI, orgId);
|
var response = await _authService.LogInSsoAsync(code, codeVerifier, REDIRECT_URI, orgId);
|
||||||
|
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
await _stateService.SetRememberedOrgIdentifierAsync(OrgIdentifier);
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
@@ -212,9 +218,31 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
else if (decryptOptions?.TrustedDeviceOption != null)
|
||||||
|
{
|
||||||
|
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||||
|
if (!decryptOptions.HasMasterPassword &&
|
||||||
|
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||||
|
{
|
||||||
|
StartSetPasswordAction?.Invoke();
|
||||||
|
}
|
||||||
|
else if (response.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
}
|
||||||
|
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||||
|
{
|
||||||
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
|
SsoAuthSuccessAction?.Invoke();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var task = Task.Run(async () => await _syncService.FullSyncAsync(true));
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,5 +10,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<DeviceResponse> TrustDeviceIfNeededAsync();
|
Task<DeviceResponse> TrustDeviceIfNeededAsync();
|
||||||
Task<bool> GetShouldTrustDeviceAsync();
|
Task<bool> GetShouldTrustDeviceAsync();
|
||||||
Task SetShouldTrustDeviceAsync(bool value);
|
Task SetShouldTrustDeviceAsync(bool value);
|
||||||
|
Task<UserKey> DecryptUserKeyWithDeviceKeyAsync(string encryptedDevicePrivateKey, string encryptedUserKey);
|
||||||
|
Task<bool> IsDeviceTrustedAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11,11 +11,15 @@ namespace Bit.Core.Models.Domain
|
|||||||
public class TrustedDeviceOption
|
public class TrustedDeviceOption
|
||||||
{
|
{
|
||||||
public bool HasAdminApproval { get; set; }
|
public bool HasAdminApproval { get; set; }
|
||||||
|
public bool HasLoginApprovingDevice { get; set; }
|
||||||
|
public bool HasManageResetPasswordPermission { get; set; }
|
||||||
|
public string EncryptedPrivateKey { get; set; }
|
||||||
|
public string EncryptedUserKey { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public class KeyConnectorOption
|
public class KeyConnectorOption
|
||||||
{
|
{
|
||||||
public bool KeyConnectorUrl { get; set; }
|
public string KeyConnectorUrl { get; set; }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
public class DeviceResponse
|
public class DeviceResponse
|
||||||
{
|
{
|
||||||
public string Id { get; set; }
|
public string Id { get; set; }
|
||||||
public int Name { get; set; }
|
public string Name { get; set; }
|
||||||
public string Identifier { get; set; }
|
public string Identifier { get; set; }
|
||||||
public DeviceType Type { get; set; }
|
public DeviceType Type { get; set; }
|
||||||
public string CreationDate { get; set; }
|
public string CreationDate { get; set; }
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ namespace Bit.Core.Services
|
|||||||
private readonly IKeyConnectorService _keyConnectorService;
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
|
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||||
private readonly bool _setCryptoKeys;
|
private readonly bool _setCryptoKeys;
|
||||||
|
|
||||||
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
||||||
@@ -50,6 +51,7 @@ namespace Bit.Core.Services
|
|||||||
IKeyConnectorService keyConnectorService,
|
IKeyConnectorService keyConnectorService,
|
||||||
IPasswordGenerationService passwordGenerationService,
|
IPasswordGenerationService passwordGenerationService,
|
||||||
IPolicyService policyService,
|
IPolicyService policyService,
|
||||||
|
IDeviceTrustCryptoService deviceTrustCryptoService,
|
||||||
bool setCryptoKeys = true)
|
bool setCryptoKeys = true)
|
||||||
{
|
{
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
||||||
@@ -64,6 +66,7 @@ namespace Bit.Core.Services
|
|||||||
_keyConnectorService = keyConnectorService;
|
_keyConnectorService = keyConnectorService;
|
||||||
_passwordGenerationService = passwordGenerationService;
|
_passwordGenerationService = passwordGenerationService;
|
||||||
_policyService = policyService;
|
_policyService = policyService;
|
||||||
|
_deviceTrustCryptoService = deviceTrustCryptoService;
|
||||||
_setCryptoKeys = setCryptoKeys;
|
_setCryptoKeys = setCryptoKeys;
|
||||||
|
|
||||||
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
|
||||||
@@ -479,19 +482,28 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if (code == null || tokenResponse.Key != null)
|
if (code == null || tokenResponse.Key != null)
|
||||||
{
|
{
|
||||||
if (tokenResponse.KeyConnectorUrl != null)
|
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
{
|
|
||||||
await _keyConnectorService.GetAndSetKey(tokenResponse.KeyConnectorUrl);
|
|
||||||
}
|
|
||||||
|
|
||||||
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
||||||
|
|
||||||
|
if (decryptOptions?.TrustedDeviceOption != null)
|
||||||
|
{
|
||||||
|
var key = await _deviceTrustCryptoService.DecryptUserKeyWithDeviceKeyAsync(decryptOptions.TrustedDeviceOption.EncryptedPrivateKey, decryptOptions.TrustedDeviceOption.EncryptedUserKey);
|
||||||
|
if (key != null)
|
||||||
|
{
|
||||||
|
await _cryptoService.SetUserKeyAsync(key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (!string.IsNullOrEmpty(tokenResponse.KeyConnectorUrl) || !string.IsNullOrEmpty(decryptOptions?.KeyConnectorOption?.KeyConnectorUrl))
|
||||||
|
{
|
||||||
|
|
||||||
|
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
|
||||||
if (masterKey != null)
|
if (masterKey != null)
|
||||||
{
|
{
|
||||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||||
await _cryptoService.SetUserKeyAsync(userKey);
|
await _cryptoService.SetUserKeyAsync(userKey);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// User doesn't have a key pair yet (old account), let's generate one for them.
|
// User doesn't have a key pair yet (old account), let's generate one for them.
|
||||||
if (tokenResponse.PrivateKey == null)
|
if (tokenResponse.PrivateKey == null)
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ namespace Bit.Core.Services
|
|||||||
var deviceIdentifier = await _appIdService.GetAppIdAsync();
|
var deviceIdentifier = await _appIdService.GetAppIdAsync();
|
||||||
var deviceRequest = new TrustedDeviceKeysRequest
|
var deviceRequest = new TrustedDeviceKeysRequest
|
||||||
{
|
{
|
||||||
EncryptedUserKey = (await _cryptoService.RsaEncryptAsync(userKey.EncKey, devicePublicKey)).EncryptedString,
|
EncryptedUserKey = (await _cryptoService.RsaEncryptAsync(userKey.Key, devicePublicKey)).EncryptedString,
|
||||||
EncryptedPublicKey = (await _cryptoService.EncryptAsync(devicePublicKey, userKey)).EncryptedString,
|
EncryptedPublicKey = (await _cryptoService.EncryptAsync(devicePublicKey, userKey)).EncryptedString,
|
||||||
EncryptedPrivateKey = (await _cryptoService.EncryptAsync(devicePrivateKey, deviceKey)).EncryptedString,
|
EncryptedPrivateKey = (await _cryptoService.EncryptAsync(devicePrivateKey, deviceKey)).EncryptedString,
|
||||||
};
|
};
|
||||||
@@ -99,5 +99,31 @@ namespace Bit.Core.Services
|
|||||||
await SetShouldTrustDeviceAsync(false);
|
await SetShouldTrustDeviceAsync(false);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<bool> IsDeviceTrustedAsync()
|
||||||
|
{
|
||||||
|
var existingDeviceKey = await GetDeviceKeyAsync();
|
||||||
|
return existingDeviceKey != null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<UserKey> DecryptUserKeyWithDeviceKeyAsync(string encryptedDevicePrivateKey, string encryptedUserKey)
|
||||||
|
{
|
||||||
|
var existingDeviceKey = await GetDeviceKeyAsync();
|
||||||
|
if (existingDeviceKey == null)
|
||||||
|
{
|
||||||
|
// User doesn't have a device key anymore so device is untrusted
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Attempt to decrypt encryptedDevicePrivateKey with device key
|
||||||
|
var devicePrivateKeyBytes = await _cryptoService.DecryptToBytesAsync(
|
||||||
|
new EncString(encryptedDevicePrivateKey),
|
||||||
|
existingDeviceKey
|
||||||
|
);
|
||||||
|
|
||||||
|
// Attempt to decrypt encryptedUserDataKey with devicePrivateKey
|
||||||
|
var userKeyBytes = await _cryptoService.RsaDecryptAsync(encryptedUserKey, devicePrivateKeyBytes);
|
||||||
|
return new UserKey(userKeyBytes);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -515,13 +515,21 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
|
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var deviceKeyB64 = await _storageMediatorService.GetAsync<string>(Constants.DeviceKeyKey(userId), true);
|
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));
|
return new SymmetricCryptoKey(Convert.FromBase64String(deviceKeyB64));
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
|
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
|
||||||
{
|
{
|
||||||
await _storageMediatorService.SaveAsync(Constants.DeviceKeyKey(userId), value.KeyB64, true);
|
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)
|
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
|
||||||
|
|||||||
@@ -77,9 +77,10 @@ namespace Bit.Core.Utilities
|
|||||||
});
|
});
|
||||||
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
|
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
|
||||||
var totpService = new TotpService(cryptoFunctionService);
|
var totpService = new TotpService(cryptoFunctionService);
|
||||||
|
var deviceTrustCryptoService = new DeviceTrustCryptoService(apiService, appIdService, cryptoFunctionService, cryptoService, stateService);
|
||||||
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
|
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
|
||||||
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
|
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
|
||||||
keyConnectorService, passwordGenerationService, policyService);
|
keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService);
|
||||||
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
var exportService = new ExportService(folderService, cipherService, cryptoService);
|
||||||
var auditService = new AuditService(cryptoFunctionService, apiService);
|
var auditService = new AuditService(cryptoFunctionService, apiService);
|
||||||
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
|
||||||
@@ -88,7 +89,6 @@ namespace Bit.Core.Utilities
|
|||||||
cryptoService);
|
cryptoService);
|
||||||
var usernameGenerationService = new UsernameGenerationService(cryptoService, apiService, stateService);
|
var usernameGenerationService = new UsernameGenerationService(cryptoService, apiService, stateService);
|
||||||
var configService = new ConfigService(apiService, stateService, logger);
|
var configService = new ConfigService(apiService, stateService, logger);
|
||||||
var deviceTrustCryptoService = new DeviceTrustCryptoService(apiService, appIdService, cryptoFunctionService, cryptoService, stateService);
|
|
||||||
|
|
||||||
Register<IConditionedAwaiterManager>(conditionedRunner);
|
Register<IConditionedAwaiterManager>(conditionedRunner);
|
||||||
Register<ITokenService>("tokenService", tokenService);
|
Register<ITokenService>("tokenService", tokenService);
|
||||||
|
|||||||
@@ -498,7 +498,7 @@ namespace Bit.iOS.Autofill
|
|||||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
||||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(email));
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, email));
|
||||||
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
||||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -511,11 +511,11 @@ namespace Bit.iOS.Autofill
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(string email = null)
|
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null)
|
||||||
{
|
{
|
||||||
var appOptions = new AppOptions { IosExtension = true };
|
var appOptions = new AppOptions { IosExtension = true };
|
||||||
var app = new App.App(appOptions);
|
var app = new App.App(appOptions);
|
||||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, appOptions);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, appOptions);
|
||||||
ThemeManager.SetTheme(app.Resources);
|
ThemeManager.SetTheme(app.Resources);
|
||||||
ThemeManager.ApplyResourcesTo(loginWithDevicePage);
|
ThemeManager.ApplyResourcesTo(loginWithDevicePage);
|
||||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||||
@@ -545,6 +545,7 @@ namespace Bit.iOS.Autofill
|
|||||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
|
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
|
||||||
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
||||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||||
|
vm.StartDeviceApprovalOptionsAction = () => DismissViewController(false, () => LaunchDeviceApprovalOptionsFlow());
|
||||||
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
||||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -621,6 +622,26 @@ namespace Bit.iOS.Autofill
|
|||||||
PresentViewController(updateTempPasswordController, true, null);
|
PresentViewController(updateTempPasswordController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LaunchDeviceApprovalOptionsFlow()
|
||||||
|
{
|
||||||
|
var loginApproveDevicePage = new LoginApproveDevicePage();
|
||||||
|
var app = new App.App(new AppOptions { IosExtension = true });
|
||||||
|
ThemeManager.SetTheme(app.Resources);
|
||||||
|
ThemeManager.ApplyResourcesTo(loginApproveDevicePage);
|
||||||
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
|
{
|
||||||
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
|
}
|
||||||
|
|
||||||
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
var loginApproveDeviceController = navigationPage.CreateViewController();
|
||||||
|
loginApproveDeviceController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||||
|
PresentViewController(loginApproveDeviceController, true, null);
|
||||||
|
}
|
||||||
|
|
||||||
public Task SetPreviousPageInfoAsync() => Task.CompletedTask;
|
public Task SetPreviousPageInfoAsync() => Task.CompletedTask;
|
||||||
public Task UpdateThemeAsync() => Task.CompletedTask;
|
public Task UpdateThemeAsync() => Task.CompletedTask;
|
||||||
|
|
||||||
|
|||||||
@@ -520,7 +520,7 @@ namespace Bit.iOS.Extension
|
|||||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(false));
|
||||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||||
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
vm.StartSsoLoginAction = () => DismissViewController(false, () => LaunchLoginSsoFlow());
|
||||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(email));
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, email));
|
||||||
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
vm.LogInSuccessAction = () => DismissLockAndContinue();
|
||||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -533,11 +533,11 @@ namespace Bit.iOS.Extension
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(string email = null)
|
private void LaunchLoginWithDevice(AuthRequestType authRequestType,string email = null)
|
||||||
{
|
{
|
||||||
var appOptions = new AppOptions { IosExtension = true };
|
var appOptions = new AppOptions { IosExtension = true };
|
||||||
var app = new App.App(appOptions);
|
var app = new App.App(appOptions);
|
||||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, appOptions);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, appOptions);
|
||||||
ThemeManager.SetTheme(app.Resources);
|
ThemeManager.SetTheme(app.Resources);
|
||||||
ThemeManager.ApplyResourcesTo(loginWithDevicePage);
|
ThemeManager.ApplyResourcesTo(loginWithDevicePage);
|
||||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||||
@@ -567,6 +567,7 @@ namespace Bit.iOS.Extension
|
|||||||
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
|
vm.StartTwoFactorAction = () => DismissViewController(false, () => LaunchTwoFactorFlow(true));
|
||||||
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
vm.StartSetPasswordAction = () => DismissViewController(false, () => LaunchSetPasswordFlow());
|
||||||
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissViewController(false, () => LaunchUpdateTempPasswordFlow());
|
||||||
|
vm.StartDeviceApprovalOptionsAction = () => DismissViewController(false, () => LaunchDeviceApprovalOptionsFlow());
|
||||||
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
||||||
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -642,5 +643,25 @@ namespace Bit.iOS.Extension
|
|||||||
updateTempPasswordController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
updateTempPasswordController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||||
PresentViewController(updateTempPasswordController, true, null);
|
PresentViewController(updateTempPasswordController, true, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LaunchDeviceApprovalOptionsFlow()
|
||||||
|
{
|
||||||
|
var loginApproveDevicePage = new LoginApproveDevicePage();
|
||||||
|
var app = new App.App(new AppOptions { IosExtension = true });
|
||||||
|
ThemeManager.SetTheme(app.Resources);
|
||||||
|
ThemeManager.ApplyResourcesTo(loginApproveDevicePage);
|
||||||
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
|
{
|
||||||
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
|
}
|
||||||
|
|
||||||
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
var loginApproveDeviceController = navigationPage.CreateViewController();
|
||||||
|
loginApproveDeviceController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||||
|
PresentViewController(loginApproveDeviceController, true, null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ namespace Bit.iOS.ShareExtension
|
|||||||
vm.StartTwoFactorAction = () => DismissAndLaunch(() => LaunchTwoFactorFlow(false));
|
vm.StartTwoFactorAction = () => DismissAndLaunch(() => LaunchTwoFactorFlow(false));
|
||||||
vm.UpdateTempPasswordAction = () => DismissAndLaunch(() => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissAndLaunch(() => LaunchUpdateTempPasswordFlow());
|
||||||
vm.StartSsoLoginAction = () => DismissAndLaunch(() => LaunchLoginSsoFlow());
|
vm.StartSsoLoginAction = () => DismissAndLaunch(() => LaunchLoginSsoFlow());
|
||||||
vm.LogInWithDeviceAction = () => DismissAndLaunch(() => LaunchLoginWithDevice(email));
|
vm.LogInWithDeviceAction = () => DismissAndLaunch(() => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, email));
|
||||||
vm.LogInSuccessAction = () => { DismissLockAndContinue(); };
|
vm.LogInSuccessAction = () => { DismissLockAndContinue(); };
|
||||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -348,9 +348,9 @@ namespace Bit.iOS.ShareExtension
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(string email = null)
|
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null)
|
||||||
{
|
{
|
||||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, _appOptions.Value);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, _appOptions.Value);
|
||||||
SetupAppAndApplyResources(loginWithDevicePage);
|
SetupAppAndApplyResources(loginWithDevicePage);
|
||||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||||
{
|
{
|
||||||
@@ -373,6 +373,7 @@ namespace Bit.iOS.ShareExtension
|
|||||||
vm.StartTwoFactorAction = () => DismissAndLaunch(() => LaunchTwoFactorFlow(true));
|
vm.StartTwoFactorAction = () => DismissAndLaunch(() => LaunchTwoFactorFlow(true));
|
||||||
vm.StartSetPasswordAction = () => DismissAndLaunch(() => LaunchSetPasswordFlow());
|
vm.StartSetPasswordAction = () => DismissAndLaunch(() => LaunchSetPasswordFlow());
|
||||||
vm.UpdateTempPasswordAction = () => DismissAndLaunch(() => LaunchUpdateTempPasswordFlow());
|
vm.UpdateTempPasswordAction = () => DismissAndLaunch(() => LaunchUpdateTempPasswordFlow());
|
||||||
|
vm.StartDeviceApprovalOptionsAction = () => DismissViewController(false, () => LaunchDeviceApprovalOptionsFlow());
|
||||||
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
vm.SsoAuthSuccessAction = () => DismissLockAndContinue();
|
||||||
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
vm.CloseAction = () => DismissAndLaunch(() => LaunchHomePage());
|
||||||
}
|
}
|
||||||
@@ -427,6 +428,26 @@ namespace Bit.iOS.ShareExtension
|
|||||||
NavigateToPage(updateTempPasswordPage);
|
NavigateToPage(updateTempPasswordPage);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void LaunchDeviceApprovalOptionsFlow()
|
||||||
|
{
|
||||||
|
var loginApproveDevicePage = new LoginApproveDevicePage();
|
||||||
|
var app = new App.App(new AppOptions { IosExtension = true });
|
||||||
|
ThemeManager.SetTheme(app.Resources);
|
||||||
|
ThemeManager.ApplyResourcesTo(loginApproveDevicePage);
|
||||||
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
|
{
|
||||||
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
|
vm.CloseAction = () => DismissViewController(false, () => LaunchHomePage());
|
||||||
|
}
|
||||||
|
|
||||||
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
var loginApproveDeviceController = navigationPage.CreateViewController();
|
||||||
|
loginApproveDeviceController.ModalPresentationStyle = UIModalPresentationStyle.FullScreen;
|
||||||
|
PresentViewController(loginApproveDeviceController, true, null);
|
||||||
|
}
|
||||||
|
|
||||||
public void Navigate(NavigationTarget navTarget, INavigationParams navParams = null)
|
public void Navigate(NavigationTarget navTarget, INavigationParams navParams = null)
|
||||||
{
|
{
|
||||||
if (ExtNavigationController?.ViewControllers?.Any() ?? false)
|
if (ExtNavigationController?.ViewControllers?.Any() ?? false)
|
||||||
|
|||||||
Reference in New Issue
Block a user