mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
Compare commits
19 Commits
v2024.5.1
...
feature/pm
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
2014d7f562 | ||
|
|
8a399235f4 | ||
|
|
ce55750e60 | ||
|
|
a15269bafe | ||
|
|
8a59e17fc9 | ||
|
|
548bd12a8e | ||
|
|
58542fd255 | ||
|
|
fc300f3e3f | ||
|
|
800b4c71de | ||
|
|
7bcf1c377f | ||
|
|
3053eaa036 | ||
|
|
109a84607a | ||
|
|
d2b6c73a75 | ||
|
|
9dc6a725cf | ||
|
|
6268f0776b | ||
|
|
cbbc41be67 | ||
|
|
e164fb9823 | ||
|
|
87866304a6 | ||
|
|
84a82f0876 |
@@ -33,7 +33,7 @@ namespace Bit.App.Pages
|
|||||||
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
|
private readonly WeakEventManager<int?> _secretEntryFocusWeakEventManager = new WeakEventManager<int?>();
|
||||||
private readonly IPolicyService _policyService;
|
private readonly IPolicyService _policyService;
|
||||||
private readonly IPasswordGenerationService _passwordGenerationService;
|
private readonly IPasswordGenerationService _passwordGenerationService;
|
||||||
|
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||||
private string _email;
|
private string _email;
|
||||||
private string _masterPassword;
|
private string _masterPassword;
|
||||||
private string _pin;
|
private string _pin;
|
||||||
@@ -65,6 +65,7 @@ namespace Bit.App.Pages
|
|||||||
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
_watchDeviceService = ServiceContainer.Resolve<IWatchDeviceService>();
|
||||||
_policyService = ServiceContainer.Resolve<IPolicyService>();
|
_policyService = ServiceContainer.Resolve<IPolicyService>();
|
||||||
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
|
_passwordGenerationService = ServiceContainer.Resolve<IPasswordGenerationService>();
|
||||||
|
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||||
|
|
||||||
PageTitle = AppResources.VerifyMasterPassword;
|
PageTitle = AppResources.VerifyMasterPassword;
|
||||||
TogglePasswordCommand = new Command(TogglePassword);
|
TogglePasswordCommand = new Command(TogglePassword);
|
||||||
@@ -454,6 +455,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _cryptoService.SetKeyAsync(key);
|
await _cryptoService.SetKeyAsync(key);
|
||||||
}
|
}
|
||||||
|
if (await _deviceTrustCryptoService.GetUserTrustDeviceChoiceForDecryptionAsync())
|
||||||
|
{
|
||||||
|
await _deviceTrustCryptoService.TrustDeviceAsync();
|
||||||
|
await _deviceTrustCryptoService.SetUserTrustDeviceChoiceForDecryptionAsync(false);
|
||||||
|
}
|
||||||
await DoContinueAsync();
|
await DoContinueAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
76
src/App/Pages/Accounts/LoginApproveDevicePage.xaml
Normal file
76
src/App/Pages/Accounts/LoginApproveDevicePage.xaml
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8" ?>
|
||||||
|
<pages:BaseContentPage
|
||||||
|
xmlns="http://xamarin.com/schemas/2014/forms"
|
||||||
|
xmlns:x="http://schemas.microsoft.com/winfx/2009/xaml"
|
||||||
|
x:Class="Bit.App.Pages.LoginApproveDevicePage"
|
||||||
|
xmlns:pages="clr-namespace:Bit.App.Pages"
|
||||||
|
xmlns:u="clr-namespace:Bit.App.Utilities"
|
||||||
|
x:DataType="pages:LoginApproveDeviceViewModel"
|
||||||
|
x:Name="_page"
|
||||||
|
Title="{Binding PageTitle}">
|
||||||
|
|
||||||
|
<ContentPage.BindingContext>
|
||||||
|
<pages:LoginApproveDeviceViewModel />
|
||||||
|
</ContentPage.BindingContext>
|
||||||
|
|
||||||
|
<StackLayout Padding="10, 10">
|
||||||
|
<StackLayout Padding="5, 10" Orientation="Horizontal">
|
||||||
|
<StackLayout HorizontalOptions="FillAndExpand">
|
||||||
|
<Label
|
||||||
|
StyleClass="text-md"
|
||||||
|
Text="{u:I18n RememberThisDevice}"/>
|
||||||
|
<Label
|
||||||
|
StyleClass="box-sub-label"
|
||||||
|
Text="{u:I18n TurnOffUsingPublicDevice}"/>
|
||||||
|
</StackLayout>
|
||||||
|
<Switch
|
||||||
|
Scale="0.8"
|
||||||
|
IsToggled="{Binding RememberThisDevice}"
|
||||||
|
VerticalOptions="Center"/>
|
||||||
|
</StackLayout>
|
||||||
|
<StackLayout Margin="0, 20, 0, 0">
|
||||||
|
<Button
|
||||||
|
x:Name="_continue"
|
||||||
|
Text="{u:I18n Continue}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Command="{Binding ContinueCommand}"
|
||||||
|
IsVisible="{Binding ContinueEnabled}"/>
|
||||||
|
<Button
|
||||||
|
x:Name="_approveWithMyOtherDevice"
|
||||||
|
Text="{u:I18n ApproveWithMyOtherDevice}"
|
||||||
|
StyleClass="btn-primary"
|
||||||
|
Command="{Binding ApproveWithMyOtherDeviceCommand}"
|
||||||
|
IsVisible="{Binding ApproveWithMyOtherDeviceEnabled}"/>
|
||||||
|
<Button
|
||||||
|
x:Name="_requestAdminApproval"
|
||||||
|
Text="{u:I18n RequestAdminApproval}"
|
||||||
|
StyleClass="box-button-row"
|
||||||
|
Command="{Binding RequestAdminApprovalCommand}"
|
||||||
|
IsVisible="{Binding RequestAdminApprovalEnabled}"/>
|
||||||
|
<Button
|
||||||
|
x:Name="_approveWithMasterPassword"
|
||||||
|
Text="{u:I18n ApproveWithMasterPassword}"
|
||||||
|
StyleClass="box-button-row"
|
||||||
|
Command="{Binding ApproveWithMasterPasswordCommand}"
|
||||||
|
IsVisible="{Binding ApproveWithMasterPasswordEnabled}"/>
|
||||||
|
<Label
|
||||||
|
Text="{Binding LoggingInAsText}"
|
||||||
|
StyleClass="text-sm"
|
||||||
|
Margin="0,40,0,0"
|
||||||
|
AutomationId="LoggingInAsLabel"
|
||||||
|
/>
|
||||||
|
<Label
|
||||||
|
Text="{u:I18n NotYou}"
|
||||||
|
StyleClass="text-md"
|
||||||
|
HorizontalOptions="Start"
|
||||||
|
TextColor="{DynamicResource HyperlinkColor}"
|
||||||
|
AutomationId="NotYouLabel">
|
||||||
|
<Label.GestureRecognizers>
|
||||||
|
<TapGestureRecognizer Tapped="Cancel_Clicked" />
|
||||||
|
</Label.GestureRecognizers>
|
||||||
|
</Label>
|
||||||
|
</StackLayout>
|
||||||
|
</StackLayout>
|
||||||
|
</pages:BaseContentPage>
|
||||||
|
|
||||||
|
|
||||||
62
src/App/Pages/Accounts/LoginApproveDevicePage.xaml.cs
Normal file
62
src/App/Pages/Accounts/LoginApproveDevicePage.xaml.cs
Normal file
@@ -0,0 +1,62 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.App.Models;
|
||||||
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public partial class LoginApproveDevicePage : BaseContentPage
|
||||||
|
{
|
||||||
|
|
||||||
|
private readonly LoginApproveDeviceViewModel _vm;
|
||||||
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
|
public LoginApproveDevicePage(AppOptions appOptions = null)
|
||||||
|
{
|
||||||
|
InitializeComponent();
|
||||||
|
_vm = BindingContext as LoginApproveDeviceViewModel;
|
||||||
|
_vm.LogInWithMasterPassword = () => StartLogInWithMasterPassword().FireAndForget();
|
||||||
|
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
|
||||||
|
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
|
||||||
|
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
|
||||||
|
_vm.Page = this;
|
||||||
|
_appOptions = appOptions;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void OnAppearing()
|
||||||
|
{
|
||||||
|
_vm.InitAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void Cancel_Clicked(object sender, EventArgs e)
|
||||||
|
{
|
||||||
|
if (DoOnce())
|
||||||
|
{
|
||||||
|
_vm.CloseAction();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartLogInWithMasterPassword()
|
||||||
|
{
|
||||||
|
var page = new LockPage(_appOptions);
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task StartLoginWithDeviceAsync()
|
||||||
|
{
|
||||||
|
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task RequestAdminApprovalAsync()
|
||||||
|
{
|
||||||
|
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AdminApproval, _appOptions);
|
||||||
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
140
src/App/Pages/Accounts/LoginApproveDeviceViewModel.cs
Normal file
140
src/App/Pages/Accounts/LoginApproveDeviceViewModel.cs
Normal file
@@ -0,0 +1,140 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using System.Windows.Input;
|
||||||
|
using Bit.App.Abstractions;
|
||||||
|
using Bit.App.Resources;
|
||||||
|
using Bit.App.Utilities.AccountManagement;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
using Bit.Core.Services;
|
||||||
|
using Bit.Core.Utilities;
|
||||||
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
|
using Xamarin.Essentials;
|
||||||
|
using Xamarin.Forms;
|
||||||
|
|
||||||
|
namespace Bit.App.Pages
|
||||||
|
{
|
||||||
|
public class LoginApproveDeviceViewModel : BaseViewModel
|
||||||
|
{
|
||||||
|
private bool _rememberThisDevice;
|
||||||
|
private bool _approveWithMyOtherDeviceEnabled;
|
||||||
|
private bool _requestAdminApprovalEnabled;
|
||||||
|
private bool _approveWithMasterPasswordEnabled;
|
||||||
|
private bool _continueEnabled;
|
||||||
|
private string _email;
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||||
|
|
||||||
|
public ICommand ApproveWithMyOtherDeviceCommand { get; }
|
||||||
|
public ICommand RequestAdminApprovalCommand { get; }
|
||||||
|
public ICommand ApproveWithMasterPasswordCommand { get; }
|
||||||
|
public ICommand ContinueCommand { get; }
|
||||||
|
|
||||||
|
public Action LogInWithMasterPassword { get; set; }
|
||||||
|
public Action LogInWithDeviceAction { get; set; }
|
||||||
|
public Action RequestAdminApprovalAction { get; set; }
|
||||||
|
public Action CloseAction { get; set; }
|
||||||
|
|
||||||
|
public LoginApproveDeviceViewModel()
|
||||||
|
{
|
||||||
|
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||||
|
_apiService = ServiceContainer.Resolve<IApiService>();
|
||||||
|
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||||
|
|
||||||
|
PageTitle = AppResources.LoggedIn;
|
||||||
|
|
||||||
|
ApproveWithMyOtherDeviceCommand = new AsyncCommand(() => CheckDeviceTrustAndInvoke(LogInWithDeviceAction),
|
||||||
|
onException: ex => HandleException(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
RequestAdminApprovalCommand = new AsyncCommand(() => CheckDeviceTrustAndInvoke(RequestAdminApprovalAction),
|
||||||
|
onException: ex => HandleException(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
ApproveWithMasterPasswordCommand = new AsyncCommand(() => CheckDeviceTrustAndInvoke(LogInWithMasterPassword),
|
||||||
|
onException: ex => HandleException(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
|
||||||
|
ContinueCommand = new AsyncCommand(InitAsync,
|
||||||
|
onException: ex => HandleException(ex),
|
||||||
|
allowsMultipleExecutions: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string LoggingInAsText => string.Format(AppResources.LoggingInAsX, Email);
|
||||||
|
|
||||||
|
public bool RememberThisDevice
|
||||||
|
{
|
||||||
|
get => _rememberThisDevice;
|
||||||
|
set => SetProperty(ref _rememberThisDevice, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApproveWithMyOtherDeviceEnabled
|
||||||
|
{
|
||||||
|
get => _approveWithMyOtherDeviceEnabled;
|
||||||
|
set => SetProperty(ref _approveWithMyOtherDeviceEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RequestAdminApprovalEnabled
|
||||||
|
{
|
||||||
|
get => _requestAdminApprovalEnabled;
|
||||||
|
set => SetProperty(ref _requestAdminApprovalEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ApproveWithMasterPasswordEnabled
|
||||||
|
{
|
||||||
|
get => _approveWithMasterPasswordEnabled;
|
||||||
|
set => SetProperty(ref _approveWithMasterPasswordEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ContinueEnabled
|
||||||
|
{
|
||||||
|
get => _continueEnabled;
|
||||||
|
set => SetProperty(ref _continueEnabled, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Email
|
||||||
|
{
|
||||||
|
get => _email;
|
||||||
|
set => SetProperty(ref _email, value, additionalPropertyNames:
|
||||||
|
new string[] {
|
||||||
|
nameof(LoggingInAsText)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task InitAsync()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Email = await _stateService.GetRememberedEmailAsync();
|
||||||
|
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
|
RequestAdminApprovalEnabled = decryptOptions != null && decryptOptions.TrustedDeviceOption != null && decryptOptions.TrustedDeviceOption.HasAdminApproval;
|
||||||
|
ApproveWithMasterPasswordEnabled = decryptOptions != null && decryptOptions.HasMasterPassword;
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
try
|
||||||
|
{
|
||||||
|
ApproveWithMyOtherDeviceEnabled = await _apiService.GetDevicesExistenceByTypes(DeviceTypeExtensions.GetDesktopAndMobileTypes().ToArray());
|
||||||
|
}
|
||||||
|
catch (Exception ex)
|
||||||
|
{
|
||||||
|
HandleException(ex);
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Change this expression to, Appear if the browser is trusted and shared the key with the app
|
||||||
|
ContinueEnabled = !RequestAdminApprovalEnabled && !ApproveWithMasterPasswordEnabled && !ApproveWithMyOtherDeviceEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task CheckDeviceTrustAndInvoke(Action action)
|
||||||
|
{
|
||||||
|
await _deviceTrustCryptoService.SetUserTrustDeviceChoiceForDecryptionAsync(RememberThisDevice);
|
||||||
|
await Device.InvokeOnMainThreadAsync(action);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -4,6 +4,7 @@ using Bit.App.Models;
|
|||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.Core;
|
using Bit.Core;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
using Xamarin.CommunityToolkit.ObjectModel;
|
using Xamarin.CommunityToolkit.ObjectModel;
|
||||||
@@ -135,7 +136,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task StartLoginWithDeviceAsync()
|
private async Task StartLoginWithDeviceAsync()
|
||||||
{
|
{
|
||||||
var page = new LoginPasswordlessRequestPage(_vm.Email, _appOptions);
|
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,17 +21,17 @@
|
|||||||
<StackLayout
|
<StackLayout
|
||||||
Padding="7, 0, 7, 20">
|
Padding="7, 0, 7, 20">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n LogInInitiated}"
|
Text="{Binding Tittle}"
|
||||||
FontSize="Title"
|
FontSize="Title"
|
||||||
FontAttributes="Bold"
|
FontAttributes="Bold"
|
||||||
Margin="0,14,0,21"
|
Margin="0,14,0,21"
|
||||||
AutomationId="LogInInitiatedLabel" />
|
AutomationId="LogInInitiatedLabel" />
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n ANotificationHasBeenSentToYourDevice}"
|
Text="{Binding SubTittle}"
|
||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
Margin="0,0,0,10"/>
|
Margin="0,0,0,10"/>
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice}"
|
Text="{Binding Description}"
|
||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
Margin="0,0,0,24"/>
|
Margin="0,0,0,24"/>
|
||||||
<Label
|
<Label
|
||||||
@@ -45,6 +45,7 @@
|
|||||||
AutomationId="FingerprintPhraseValue" />
|
AutomationId="FingerprintPhraseValue" />
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n ResendNotification}"
|
Text="{u:I18n ResendNotification}"
|
||||||
|
IsVisible="{Binding ResendNotificationVisible}"
|
||||||
StyleClass="text-md"
|
StyleClass="text-md"
|
||||||
HorizontalOptions="Start"
|
HorizontalOptions="Start"
|
||||||
Margin="0,40,0,0"
|
Margin="0,40,0,0"
|
||||||
@@ -58,7 +59,7 @@
|
|||||||
Orientation="Horizontal"
|
Orientation="Horizontal"
|
||||||
Margin="0,30,0,0">
|
Margin="0,30,0,0">
|
||||||
<Label
|
<Label
|
||||||
Text="{u:I18n NeedAnotherOption}"
|
Text="{Binding OtherOptions}"
|
||||||
FontSize="Small"
|
FontSize="Small"
|
||||||
VerticalTextAlignment="End"/>
|
VerticalTextAlignment="End"/>
|
||||||
<Label
|
<Label
|
||||||
|
|||||||
@@ -3,6 +3,7 @@ using System.Collections.Generic;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Xamarin.Forms;
|
using Xamarin.Forms;
|
||||||
|
|
||||||
namespace Bit.App.Pages
|
namespace Bit.App.Pages
|
||||||
@@ -12,13 +13,14 @@ namespace Bit.App.Pages
|
|||||||
private LoginPasswordlessRequestViewModel _vm;
|
private LoginPasswordlessRequestViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
public LoginPasswordlessRequestPage(string email, AppOptions appOptions = null)
|
public LoginPasswordlessRequestPage(string email, AuthRequestType authRequestType, AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
_vm = BindingContext as LoginPasswordlessRequestViewModel;
|
_vm = BindingContext as LoginPasswordlessRequestViewModel;
|
||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.Email = email;
|
_vm.Email = email;
|
||||||
|
_vm.AuthRequestType = authRequestType;
|
||||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||||
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using System;
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Linq;
|
using System.Linq;
|
||||||
|
using System.Net.Http;
|
||||||
using System.Text;
|
using System.Text;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
@@ -32,6 +33,7 @@ namespace Bit.App.Pages
|
|||||||
private IPlatformUtilsService _platformUtilsService;
|
private IPlatformUtilsService _platformUtilsService;
|
||||||
private IEnvironmentService _environmentService;
|
private IEnvironmentService _environmentService;
|
||||||
private ILogger _logger;
|
private ILogger _logger;
|
||||||
|
private IDeviceTrustCryptoService _deviceTrustCryptoService;
|
||||||
|
|
||||||
protected override II18nService i18nService => _i18nService;
|
protected override II18nService i18nService => _i18nService;
|
||||||
protected override IEnvironmentService environmentService => _environmentService;
|
protected override IEnvironmentService environmentService => _environmentService;
|
||||||
@@ -44,6 +46,7 @@ namespace Bit.App.Pages
|
|||||||
private string _email;
|
private string _email;
|
||||||
private string _requestId;
|
private string _requestId;
|
||||||
private string _requestAccessCode;
|
private string _requestAccessCode;
|
||||||
|
private AuthRequestType _authRequestType;
|
||||||
// Item1 publicKey, Item2 privateKey
|
// Item1 publicKey, Item2 privateKey
|
||||||
private Tuple<byte[], byte[]> _requestKeyPair;
|
private Tuple<byte[], byte[]> _requestKeyPair;
|
||||||
|
|
||||||
@@ -57,6 +60,7 @@ namespace Bit.App.Pages
|
|||||||
_i18nService = ServiceContainer.Resolve<II18nService>();
|
_i18nService = ServiceContainer.Resolve<II18nService>();
|
||||||
_stateService = ServiceContainer.Resolve<IStateService>();
|
_stateService = ServiceContainer.Resolve<IStateService>();
|
||||||
_logger = ServiceContainer.Resolve<ILogger>();
|
_logger = ServiceContainer.Resolve<ILogger>();
|
||||||
|
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
|
||||||
|
|
||||||
PageTitle = AppResources.LogInWithAnotherDevice;
|
PageTitle = AppResources.LogInWithAnotherDevice;
|
||||||
|
|
||||||
@@ -77,6 +81,70 @@ namespace Bit.App.Pages
|
|||||||
public ICommand CreatePasswordlessLoginCommand { get; }
|
public ICommand CreatePasswordlessLoginCommand { get; }
|
||||||
public ICommand CloseCommand { get; }
|
public ICommand CloseCommand { get; }
|
||||||
|
|
||||||
|
public string Tittle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_authRequestType)
|
||||||
|
{
|
||||||
|
case AuthRequestType.AuthenticateAndUnlock:
|
||||||
|
return AppResources.LogInInitiated;
|
||||||
|
case AuthRequestType.AdminApproval:
|
||||||
|
return AppResources.AdminApprovalRequested;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string SubTittle
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_authRequestType)
|
||||||
|
{
|
||||||
|
case AuthRequestType.AuthenticateAndUnlock:
|
||||||
|
return AppResources.ANotificationHasBeenSentToYourDevice;
|
||||||
|
case AuthRequestType.AdminApproval:
|
||||||
|
return AppResources.YourRequestHasBeenSentToYourAdmin;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string Description
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_authRequestType)
|
||||||
|
{
|
||||||
|
case AuthRequestType.AuthenticateAndUnlock:
|
||||||
|
return AppResources.PleaseMakeSureYourVaultIsUnlockedAndTheFingerprintPhraseMatchesOnTheOtherDevice;
|
||||||
|
case AuthRequestType.AdminApproval:
|
||||||
|
return AppResources.YouWillBeNotifiedOnceApproved;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public string OtherOptions
|
||||||
|
{
|
||||||
|
get
|
||||||
|
{
|
||||||
|
switch (_authRequestType)
|
||||||
|
{
|
||||||
|
case AuthRequestType.AuthenticateAndUnlock:
|
||||||
|
return AppResources.NeedAnotherOption;
|
||||||
|
case AuthRequestType.AdminApproval:
|
||||||
|
return AppResources.TroubleLoggingIn;
|
||||||
|
default:
|
||||||
|
return string.Empty;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public string FingerprintPhrase
|
public string FingerprintPhrase
|
||||||
{
|
{
|
||||||
get => _fingerprintPhrase;
|
get => _fingerprintPhrase;
|
||||||
@@ -89,6 +157,21 @@ namespace Bit.App.Pages
|
|||||||
set => SetProperty(ref _email, value);
|
set => SetProperty(ref _email, value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public AuthRequestType AuthRequestType
|
||||||
|
{
|
||||||
|
get => _authRequestType;
|
||||||
|
set => SetProperty(ref _authRequestType, value, additionalPropertyNames: new string[]
|
||||||
|
{
|
||||||
|
nameof(Tittle),
|
||||||
|
nameof(SubTittle),
|
||||||
|
nameof(Description),
|
||||||
|
nameof(OtherOptions),
|
||||||
|
nameof(ResendNotificationVisible)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool ResendNotificationVisible => AuthRequestType == AuthRequestType.AuthenticateAndUnlock;
|
||||||
|
|
||||||
public void StartCheckLoginRequestStatus()
|
public void StartCheckLoginRequestStatus()
|
||||||
{
|
{
|
||||||
try
|
try
|
||||||
@@ -154,6 +237,11 @@ namespace Bit.App.Pages
|
|||||||
else
|
else
|
||||||
{
|
{
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
|
if (await _deviceTrustCryptoService.GetUserTrustDeviceChoiceForDecryptionAsync())
|
||||||
|
{
|
||||||
|
await _deviceTrustCryptoService.TrustDeviceAsync();
|
||||||
|
await _deviceTrustCryptoService.SetUserTrustDeviceChoiceForDecryptionAsync(false);
|
||||||
|
}
|
||||||
LogInSuccessAction?.Invoke();
|
LogInSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -168,7 +256,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
|
await Device.InvokeOnMainThreadAsync(() => _deviceActionService.ShowLoadingAsync(AppResources.Loading));
|
||||||
|
|
||||||
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email);
|
var response = await _authService.PasswordlessCreateLoginRequestAsync(_email, AuthRequestType);
|
||||||
if (response != null)
|
if (response != null)
|
||||||
{
|
{
|
||||||
FingerprintPhrase = response.FingerprintPhrase;
|
FingerprintPhrase = response.FingerprintPhrase;
|
||||||
|
|||||||
@@ -110,6 +110,11 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
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));
|
||||||
|
|||||||
99
src/App/Resources/AppResources.Designer.cs
generated
99
src/App/Resources/AppResources.Designer.cs
generated
@@ -418,6 +418,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Admin approval requested.
|
||||||
|
/// </summary>
|
||||||
|
public static string AdminApprovalRequested {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("AdminApprovalRequested", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to All.
|
/// Looks up a localized string similar to All.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -562,6 +571,24 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Approve with master password.
|
||||||
|
/// </summary>
|
||||||
|
public static string ApproveWithMasterPassword {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ApproveWithMasterPassword", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Approve with my other device.
|
||||||
|
/// </summary>
|
||||||
|
public static string ApproveWithMyOtherDevice {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("ApproveWithMyOtherDevice", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to April.
|
/// Looks up a localized string similar to April.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3586,6 +3613,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Logged in!.
|
||||||
|
/// </summary>
|
||||||
|
public static string LoggedIn {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LoggedIn", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Logged in as {0} on {1}..
|
/// Looks up a localized string similar to Logged in as {0} on {1}..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -3604,6 +3640,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Logging in as {0}.
|
||||||
|
/// </summary>
|
||||||
|
public static string LoggingInAsX {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("LoggingInAsX", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Logging in as {0} on {1}.
|
/// Looks up a localized string similar to Logging in as {0} on {1}.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -5192,6 +5237,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Remember this device.
|
||||||
|
/// </summary>
|
||||||
|
public static string RememberThisDevice {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RememberThisDevice", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Remove.
|
/// Looks up a localized string similar to Remove.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -5264,6 +5318,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Request admin approval.
|
||||||
|
/// </summary>
|
||||||
|
public static string RequestAdminApproval {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("RequestAdminApproval", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Request one-time password.
|
/// Looks up a localized string similar to Request one-time password.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -6254,6 +6317,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Trouble logging in?.
|
||||||
|
/// </summary>
|
||||||
|
public static string TroubleLoggingIn {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TroubleLoggingIn", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to Try again.
|
/// Looks up a localized string similar to Try again.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -6263,6 +6335,15 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Turn off using a public device.
|
||||||
|
/// </summary>
|
||||||
|
public static string TurnOffUsingPublicDevice {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("TurnOffUsingPublicDevice", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to 20 seconds.
|
/// Looks up a localized string similar to 20 seconds.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -7145,6 +7226,24 @@ namespace Bit.App.Resources {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to Your request has been sent to your admin..
|
||||||
|
/// </summary>
|
||||||
|
public static string YourRequestHasBeenSentToYourAdmin {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("YourRequestHasBeenSentToYourAdmin", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Looks up a localized string similar to You will be notified once approved. .
|
||||||
|
/// </summary>
|
||||||
|
public static string YouWillBeNotifiedOnceApproved {
|
||||||
|
get {
|
||||||
|
return ResourceManager.GetString("YouWillBeNotifiedOnceApproved", resourceCulture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Looks up a localized string similar to To continue, hold your YubiKey NEO against the back of the device or insert your YubiKey into your device's USB port, then touch its button..
|
/// Looks up a localized string similar to To continue, hold your YubiKey NEO against the back of the device or insert your YubiKey into your device's USB port, then touch its button..
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -2631,6 +2631,24 @@ Do you want to switch to this account?</value>
|
|||||||
<data name="CurrentMasterPassword" xml:space="preserve">
|
<data name="CurrentMasterPassword" xml:space="preserve">
|
||||||
<value>Current master password</value>
|
<value>Current master password</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="LoggedIn" xml:space="preserve">
|
||||||
|
<value>Logged in!</value>
|
||||||
|
</data>
|
||||||
|
<data name="ApproveWithMyOtherDevice" xml:space="preserve">
|
||||||
|
<value>Approve with my other device</value>
|
||||||
|
</data>
|
||||||
|
<data name="RequestAdminApproval" xml:space="preserve">
|
||||||
|
<value>Request admin approval</value>
|
||||||
|
</data>
|
||||||
|
<data name="ApproveWithMasterPassword" xml:space="preserve">
|
||||||
|
<value>Approve with master password</value>
|
||||||
|
</data>
|
||||||
|
<data name="TurnOffUsingPublicDevice" xml:space="preserve">
|
||||||
|
<value>Turn off using a public device</value>
|
||||||
|
</data>
|
||||||
|
<data name="RememberThisDevice" xml:space="preserve">
|
||||||
|
<value>Remember this device</value>
|
||||||
|
</data>
|
||||||
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
|
<data name="MasterPasswordRePromptHelp" xml:space="preserve">
|
||||||
<value>Master password re-prompt help</value>
|
<value>Master password re-prompt help</value>
|
||||||
</data>
|
</data>
|
||||||
@@ -2643,4 +2661,19 @@ Do you want to switch to this account?</value>
|
|||||||
<data name="InvalidAPIToken" xml:space="preserve">
|
<data name="InvalidAPIToken" xml:space="preserve">
|
||||||
<value>Invalid API token</value>
|
<value>Invalid API token</value>
|
||||||
</data>
|
</data>
|
||||||
|
<data name="AdminApprovalRequested" xml:space="preserve">
|
||||||
|
<value>Admin approval requested</value>
|
||||||
|
</data>
|
||||||
|
<data name="YourRequestHasBeenSentToYourAdmin" xml:space="preserve">
|
||||||
|
<value>Your request has been sent to your admin.</value>
|
||||||
|
</data>
|
||||||
|
<data name="YouWillBeNotifiedOnceApproved" xml:space="preserve">
|
||||||
|
<value>You will be notified once approved. </value>
|
||||||
|
</data>
|
||||||
|
<data name="TroubleLoggingIn" xml:space="preserve">
|
||||||
|
<value>Trouble logging in?</value>
|
||||||
|
</data>
|
||||||
|
<data name="LoggingInAsX" xml:space="preserve">
|
||||||
|
<value>Logging in as {0}</value>
|
||||||
|
</data>
|
||||||
</root>
|
</root>
|
||||||
|
|||||||
@@ -4,6 +4,7 @@ using System.Net;
|
|||||||
using System.Net.Http;
|
using System.Net.Http;
|
||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
@@ -92,7 +93,10 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
||||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||||
|
Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier);
|
||||||
|
Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest deviceRequest);
|
||||||
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
Task<OrganizationDomainSsoDetailsResponse> GetOrgDomainSsoDetailsAsync(string email);
|
||||||
|
Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes);
|
||||||
Task<ConfigResponse> GetConfigsAsync();
|
Task<ConfigResponse> GetConfigsAsync();
|
||||||
Task<string> GetFastmailAccountIdAsync(string apiKey);
|
Task<string> GetFastmailAccountIdAsync(string apiKey);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,7 +34,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
||||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode);
|
Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode);
|
||||||
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
||||||
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email);
|
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType);
|
||||||
|
|
||||||
void LogOut(Action callback);
|
void LogOut(Action callback);
|
||||||
void Init();
|
void Init();
|
||||||
|
|||||||
13
src/Core/Abstractions/IDeviceTrustCryptoService.cs
Normal file
13
src/Core/Abstractions/IDeviceTrustCryptoService.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
|
||||||
|
namespace Bit.Core.Abstractions
|
||||||
|
{
|
||||||
|
public interface IDeviceTrustCryptoService
|
||||||
|
{
|
||||||
|
Task<SymmetricCryptoKey> GetDeviceKeyAsync();
|
||||||
|
Task<DeviceResponse> TrustDeviceAsync();
|
||||||
|
Task<bool> GetUserTrustDeviceChoiceForDecryptionAsync();
|
||||||
|
Task SetUserTrustDeviceChoiceForDecryptionAsync(bool value);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -56,6 +56,8 @@ namespace Bit.Core.Abstractions
|
|||||||
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
Task SetOrgKeysEncryptedAsync(Dictionary<string, string> value, string userId = null);
|
||||||
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
Task<string> GetPrivateKeyEncryptedAsync(string userId = null);
|
||||||
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
|
Task SetPrivateKeyEncryptedAsync(string value, string userId = null);
|
||||||
|
Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null);
|
||||||
|
Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null);
|
||||||
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
|
Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null);
|
||||||
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
|
Task SetAutofillBlacklistedUrisAsync(List<string> value, string userId = null);
|
||||||
Task<bool?> GetAutofillTileAddedAsync();
|
Task<bool?> GetAutofillTileAddedAsync();
|
||||||
@@ -172,9 +174,12 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<string> GetAvatarColorAsync(string userId = null);
|
Task<string> GetAvatarColorAsync(string userId = null);
|
||||||
Task<string> GetPreLoginEmailAsync();
|
Task<string> GetPreLoginEmailAsync();
|
||||||
Task SetPreLoginEmailAsync(string value);
|
Task SetPreLoginEmailAsync(string value);
|
||||||
|
Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null);
|
||||||
string GetLocale();
|
string GetLocale();
|
||||||
void SetLocale(string locale);
|
void SetLocale(string locale);
|
||||||
ConfigResponse GetConfigs();
|
ConfigResponse GetConfigs();
|
||||||
void SetConfigs(ConfigResponse value);
|
void SetConfigs(ConfigResponse value);
|
||||||
|
Task<bool> GetUserTrustDeviceChoiceForDecryptionAsync();
|
||||||
|
Task SetUserTrustDeviceChoiceForDecryptionAsync(bool value);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@
|
|||||||
public const string AppLocaleKey = "appLocale";
|
public const string AppLocaleKey = "appLocale";
|
||||||
public const string ClearSensitiveFields = "clearSensitiveFields";
|
public const string ClearSensitiveFields = "clearSensitiveFields";
|
||||||
public const string ForceUpdatePassword = "forceUpdatePassword";
|
public const string ForceUpdatePassword = "forceUpdatePassword";
|
||||||
|
public const string RememberDeviceTde = "rememberDeviceTde";
|
||||||
public const int SelectFileRequestCode = 42;
|
public const int SelectFileRequestCode = 42;
|
||||||
public const int SelectFilePermissionRequestCode = 43;
|
public const int SelectFilePermissionRequestCode = 43;
|
||||||
public const int SaveFileRequestCode = 44;
|
public const int SaveFileRequestCode = 44;
|
||||||
@@ -91,6 +92,7 @@
|
|||||||
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
public static string EncOrgKeysKey(string userId) => $"encOrgKeys_{userId}";
|
||||||
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
public static string EncPrivateKeyKey(string userId) => $"encPrivateKey_{userId}";
|
||||||
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
public static string EncKeyKey(string userId) => $"encKey_{userId}";
|
||||||
|
public static string DeviceKeyKey(string userId) => $"deviceKey_{userId}";
|
||||||
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
public static string KeyHashKey(string userId) => $"keyHash_{userId}";
|
||||||
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
public static string PinProtectedKey(string userId) => $"pinProtectedKey_{userId}";
|
||||||
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
public static string PassGenOptionsKey(string userId) => $"passwordGenerationOptions_{userId}";
|
||||||
|
|||||||
11
src/Core/Enums/AuthRequestType.cs
Normal file
11
src/Core/Enums/AuthRequestType.cs
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
using System;
|
||||||
|
namespace Bit.Core.Enums
|
||||||
|
{
|
||||||
|
public enum AuthRequestType : byte
|
||||||
|
{
|
||||||
|
AuthenticateAndUnlock = 0,
|
||||||
|
Unlock = 1,
|
||||||
|
AdminApproval = 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,7 @@
|
|||||||
namespace Bit.Core.Enums
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
|
||||||
|
namespace Bit.Core.Enums
|
||||||
{
|
{
|
||||||
public enum DeviceType : byte
|
public enum DeviceType : byte
|
||||||
{
|
{
|
||||||
@@ -24,4 +27,24 @@
|
|||||||
VivaldiExtension = 19,
|
VivaldiExtension = 19,
|
||||||
SafariExtension = 20
|
SafariExtension = 20
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class DeviceTypeExtensions
|
||||||
|
{
|
||||||
|
public static List<DeviceType> GetMobileTypes() => new List<DeviceType>
|
||||||
|
{
|
||||||
|
DeviceType.Android,
|
||||||
|
DeviceType.AndroidAmazon,
|
||||||
|
DeviceType.iOS
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<DeviceType> GetDesktopTypes() => new List<DeviceType>
|
||||||
|
{
|
||||||
|
DeviceType.WindowsDesktop,
|
||||||
|
DeviceType.MacOsDesktop,
|
||||||
|
DeviceType.LinuxDesktop,
|
||||||
|
DeviceType.UWP,
|
||||||
|
};
|
||||||
|
|
||||||
|
public static List<DeviceType> GetDesktopAndMobileTypes() => GetMobileTypes().Concat(GetDesktopTypes()).ToList();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
HasPremiumPersonally = copy.HasPremiumPersonally;
|
HasPremiumPersonally = copy.HasPremiumPersonally;
|
||||||
AvatarColor = copy.AvatarColor;
|
AvatarColor = copy.AvatarColor;
|
||||||
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
ForcePasswordResetReason = copy.ForcePasswordResetReason;
|
||||||
|
UserDecryptionOptions = copy.UserDecryptionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public string UserId;
|
public string UserId;
|
||||||
@@ -68,6 +69,7 @@ namespace Bit.Core.Models.Domain
|
|||||||
public bool? EmailVerified;
|
public bool? EmailVerified;
|
||||||
public bool? HasPremiumPersonally;
|
public bool? HasPremiumPersonally;
|
||||||
public ForcePasswordResetReason? ForcePasswordResetReason;
|
public ForcePasswordResetReason? ForcePasswordResetReason;
|
||||||
|
public AccountDecryptionOptions UserDecryptionOptions;
|
||||||
}
|
}
|
||||||
|
|
||||||
public class AccountTokens
|
public class AccountTokens
|
||||||
|
|||||||
21
src/Core/Models/Domain/AccountDecryptionOptions.cs
Normal file
21
src/Core/Models/Domain/AccountDecryptionOptions.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using System;
|
||||||
|
namespace Bit.Core.Models.Domain
|
||||||
|
{
|
||||||
|
public class AccountDecryptionOptions
|
||||||
|
{
|
||||||
|
public bool HasMasterPassword { get; set; }
|
||||||
|
public TrustedDeviceOption TrustedDeviceOption { get; set; }
|
||||||
|
public KeyConnectorOption KeyConnectorOption { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class TrustedDeviceOption
|
||||||
|
{
|
||||||
|
public bool HasAdminApproval { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
public class KeyConnectorOption
|
||||||
|
{
|
||||||
|
public bool KeyConnectorUrl { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@@ -1,4 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Request
|
namespace Bit.Core.Models.Request
|
||||||
{
|
{
|
||||||
public class PasswordlessCreateLoginRequest
|
public class PasswordlessCreateLoginRequest
|
||||||
@@ -25,10 +27,4 @@ namespace Bit.Core.Models.Request
|
|||||||
|
|
||||||
public string FingerprintPhrase { get; set; }
|
public string FingerprintPhrase { get; set; }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AuthRequestType : byte
|
|
||||||
{
|
|
||||||
AuthenticateAndUnlock = 0,
|
|
||||||
Unlock = 1
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
10
src/Core/Models/Request/TrustedDeviceKeysRequest.cs
Normal file
10
src/Core/Models/Request/TrustedDeviceKeysRequest.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
|
||||||
|
namespace Bit.Core.Models.Request
|
||||||
|
{
|
||||||
|
public class TrustedDeviceKeysRequest
|
||||||
|
{
|
||||||
|
public string EncryptedUserKey { get; set; }
|
||||||
|
public string EncryptedPublicKey { get; set; }
|
||||||
|
public string EncryptedPrivateKey { get; set; }
|
||||||
|
}
|
||||||
|
}
|
||||||
13
src/Core/Models/Response/DeviceResponse.cs
Normal file
13
src/Core/Models/Response/DeviceResponse.cs
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
|
public class DeviceResponse
|
||||||
|
{
|
||||||
|
public string Id { get; set; }
|
||||||
|
public int Name { get; set; }
|
||||||
|
public string Identifier { get; set; }
|
||||||
|
public DeviceType Type { get; set; }
|
||||||
|
public string CreationDate { get; set; }
|
||||||
|
public string EncryptedUserKey { get; set; }
|
||||||
|
public string EncryptedPublicKey { get; set; }
|
||||||
|
public string EncryptedPrivateKey { get; set; }
|
||||||
|
}
|
||||||
@@ -27,6 +27,7 @@ namespace Bit.Core.Models.Response
|
|||||||
public bool ForcePasswordReset { get; set; }
|
public bool ForcePasswordReset { get; set; }
|
||||||
public string KeyConnectorUrl { get; set; }
|
public string KeyConnectorUrl { get; set; }
|
||||||
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
public MasterPasswordPolicyOptions MasterPasswordPolicy { get; set; }
|
||||||
|
public AccountDecryptionOptions UserDecryptionOptions { get; set; }
|
||||||
[JsonIgnore]
|
[JsonIgnore]
|
||||||
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
public KdfConfig KdfConfig => new KdfConfig(Kdf, KdfIterations, KdfMemory, KdfParallelism);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -397,12 +397,38 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
#region Device APIs
|
#region Device APIs
|
||||||
|
|
||||||
|
|
||||||
|
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
||||||
|
{
|
||||||
|
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
||||||
|
{
|
||||||
|
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
||||||
|
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request)
|
public Task PutDeviceTokenAsync(string identifier, DeviceTokenRequest request)
|
||||||
{
|
{
|
||||||
return SendAsync<DeviceTokenRequest, object>(
|
return SendAsync<DeviceTokenRequest, object>(
|
||||||
HttpMethod.Put, $"/devices/identifier/{identifier}/token", request, true, false);
|
HttpMethod.Put, $"/devices/identifier/{identifier}/token", request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Task<bool> GetDevicesExistenceByTypes(DeviceType[] deviceTypes)
|
||||||
|
{
|
||||||
|
return SendAsync<DeviceType[], bool>(
|
||||||
|
HttpMethod.Post, "/devices/exist-by-types", deviceTypes, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier)
|
||||||
|
{
|
||||||
|
return SendAsync<object, DeviceResponse>(HttpMethod.Get, $"/devices/identifier/{deviceIdentifier}", null, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest trustedDeviceKeysRequest)
|
||||||
|
{
|
||||||
|
return SendAsync<TrustedDeviceKeysRequest, DeviceResponse>(HttpMethod.Put, $"/devices/{deviceIdentifier}/keys", trustedDeviceKeysRequest, true, true);
|
||||||
|
}
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Event APIs
|
#region Event APIs
|
||||||
@@ -576,15 +602,6 @@ namespace Bit.Core.Services
|
|||||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Put, $"/auth-requests/{id}", request, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier)
|
|
||||||
{
|
|
||||||
return SendAsync<object, bool>(HttpMethod.Get, "/devices/knowndevice", null, false, true, (message) =>
|
|
||||||
{
|
|
||||||
message.Headers.Add("X-Device-Identifier", deviceIdentifier);
|
|
||||||
message.Headers.Add("X-Request-Email", CoreHelpers.Base64UrlEncode(Encoding.UTF8.GetBytes(email)));
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
#endregion
|
||||||
|
|
||||||
#region Configs
|
#region Configs
|
||||||
|
|||||||
@@ -459,6 +459,7 @@ namespace Bit.Core.Services
|
|||||||
ForcePasswordResetReason = result.ForcePasswordReset
|
ForcePasswordResetReason = result.ForcePasswordReset
|
||||||
? ForcePasswordResetReason.AdminForcePasswordReset
|
? ForcePasswordResetReason.AdminForcePasswordReset
|
||||||
: (ForcePasswordResetReason?)null,
|
: (ForcePasswordResetReason?)null,
|
||||||
|
UserDecryptionOptions = tokenResponse.UserDecryptionOptions,
|
||||||
},
|
},
|
||||||
new Account.AccountTokens()
|
new Account.AccountTokens()
|
||||||
{
|
{
|
||||||
@@ -598,7 +599,7 @@ namespace Bit.Core.Services
|
|||||||
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
|
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email)
|
public async Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType)
|
||||||
{
|
{
|
||||||
var deviceId = await _appIdService.GetAppIdAsync();
|
var deviceId = await _appIdService.GetAppIdAsync();
|
||||||
var keyPair = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
var keyPair = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||||
@@ -606,7 +607,7 @@ namespace Bit.Core.Services
|
|||||||
var fingerprintPhrase = string.Join("-", generatedFingerprintPhrase);
|
var fingerprintPhrase = string.Join("-", generatedFingerprintPhrase);
|
||||||
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
||||||
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(PasswordGenerationOptions.CreateDefault.WithLength(25));
|
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(PasswordGenerationOptions.CreateDefault.WithLength(25));
|
||||||
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, AuthRequestType.AuthenticateAndUnlock, fingerprintPhrase);
|
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, authRequestType, fingerprintPhrase);
|
||||||
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest);
|
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest);
|
||||||
|
|
||||||
if (response != null)
|
if (response != null)
|
||||||
|
|||||||
91
src/Core/Services/DeviceTrustCryptoService.cs
Normal file
91
src/Core/Services/DeviceTrustCryptoService.cs
Normal file
@@ -0,0 +1,91 @@
|
|||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Bit.Core.Abstractions;
|
||||||
|
using Bit.Core.Models.Domain;
|
||||||
|
using Bit.Core.Models.Request;
|
||||||
|
|
||||||
|
namespace Bit.Core.Services
|
||||||
|
{
|
||||||
|
public class DeviceTrustCryptoService : IDeviceTrustCryptoService
|
||||||
|
{
|
||||||
|
private readonly IApiService _apiService;
|
||||||
|
private readonly IAppIdService _appIdService;
|
||||||
|
private readonly ICryptoFunctionService _cryptoFunctionService;
|
||||||
|
private readonly ICryptoService _cryptoService;
|
||||||
|
private readonly IStateService _stateService;
|
||||||
|
|
||||||
|
private const int DEVICE_KEY_SIZE = 64;
|
||||||
|
|
||||||
|
public DeviceTrustCryptoService(
|
||||||
|
IApiService apiService,
|
||||||
|
IAppIdService appIdService,
|
||||||
|
ICryptoFunctionService cryptoFunctionService,
|
||||||
|
ICryptoService cryptoService,
|
||||||
|
IStateService stateService)
|
||||||
|
{
|
||||||
|
_apiService = apiService;
|
||||||
|
_appIdService = appIdService;
|
||||||
|
_cryptoFunctionService = cryptoFunctionService;
|
||||||
|
_cryptoService = cryptoService;
|
||||||
|
_stateService = stateService;
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync()
|
||||||
|
{
|
||||||
|
return await _stateService.GetDeviceKeyAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task SetDeviceKeyAsync(SymmetricCryptoKey deviceKey)
|
||||||
|
{
|
||||||
|
await _stateService.SetDeviceKeyAsync(deviceKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<DeviceResponse> TrustDeviceAsync()
|
||||||
|
{
|
||||||
|
// Attempt to get user key
|
||||||
|
var userKey = await _cryptoService.GetEncKeyAsync();
|
||||||
|
if (userKey == null)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// Generate deviceKey
|
||||||
|
var deviceKey = await MakeDeviceKeyAsync();
|
||||||
|
|
||||||
|
// Generate asymmetric RSA key pair: devicePrivateKey, devicePublicKey
|
||||||
|
var (devicePublicKey, devicePrivateKey) = await _cryptoFunctionService.RsaGenerateKeyPairAsync(2048);
|
||||||
|
|
||||||
|
// Send encrypted keys to server
|
||||||
|
var deviceIdentifier = await _appIdService.GetAppIdAsync();
|
||||||
|
var deviceRequest = new TrustedDeviceKeysRequest
|
||||||
|
{
|
||||||
|
EncryptedUserKey = (await _cryptoService.RsaEncryptAsync(userKey.EncKey, devicePublicKey)).EncryptedString,
|
||||||
|
EncryptedPublicKey = (await _cryptoService.EncryptAsync(devicePublicKey, userKey)).EncryptedString,
|
||||||
|
EncryptedPrivateKey = (await _cryptoService.EncryptAsync(devicePrivateKey, deviceKey)).EncryptedString,
|
||||||
|
};
|
||||||
|
|
||||||
|
var deviceResponse = await _apiService.UpdateTrustedDeviceKeysAsync(deviceIdentifier, deviceRequest);
|
||||||
|
|
||||||
|
// Store device key if successful
|
||||||
|
await SetDeviceKeyAsync(deviceKey);
|
||||||
|
return deviceResponse;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task<SymmetricCryptoKey> MakeDeviceKeyAsync()
|
||||||
|
{
|
||||||
|
// Create 512-bit device key
|
||||||
|
var randomBytes = await _cryptoFunctionService.RandomBytesAsync(DEVICE_KEY_SIZE);
|
||||||
|
return new SymmetricCryptoKey(randomBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task<bool> GetUserTrustDeviceChoiceForDecryptionAsync()
|
||||||
|
{
|
||||||
|
return await _stateService.GetUserTrustDeviceChoiceForDecryptionAsync();
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserTrustDeviceChoiceForDecryptionAsync(bool value)
|
||||||
|
{
|
||||||
|
await _stateService.SetUserTrustDeviceChoiceForDecryptionAsync(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -482,6 +482,17 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.EncPrivateKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
await SetValueAsync(Constants.EncPrivateKeyKey(reconciledOptions.UserId), value, reconciledOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async Task<SymmetricCryptoKey> GetDeviceKeyAsync(string userId = null)
|
||||||
|
{
|
||||||
|
var deviceKeyB64 = await _storageMediatorService.GetAsync<string>(Constants.DeviceKeyKey(userId), true);
|
||||||
|
return new SymmetricCryptoKey(Convert.FromBase64String(deviceKeyB64));
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetDeviceKeyAsync(SymmetricCryptoKey value, string userId = null)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(Constants.DeviceKeyKey(userId), value.KeyB64, true);
|
||||||
|
}
|
||||||
|
|
||||||
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
|
public async Task<List<string>> GetAutofillBlacklistedUrisAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||||
@@ -1280,6 +1291,23 @@ namespace Bit.Core.Services
|
|||||||
await SetValueAsync(Constants.PreLoginEmailKey, value, options);
|
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> GetUserTrustDeviceChoiceForDecryptionAsync()
|
||||||
|
{
|
||||||
|
return await _storageMediatorService.GetAsync<bool>(Constants.RememberDeviceTde);
|
||||||
|
}
|
||||||
|
|
||||||
|
public async Task SetUserTrustDeviceChoiceForDecryptionAsync(bool value)
|
||||||
|
{
|
||||||
|
await _storageMediatorService.SaveAsync(Constants.RememberDeviceTde, value);
|
||||||
|
}
|
||||||
|
|
||||||
public ConfigResponse GetConfigs()
|
public ConfigResponse GetConfigs()
|
||||||
{
|
{
|
||||||
return _storageMediatorService.Get<ConfigResponse>(Constants.ConfigsKey);
|
return _storageMediatorService.Get<ConfigResponse>(Constants.ConfigsKey);
|
||||||
|
|||||||
@@ -88,6 +88,7 @@ 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);
|
||||||
@@ -114,6 +115,7 @@ namespace Bit.Core.Utilities
|
|||||||
Register<IUserVerificationService>("userVerificationService", userVerificationService);
|
Register<IUserVerificationService>("userVerificationService", userVerificationService);
|
||||||
Register<IUsernameGenerationService>(usernameGenerationService);
|
Register<IUsernameGenerationService>(usernameGenerationService);
|
||||||
Register<IConfigService>(configService);
|
Register<IConfigService>(configService);
|
||||||
|
Register<IDeviceTrustCryptoService>(deviceTrustCryptoService);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void Register<T>(string serviceName, T obj)
|
public static void Register<T>(string serviceName, T obj)
|
||||||
|
|||||||
@@ -515,7 +515,7 @@ namespace Bit.iOS.Autofill
|
|||||||
{
|
{
|
||||||
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, appOptions);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, 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)
|
||||||
|
|||||||
@@ -20,6 +20,7 @@ using Bit.App.Pages;
|
|||||||
using Bit.App.Models;
|
using Bit.App.Models;
|
||||||
using Bit.App.Utilities;
|
using Bit.App.Utilities;
|
||||||
using Bit.iOS.Core.Views;
|
using Bit.iOS.Core.Views;
|
||||||
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.iOS.Extension
|
namespace Bit.iOS.Extension
|
||||||
{
|
{
|
||||||
@@ -536,7 +537,7 @@ namespace Bit.iOS.Extension
|
|||||||
{
|
{
|
||||||
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, appOptions);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, 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)
|
||||||
|
|||||||
@@ -350,7 +350,7 @@ namespace Bit.iOS.ShareExtension
|
|||||||
|
|
||||||
private void LaunchLoginWithDevice(string email = null)
|
private void LaunchLoginWithDevice(string email = null)
|
||||||
{
|
{
|
||||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, _appOptions.Value);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, AuthRequestType.AuthenticateAndUnlock, _appOptions.Value);
|
||||||
SetupAppAndApplyResources(loginWithDevicePage);
|
SetupAppAndApplyResources(loginWithDevicePage);
|
||||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user