From 13869b5a1b4eeaa4c3c6a7158c589d32de9c2d34 Mon Sep 17 00:00:00 2001 From: Jake Fink Date: Wed, 10 Nov 2021 20:46:48 -0500 Subject: [PATCH] [KeyConnector] Add support for key connector OTP (#1633) * initial commit - add UsesKeyConnector to UserService - add models - begin work on authentication * finish auth workflow for key connector sso login - finish api call for get user key - start api calls for posts to key connector * Bypass lock page if already unlocked * Move logic to KeyConnectorService, log out if no pin or biometric is set * Disable password reprompt when using key connector * hide password reprompt checkbox when editing or adding cipher * add PostUserKey and PostSetKeyConnector calls * add ConvertMasterPasswordPage * add functionality to RemoveMasterPasswordPage - rename Convert to Remove * Hide Change Master Password button if using key connector * Add OTP verification for export component * Update src/App/Pages/Vault/AddEditPage.xaml.cs Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * remove toolbar item "close" * Update src/Core/Models/Request/KeyConnectorUserKeyRequest.cs Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> * remove new line in resource string - format warning as two labels - set label in code behind for loading simultaneously * implement GetAndSetKey in KeyConnectorService - ignore EnvironmentService call * remove unnecesary orgIdentifier * move RemoveMasterPasswordPage call to LockPage * add spacing to export vault page * log out if no PIN or bio on lock page with key connector * Delete excessive whitespace * Delete excessive whitespace * Change capitalisation of OTP * add default value to models for backwards compatibility * remove this keyword * actually handle exceptions * move RemoveMasterPasswordPage to TabPage using messaging service * add minor improvements * remove 'this.' Co-authored-by: Hinton Co-authored-by: Thomas Rittson Co-authored-by: Thomas Rittson <31796059+eliykat@users.noreply.github.com> --- .../Abstractions/IPasswordRepromptService.cs | 2 + src/App/App.xaml.cs | 9 ++ src/App/Pages/Accounts/LockPage.xaml.cs | 3 +- src/App/Pages/Accounts/LockPageViewModel.cs | 8 ++ src/App/Pages/Accounts/LoginSsoPage.xaml.cs | 9 +- .../Accounts/RemoveMasterPasswordPage.xaml | 33 +++++++ .../Accounts/RemoveMasterPasswordPage.xaml.cs | 58 +++++++++++ .../RemoveMasterPasswordPageViewModel.cs | 56 +++++++++++ src/App/Pages/Settings/ExportVaultPage.xaml | 44 ++++++--- .../Pages/Settings/ExportVaultPage.xaml.cs | 15 ++- .../Settings/ExportVaultPageViewModel.cs | 82 +++++++++++----- .../SettingsPage/SettingsPageViewModel.cs | 9 +- src/App/Pages/TabsPage.cs | 11 +++ src/App/Pages/Vault/AddEditPage.xaml | 2 +- src/App/Pages/Vault/AddEditPage.xaml.cs | 5 + src/App/Resources/AppResources.Designer.cs | 48 +++++++++ src/App/Resources/AppResources.resx | 24 +++++ .../Services/MobilePasswordRepromptService.cs | 12 +++ src/Core/Abstractions/IApiService.cs | 7 ++ src/Core/Abstractions/IKeyConnectorService.cs | 16 +++ src/Core/Abstractions/ITokenService.cs | 1 + .../Abstractions/IUserVerificationService.cs | 10 ++ src/Core/Enums/VerificationType.cs | 8 ++ src/Core/Models/Data/OrganizationData.cs | 4 + src/Core/Models/Domain/Organization.cs | 4 + .../Request/KeyConnectorUserKeyRequest.cs | 13 +++ .../Request/SetKeyConnectorKeyRequest.cs | 24 +++++ src/Core/Models/Request/VerifyOTPRequest.cs | 13 +++ .../Models/Response/IdentityTokenResponse.cs | 1 + .../Response/KeyConnectorUserKeyResponse.cs | 9 ++ .../Response/ProfileOrganizationResponse.cs | 3 + src/Core/Models/Response/ProfileResponse.cs | 1 + src/Core/Services/ApiService.cs | 98 ++++++++++++++++++- src/Core/Services/AuthService.cs | 92 +++++++++++++---- src/Core/Services/KeyConnectorService.cs | 98 +++++++++++++++++++ src/Core/Services/SyncService.cs | 4 + src/Core/Services/TokenService.cs | 11 +++ src/Core/Services/UserVerificationService.cs | 69 +++++++++++++ src/Core/Services/VaultTimeoutService.cs | 15 +++ src/Core/Utilities/ServiceContainer.cs | 12 ++- 40 files changed, 869 insertions(+), 74 deletions(-) create mode 100644 src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml create mode 100644 src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml.cs create mode 100644 src/App/Pages/Accounts/RemoveMasterPasswordPageViewModel.cs create mode 100644 src/Core/Abstractions/IKeyConnectorService.cs create mode 100644 src/Core/Abstractions/IUserVerificationService.cs create mode 100644 src/Core/Enums/VerificationType.cs create mode 100644 src/Core/Models/Request/KeyConnectorUserKeyRequest.cs create mode 100644 src/Core/Models/Request/SetKeyConnectorKeyRequest.cs create mode 100644 src/Core/Models/Request/VerifyOTPRequest.cs create mode 100644 src/Core/Models/Response/KeyConnectorUserKeyResponse.cs create mode 100644 src/Core/Services/KeyConnectorService.cs create mode 100644 src/Core/Services/UserVerificationService.cs diff --git a/src/App/Abstractions/IPasswordRepromptService.cs b/src/App/Abstractions/IPasswordRepromptService.cs index 31323f1b3..47fc5930d 100644 --- a/src/App/Abstractions/IPasswordRepromptService.cs +++ b/src/App/Abstractions/IPasswordRepromptService.cs @@ -7,5 +7,7 @@ namespace Bit.App.Abstractions string[] ProtectedFields { get; } Task ShowPasswordPromptAsync(); + + Task Enabled(); } } diff --git a/src/App/App.xaml.cs b/src/App/App.xaml.cs index f3b80e68f..470d51342 100644 --- a/src/App/App.xaml.cs +++ b/src/App/App.xaml.cs @@ -140,6 +140,15 @@ namespace Bit.App } }); } + else if (message.Command == "convertAccountToKeyConnector") + { + Device.BeginInvokeOnMainThread(async () => + { + await Application.Current.MainPage.Navigation.PushModalAsync( + new NavigationPage(new RemoveMasterPasswordPage())); + }); + } + }); } diff --git a/src/App/Pages/Accounts/LockPage.xaml.cs b/src/App/Pages/Accounts/LockPage.xaml.cs index 428772647..5612ce674 100644 --- a/src/App/Pages/Accounts/LockPage.xaml.cs +++ b/src/App/Pages/Accounts/LockPage.xaml.cs @@ -11,7 +11,6 @@ namespace Bit.App.Pages { public partial class LockPage : BaseContentPage { - private readonly IStorageService _storageService; private readonly AppOptions _appOptions; private readonly bool _autoPromptBiometric; private readonly LockPageViewModel _vm; @@ -21,7 +20,6 @@ namespace Bit.App.Pages public LockPage(AppOptions appOptions = null, bool autoPromptBiometric = true) { - _storageService = ServiceContainer.Resolve("storageService"); _appOptions = appOptions; _autoPromptBiometric = autoPromptBiometric; InitializeComponent(); @@ -130,6 +128,7 @@ namespace Bit.App.Pages return; } var previousPage = await AppHelpers.ClearPreviousPage(); + Application.Current.MainPage = new TabsPage(_appOptions, previousPage); } } diff --git a/src/App/Pages/Accounts/LockPageViewModel.cs b/src/App/Pages/Accounts/LockPageViewModel.cs index 673ce6104..06fd30ac7 100644 --- a/src/App/Pages/Accounts/LockPageViewModel.cs +++ b/src/App/Pages/Accounts/LockPageViewModel.cs @@ -28,6 +28,7 @@ namespace Bit.App.Pages private readonly IEnvironmentService _environmentService; private readonly IStateService _stateService; private readonly IBiometricService _biometricService; + private readonly IKeyConnectorService _keyConnectorService; private string _email; private bool _showPassword; @@ -54,6 +55,7 @@ namespace Bit.App.Pages _environmentService = ServiceContainer.Resolve("environmentService"); _stateService = ServiceContainer.Resolve("stateService"); _biometricService = ServiceContainer.Resolve("biometricService"); + _keyConnectorService = ServiceContainer.Resolve("keyConnectorService"); PageTitle = AppResources.VerifyMasterPassword; TogglePasswordCommand = new Command(TogglePassword); @@ -124,6 +126,12 @@ namespace Bit.App.Pages _pinSet = await _vaultTimeoutService.IsPinLockSetAsync(); PinLock = (_pinSet.Item1 && _vaultTimeoutService.PinProtectedKey != null) || _pinSet.Item2; BiometricLock = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasKeyAsync(); + + // Users with key connector and without biometric or pin has no MP to unlock with + if (await _keyConnectorService.GetUsesKeyConnector() && !(BiometricLock || PinLock)) + { + await _vaultTimeoutService.LogOutAsync(); + } _email = await _userService.GetEmailAsync(); var webVault = _environmentService.GetWebVaultUrl(); if (string.IsNullOrWhiteSpace(webVault)) diff --git a/src/App/Pages/Accounts/LoginSsoPage.xaml.cs b/src/App/Pages/Accounts/LoginSsoPage.xaml.cs index 4b9bb4b70..19a9edce3 100644 --- a/src/App/Pages/Accounts/LoginSsoPage.xaml.cs +++ b/src/App/Pages/Accounts/LoginSsoPage.xaml.cs @@ -116,7 +116,14 @@ namespace Bit.App.Pages { RestoreAppOptionsFromCopy(); await AppHelpers.ClearPreviousPage(); - Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions)); + if (await _vaultTimeoutService.IsLockedAsync()) + { + Application.Current.MainPage = new NavigationPage(new LockPage(_appOptions)); + } + else + { + Application.Current.MainPage = new TabsPage(_appOptions, null); + } } } } diff --git a/src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml b/src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml new file mode 100644 index 000000000..a18840ce9 --- /dev/null +++ b/src/App/Pages/Accounts/RemoveMasterPasswordPage.xaml @@ -0,0 +1,33 @@ + + + + + + + + + + + +