mirror of
https://github.com/bitwarden/mobile
synced 2025-12-10 05:13:31 +00:00
Compare commits
89 Commits
v2023.8.0
...
feature/tr
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a9368be106 | ||
|
|
b2df06a7a1 | ||
|
|
3f4892fcc8 | ||
|
|
5f4cd62f66 | ||
|
|
9b5cde6a46 | ||
|
|
62b6d21371 | ||
|
|
9001fa1ccf | ||
|
|
af016cd13c | ||
|
|
3b9a9fc049 | ||
|
|
8205c0763c | ||
|
|
e9afc75f0a | ||
|
|
446b8c2d35 | ||
|
|
24378d937e | ||
|
|
6b2700bcaa | ||
|
|
aac66b23bd | ||
|
|
8221d55647 | ||
|
|
53e3429f6d | ||
|
|
faa9b1a9f7 | ||
|
|
4f62f5e5bc | ||
|
|
9bddc1d456 | ||
|
|
78004dbdb9 | ||
|
|
e820408a64 | ||
|
|
c595b1626e | ||
|
|
270a395d9f | ||
|
|
4fa8d2ba28 | ||
|
|
c75bcccf20 | ||
|
|
e076c9fe04 | ||
|
|
78788276ef | ||
|
|
ee0dcd23f5 | ||
|
|
1e8ed1b5ce | ||
|
|
7fb89fa1a5 | ||
|
|
b1eb263fef | ||
|
|
61aac20555 | ||
|
|
3e87d74061 | ||
|
|
89a9185b20 | ||
|
|
e323e196c0 | ||
|
|
c793260689 | ||
|
|
c2ddbb7eff | ||
|
|
bb5a7383a8 | ||
|
|
6fe8fc39ab | ||
|
|
de5113ede7 | ||
|
|
ba6d260565 | ||
|
|
7562c688c5 | ||
|
|
c25906206e | ||
|
|
dfc7c55b77 | ||
|
|
10574a7117 | ||
|
|
a2f1ca583a | ||
|
|
813ac841c6 | ||
|
|
0da3d25955 | ||
|
|
f8c9cde2ed | ||
|
|
080aabfe82 | ||
|
|
c0688c584e | ||
|
|
c09672ff88 | ||
|
|
635b6bc184 | ||
|
|
da7a1964ef | ||
|
|
73b8d8e6b8 | ||
|
|
c61f9f0357 | ||
|
|
a3183857b9 | ||
|
|
bedbca841d | ||
|
|
0ff314f076 | ||
|
|
c9a7c29190 | ||
|
|
546bf8dcb1 | ||
|
|
7fdc5597fc | ||
|
|
7c664f58b3 | ||
|
|
bdfe806846 | ||
|
|
5ed567ab90 | ||
|
|
cd4f44e6f6 | ||
|
|
d58f0b281b | ||
|
|
5ba3fac0c0 | ||
|
|
1e30524985 | ||
|
|
515decb4c9 | ||
|
|
bf28d373e9 | ||
|
|
69d38d4d75 | ||
|
|
c1619536aa | ||
|
|
079e02e4e5 | ||
|
|
15d3da607b | ||
|
|
b5cf9fd79d | ||
|
|
b688b85d0f | ||
|
|
a5df6c0c65 | ||
|
|
c2d4fa4429 | ||
|
|
548bd12a8e | ||
|
|
58542fd255 | ||
|
|
800b4c71de | ||
|
|
3053eaa036 | ||
|
|
6268f0776b | ||
|
|
cbbc41be67 | ||
|
|
e164fb9823 | ||
|
|
87866304a6 | ||
|
|
84a82f0876 |
@@ -68,6 +68,7 @@ namespace Bit.Droid
|
|||||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||||
|
|
||||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||||
|
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
|
||||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||||
ServiceContainer.Resolve<IUserVerificationService>());
|
ServiceContainer.Resolve<IUserVerificationService>());
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.8.0" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools" android:versionCode="1" android:versionName="2023.7.1" android:installLocation="internalOnly" package="com.x8bit.bitwarden">
|
||||||
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
<uses-sdk android:minSdkVersion="21" android:targetSdkVersion="33" />
|
||||||
<uses-permission android:name="android.permission.INTERNET" />
|
<uses-permission android:name="android.permission.INTERNET" />
|
||||||
<uses-permission android:name="android.permission.NFC" />
|
<uses-permission android:name="android.permission.NFC" />
|
||||||
|
|||||||
@@ -1,5 +1,4 @@
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Enums;
|
|
||||||
|
|
||||||
namespace Bit.App.Abstractions
|
namespace Bit.App.Abstractions
|
||||||
{
|
{
|
||||||
@@ -7,7 +6,7 @@ namespace Bit.App.Abstractions
|
|||||||
{
|
{
|
||||||
string[] ProtectedFields { get; }
|
string[] ProtectedFields { get; }
|
||||||
|
|
||||||
Task<bool> PromptAndCheckPasswordIfNeededAsync(CipherRepromptType repromptType = CipherRepromptType.Password);
|
Task<bool> ShowPasswordPromptAsync();
|
||||||
|
|
||||||
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -50,13 +50,13 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private async Task StartLoginWithDeviceAsync()
|
private async Task StartLoginWithDeviceAsync()
|
||||||
{
|
{
|
||||||
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions, true);
|
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AuthenticateAndUnlock, _appOptions);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task RequestAdminApprovalAsync()
|
private async Task RequestAdminApprovalAsync()
|
||||||
{
|
{
|
||||||
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AdminApproval, _appOptions, true);
|
var page = new LoginPasswordlessRequestPage(_vm.Email, AuthRequestType.AdminApproval, _appOptions);
|
||||||
await Navigation.PushModalAsync(new NavigationPage(page));
|
await Navigation.PushModalAsync(new NavigationPage(page));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ namespace Bit.App.Pages
|
|||||||
private LoginPasswordlessRequestViewModel _vm;
|
private LoginPasswordlessRequestViewModel _vm;
|
||||||
private readonly AppOptions _appOptions;
|
private readonly AppOptions _appOptions;
|
||||||
|
|
||||||
public LoginPasswordlessRequestPage(string email, AuthRequestType authRequestType, AppOptions appOptions = null, bool authingWithSso = false)
|
public LoginPasswordlessRequestPage(string email, AuthRequestType authRequestType, AppOptions appOptions = null)
|
||||||
{
|
{
|
||||||
InitializeComponent();
|
InitializeComponent();
|
||||||
_appOptions = appOptions;
|
_appOptions = appOptions;
|
||||||
@@ -21,7 +21,6 @@ namespace Bit.App.Pages
|
|||||||
_vm.Page = this;
|
_vm.Page = this;
|
||||||
_vm.Email = email;
|
_vm.Email = email;
|
||||||
_vm.AuthRequestType = authRequestType;
|
_vm.AuthRequestType = authRequestType;
|
||||||
_vm.AuthingWithSso = authingWithSso;
|
|
||||||
_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());
|
||||||
|
|||||||
@@ -12,7 +12,6 @@ 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.Enums;
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
using Bit.Core.Services;
|
using Bit.Core.Services;
|
||||||
@@ -81,7 +80,6 @@ namespace Bit.App.Pages
|
|||||||
public Action LogInSuccessAction { get; set; }
|
public Action LogInSuccessAction { get; set; }
|
||||||
public Action UpdateTempPasswordAction { get; set; }
|
public Action UpdateTempPasswordAction { get; set; }
|
||||||
public Action CloseAction { get; set; }
|
public Action CloseAction { get; set; }
|
||||||
public bool AuthingWithSso { get; set; }
|
|
||||||
|
|
||||||
public ICommand CreatePasswordlessLoginCommand { get; }
|
public ICommand CreatePasswordlessLoginCommand { get; }
|
||||||
public ICommand CloseCommand { get; }
|
public ICommand CloseCommand { get; }
|
||||||
@@ -235,7 +233,7 @@ namespace Bit.App.Pages
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
PasswordlessLoginResponse response = null;
|
PasswordlessLoginResponse response = null;
|
||||||
if (AuthingWithSso)
|
if (await _stateService.IsAuthenticatedAsync())
|
||||||
{
|
{
|
||||||
response = await _authService.GetPasswordlessLoginRequestByIdAsync(_requestId);
|
response = await _authService.GetPasswordlessLoginRequestByIdAsync(_requestId);
|
||||||
}
|
}
|
||||||
@@ -244,14 +242,14 @@ namespace Bit.App.Pages
|
|||||||
response = await _authService.GetPasswordlessLoginResquestAsync(_requestId, _requestAccessCode);
|
response = await _authService.GetPasswordlessLoginResquestAsync(_requestId, _requestAccessCode);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response?.RequestApproved != true)
|
if (response.RequestApproved == null || !response.RequestApproved.Value)
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
StopCheckLoginRequestStatus();
|
StopCheckLoginRequestStatus();
|
||||||
|
|
||||||
var authResult = await _authService.LogInPasswordlessAsync(AuthingWithSso, Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
|
var authResult = await _authService.LogInPasswordlessAsync(Email, _requestAccessCode, _requestId, _requestKeyPair.Item2, response.Key, response.MasterPasswordHash);
|
||||||
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
await AppHelpers.ResetInvalidUnlockAttemptsAsync();
|
||||||
|
|
||||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
||||||
@@ -278,10 +276,6 @@ namespace Bit.App.Pages
|
|||||||
await HandleLoginCompleteAsync();
|
await HandleLoginCompleteAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
catch (ApiException ex) when (ex.Error?.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
|
||||||
{
|
|
||||||
HandleException(ex);
|
|
||||||
}
|
|
||||||
catch (Exception ex)
|
catch (Exception ex)
|
||||||
{
|
{
|
||||||
StartCheckLoginRequestStatus();
|
StartCheckLoginRequestStatus();
|
||||||
@@ -349,6 +343,16 @@ namespace Bit.App.Pages
|
|||||||
_requestAccessCode = response.RequestAccessCode;
|
_requestAccessCode = response.RequestAccessCode;
|
||||||
_requestKeyPair = response.RequestKeyPair;
|
_requestKeyPair = response.RequestKeyPair;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void HandleException(Exception ex)
|
||||||
|
{
|
||||||
|
Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(async () =>
|
||||||
|
{
|
||||||
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
await _platformUtilsService.ShowDialogAsync(AppResources.GenericErrorMessage);
|
||||||
|
}).FireAndForget();
|
||||||
|
_logger.Exception(ex);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -114,7 +114,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
await _deviceActionService.ShowLoadingAsync(AppResources.LoggingIn);
|
||||||
|
|
||||||
var response = await _apiService.PreValidateSsoAsync(OrgIdentifier);
|
var response = await _apiService.PreValidateSso(OrgIdentifier);
|
||||||
|
|
||||||
if (string.IsNullOrWhiteSpace(response?.Token))
|
if (string.IsNullOrWhiteSpace(response?.Token))
|
||||||
{
|
{
|
||||||
@@ -211,46 +211,38 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Trusted device option is sent regardless if this is a trusted device or not
|
|
||||||
// If it is trusted, it will have the necessary keys
|
|
||||||
if (decryptOptions?.TrustedDeviceOption != null)
|
if (decryptOptions?.TrustedDeviceOption != null)
|
||||||
{
|
{
|
||||||
if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
||||||
{
|
|
||||||
// If we have a device key but no keys on server, we need to remove the device key
|
|
||||||
if (decryptOptions.TrustedDeviceOption.EncryptedPrivateKey == null && decryptOptions.TrustedDeviceOption.EncryptedUserKey == null)
|
|
||||||
{
|
|
||||||
await _deviceTrustCryptoService.RemoveTrustedDeviceAsync();
|
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||||
if (!decryptOptions.HasMasterPassword &&
|
if (!decryptOptions.HasMasterPassword &&
|
||||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
else if (response.ForcePasswordReset)
|
||||||
if (response.ForcePasswordReset)
|
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Device is trusted and has keys, so we can decrypt
|
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||||
|
{
|
||||||
|
if (decryptOptions.TrustedDeviceOption.EncryptedPrivateKey == null && decryptOptions.TrustedDeviceOption.EncryptedUserKey == null)
|
||||||
|
{
|
||||||
|
await _deviceTrustCryptoService.RemoveTrustedDeviceAsync();
|
||||||
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
// Check for pending Admin Auth requests before navigating to device approval options
|
else if (pendingRequest != null)
|
||||||
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
|
||||||
if (pendingRequest != null)
|
|
||||||
{
|
{
|
||||||
var authRequest = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
|
var authRequest = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
|
||||||
if (authRequest?.RequestApproved == true)
|
if (authRequest != null && authRequest.RequestApproved != null && authRequest.RequestApproved.Value)
|
||||||
{
|
{
|
||||||
var authResult = await _authService.LogInPasswordlessAsync(true, await _stateService.GetActiveUserEmailAsync(), authRequest.RequestAccessCode, pendingRequest.Id, pendingRequest.PrivateKey, authRequest.Key, authRequest.MasterPasswordHash);
|
var authResult = await _authService.LogInPasswordlessAsync(await _stateService.GetActiveUserEmailAsync(), authRequest.RequestAccessCode, pendingRequest.Id, pendingRequest.PrivateKey, authRequest.Key, authRequest.MasterPasswordHash);
|
||||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
||||||
{
|
{
|
||||||
await Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(
|
await Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(
|
||||||
@@ -276,14 +268,18 @@ namespace Bit.App.Pages
|
|||||||
// In the standard, non TDE case, a user must set password if they don't
|
// In the standard, non TDE case, a user must set password if they don't
|
||||||
// have one and they aren't using key connector.
|
// have one and they aren't using key connector.
|
||||||
// Note: TDE & Key connector are mutually exclusive org config options.
|
// Note: TDE & Key connector are mutually exclusive org config options.
|
||||||
if (response.ResetMasterPassword || (decryptOptions?.RequireSetPassword == true))
|
if (response.ResetMasterPassword || (decryptOptions?.RequireSetPassword ?? false))
|
||||||
{
|
{
|
||||||
// TODO: We need to look into how to handle this when Org removes TDE
|
|
||||||
// Will we have the User Key by now to set a new password?
|
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (response.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
UpdateTempPasswordAction?.Invoke();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
_syncService.FullSyncAsync(true).FireAndForget();
|
||||||
SsoAuthSuccessAction?.Invoke();
|
SsoAuthSuccessAction?.Invoke();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -178,10 +178,7 @@ namespace Bit.App.Pages
|
|||||||
Email = Email.Trim().ToLower();
|
Email = Email.Trim().ToLower();
|
||||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||||
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
|
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
|
||||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
newMasterKey,
|
|
||||||
await _cryptoService.MakeUserKeyAsync()
|
|
||||||
);
|
|
||||||
var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
|
var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
|
||||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||||
var request = new RegisterRequest
|
var request = new RegisterRequest
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Bit.App.Pages
|
|||||||
{
|
{
|
||||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||||
|
|
||||||
await _apiService.PostLeaveOrganizationAsync(Organization.Id);
|
await _apiService.PostLeaveOrganization(Organization.Id);
|
||||||
await _syncService.FullSyncAsync(true);
|
await _syncService.FullSyncAsync(true);
|
||||||
|
|
||||||
await _deviceActionService.HideLoadingAsync();
|
await _deviceActionService.HideLoadingAsync();
|
||||||
|
|||||||
@@ -169,8 +169,7 @@ namespace Bit.App.Pages
|
|||||||
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
|
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
|
||||||
var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
|
var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
|
||||||
|
|
||||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
|
||||||
|
|
||||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||||
var request = new SetPasswordRequest
|
var request = new SetPasswordRequest
|
||||||
|
|||||||
@@ -18,6 +18,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
private TwoFactorPageViewModel _vm;
|
private TwoFactorPageViewModel _vm;
|
||||||
private bool _inited;
|
private bool _inited;
|
||||||
|
private bool _authingWithSso;
|
||||||
private string _orgIdentifier;
|
private string _orgIdentifier;
|
||||||
|
|
||||||
public TwoFactorPage(bool? authingWithSso = false, AppOptions appOptions = null, string orgIdentifier = null)
|
public TwoFactorPage(bool? authingWithSso = false, AppOptions appOptions = null, string orgIdentifier = null)
|
||||||
|
|||||||
@@ -39,6 +39,7 @@ namespace Bit.App.Pages
|
|||||||
private TwoFactorProviderType? _selectedProviderType;
|
private TwoFactorProviderType? _selectedProviderType;
|
||||||
private string _totpInstruction;
|
private string _totpInstruction;
|
||||||
private string _webVaultUrl = "https://vault.bitwarden.com";
|
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||||
|
private bool _authingWithSso = false;
|
||||||
private bool _enableContinue = false;
|
private bool _enableContinue = false;
|
||||||
private bool _showContinue = true;
|
private bool _showContinue = true;
|
||||||
|
|
||||||
@@ -143,6 +144,8 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
_authingWithSso = _authService.AuthingWithSso();
|
||||||
|
|
||||||
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
|
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
|
||||||
{
|
{
|
||||||
_webVaultUrl = _environmentService.BaseUrl;
|
_webVaultUrl = _environmentService.BaseUrl;
|
||||||
@@ -324,81 +327,48 @@ namespace Bit.App.Pages
|
|||||||
_messagingService.Send("listenYubiKeyOTP", false);
|
_messagingService.Send("listenYubiKeyOTP", false);
|
||||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||||
|
|
||||||
if (decryptOptions?.TrustedDeviceOption != null)
|
if (_authingWithSso && result.ResetMasterPassword)
|
||||||
{
|
{
|
||||||
if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
StartSetPasswordAction?.Invoke();
|
||||||
{
|
|
||||||
// If we have a device key but no keys on server, we need to remove the device key
|
|
||||||
if (decryptOptions.TrustedDeviceOption.EncryptedPrivateKey == null && decryptOptions.TrustedDeviceOption.EncryptedUserKey == null)
|
|
||||||
{
|
|
||||||
await _deviceTrustCryptoService.RemoveTrustedDeviceAsync();
|
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (result.ForcePasswordReset)
|
||||||
|
{
|
||||||
|
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 user doesn't have a MP, but has reset password permission, they must set a MP
|
||||||
if (!decryptOptions.HasMasterPassword &&
|
if (!decryptOptions.HasMasterPassword &&
|
||||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||||
{
|
{
|
||||||
StartSetPasswordAction?.Invoke();
|
StartSetPasswordAction?.Invoke();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
else if (result.ForcePasswordReset)
|
||||||
if (result.ForcePasswordReset)
|
|
||||||
{
|
{
|
||||||
UpdateTempPasswordAction?.Invoke();
|
UpdateTempPasswordAction?.Invoke();
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||||
// Device is trusted and has keys, so we can decrypt
|
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
|
||||||
await TwoFactorAuthSuccessAsync();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Check for pending Admin Auth requests before navigating to device approval options
|
|
||||||
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
|
||||||
if (pendingRequest != null)
|
|
||||||
{
|
{
|
||||||
var authRequest = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
|
if (decryptOptions.TrustedDeviceOption.EncryptedPrivateKey == null && decryptOptions.TrustedDeviceOption.EncryptedUserKey == null)
|
||||||
if (authRequest?.RequestApproved == true)
|
|
||||||
{
|
{
|
||||||
var authResult = await _authService.LogInPasswordlessAsync(true, await _stateService.GetActiveUserEmailAsync(), authRequest.RequestAccessCode, pendingRequest.Id, pendingRequest.PrivateKey, authRequest.Key, authRequest.MasterPasswordHash);
|
await _deviceTrustCryptoService.RemoveTrustedDeviceAsync();
|
||||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
{
|
|
||||||
await Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(
|
|
||||||
() => _platformUtilsService.ShowToast("info", null, AppResources.LoginApproved));
|
|
||||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
|
||||||
await TwoFactorAuthSuccessAsync();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
await TwoFactorAuthSuccessAsync();
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
StartDeviceApprovalOptionsAction?.Invoke();
|
StartDeviceApprovalOptionsAction?.Invoke();
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// In the standard, non TDE case, a user must set password if they don't
|
|
||||||
// have one and they aren't using key connector.
|
|
||||||
// Note: TDE & Key connector are mutually exclusive org config options.
|
|
||||||
if (result.ResetMasterPassword || (decryptOptions?.RequireSetPassword ?? false))
|
|
||||||
{
|
{
|
||||||
// TODO: We need to look into how to handle this when Org removes TDE
|
|
||||||
// Will we have the User Key by now to set a new password?
|
|
||||||
StartSetPasswordAction?.Invoke();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_syncService.FullSyncAsync(true).FireAndForget();
|
|
||||||
await TwoFactorAuthSuccessAsync();
|
await TwoFactorAuthSuccessAsync();
|
||||||
}
|
}
|
||||||
|
}
|
||||||
catch (ApiException e)
|
catch (ApiException e)
|
||||||
{
|
{
|
||||||
_captchaToken = null;
|
_captchaToken = null;
|
||||||
|
|||||||
@@ -65,7 +65,7 @@ namespace Bit.App.Pages
|
|||||||
_initialized = true;
|
_initialized = true;
|
||||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||||
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||||
UseOTPVerification = !await _userVerificationService.HasMasterPasswordAsync(true);
|
UseOTPVerification = !await _userVerificationService.HasMasterPasswordAsync();
|
||||||
|
|
||||||
if (UseOTPVerification)
|
if (UseOTPVerification)
|
||||||
{
|
{
|
||||||
@@ -163,7 +163,7 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync(true)
|
var verificationType = await _userVerificationService.HasMasterPasswordAsync()
|
||||||
? VerificationType.MasterPassword
|
? VerificationType.MasterPassword
|
||||||
: VerificationType.OTP;
|
: VerificationType.OTP;
|
||||||
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
||||||
|
|||||||
@@ -82,7 +82,7 @@ namespace Bit.App.Pages
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -698,12 +698,12 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
public async Task<bool> PromptPasswordAsync()
|
public async Task<bool> PromptPasswordAsync()
|
||||||
{
|
{
|
||||||
if (_passwordReprompted)
|
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return _passwordReprompted = await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(Cipher.Reprompt);
|
return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync();
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> CanCloneAsync()
|
private async Task<bool> CanCloneAsync()
|
||||||
|
|||||||
@@ -191,7 +191,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
if (_appOptions?.OtpData != null)
|
if (_appOptions?.OtpData != null)
|
||||||
{
|
{
|
||||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@@ -208,7 +208,7 @@ namespace Bit.App.Pages
|
|||||||
}
|
}
|
||||||
else if (selection == AppResources.Autofill || selection == AppResources.AutofillAndSave)
|
else if (selection == AppResources.Autofill || selection == AppResources.AutofillAndSave)
|
||||||
{
|
{
|
||||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -60,7 +60,7 @@ namespace Bit.App.Pages
|
|||||||
|
|
||||||
var cipher = listItem.Cipher;
|
var cipher = listItem.Cipher;
|
||||||
|
|
||||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,6 @@
|
|||||||
using Bit.App.Abstractions;
|
using Bit.App.Abstractions;
|
||||||
using Bit.App.Resources;
|
using Bit.App.Resources;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
|
||||||
|
|
||||||
namespace Bit.App.Services
|
namespace Bit.App.Services
|
||||||
{
|
{
|
||||||
@@ -19,13 +18,8 @@ namespace Bit.App.Services
|
|||||||
|
|
||||||
public string[] ProtectedFields { get; } = { "LoginTotp", "LoginPassword", "H_FieldValue", "CardNumber", "CardCode" };
|
public string[] ProtectedFields { get; } = { "LoginTotp", "LoginPassword", "H_FieldValue", "CardNumber", "CardCode" };
|
||||||
|
|
||||||
public async Task<bool> PromptAndCheckPasswordIfNeededAsync(CipherRepromptType repromptType = CipherRepromptType.Password)
|
public async Task<bool> ShowPasswordPromptAsync()
|
||||||
{
|
{
|
||||||
if (repromptType == CipherRepromptType.None || await ShouldByPassMasterPasswordRepromptAsync())
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return await _platformUtilsService.ShowPasswordDialogAsync(AppResources.PasswordConfirmation, AppResources.PasswordConfirmationDesc, ValidatePasswordAsync);
|
return await _platformUtilsService.ShowPasswordDialogAsync(AppResources.PasswordConfirmation, AppResources.PasswordConfirmationDesc, ValidatePasswordAsync);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -44,10 +38,5 @@ namespace Bit.App.Services
|
|||||||
|
|
||||||
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
|
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
|
||||||
}
|
}
|
||||||
|
|
||||||
private async Task<bool> ShouldByPassMasterPasswordRepromptAsync()
|
|
||||||
{
|
|
||||||
return await _cryptoService.GetMasterKeyHashAsync() is null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -99,28 +99,30 @@ namespace Bit.App.Utilities
|
|||||||
{
|
{
|
||||||
await page.Navigation.PushModalAsync(new NavigationPage(new CipherDetailsPage(cipher.Id)));
|
await page.Navigation.PushModalAsync(new NavigationPage(new CipherDetailsPage(cipher.Id)));
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.Edit
|
else if (selection == AppResources.Edit)
|
||||||
&&
|
{
|
||||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id)));
|
await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id)));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (selection == AppResources.CopyUsername)
|
else if (selection == AppResources.CopyUsername)
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Type == CipherType.Login ? cipher.Login.Username : cipher.Fido2Key.UserName);
|
await clipboardService.CopyTextAsync(cipher.Type == CipherType.Login ? cipher.Login.Username : cipher.Fido2Key.UserName);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Username);
|
platformUtilsService.ShowToastForCopiedValue(AppResources.Username);
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyPassword
|
else if (selection == AppResources.CopyPassword)
|
||||||
&&
|
{
|
||||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyTotp
|
}
|
||||||
&&
|
else if (selection == AppResources.CopyTotp)
|
||||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
{
|
||||||
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||||
@@ -130,24 +132,27 @@ namespace Bit.App.Utilities
|
|||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
else if (selection == AppResources.Launch && cipher.CanLaunch)
|
else if (selection == AppResources.Launch && cipher.CanLaunch)
|
||||||
{
|
{
|
||||||
platformUtilsService.LaunchUri(cipher.LaunchUri);
|
platformUtilsService.LaunchUri(cipher.LaunchUri);
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyNumber
|
else if (selection == AppResources.CopyNumber)
|
||||||
&&
|
{
|
||||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Number);
|
platformUtilsService.ShowToastForCopiedValue(AppResources.Number);
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopySecurityCode
|
}
|
||||||
&&
|
else if (selection == AppResources.CopySecurityCode)
|
||||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
{
|
||||||
|
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
||||||
platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode);
|
platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode);
|
||||||
eventService.CollectAsync(EventType.Cipher_ClientCopiedCardCode, cipher.Id).FireAndForget();
|
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else if (selection == AppResources.CopyNotes)
|
else if (selection == AppResources.CopyNotes)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -60,6 +60,7 @@ namespace Bit.App.Utilities
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class VerificationActionsFlowHelper : IVerificationActionsFlowHelper
|
public class VerificationActionsFlowHelper : IVerificationActionsFlowHelper
|
||||||
{
|
{
|
||||||
|
private readonly IKeyConnectorService _keyConnectorService;
|
||||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||||
private readonly ICryptoService _cryptoService;
|
private readonly ICryptoService _cryptoService;
|
||||||
private readonly IUserVerificationService _userVerificationService;
|
private readonly IUserVerificationService _userVerificationService;
|
||||||
@@ -71,11 +72,12 @@ namespace Bit.App.Utilities
|
|||||||
|
|
||||||
private readonly Dictionary<VerificationFlowAction, IActionFlowExecutioner> _actionExecutionerDictionary = new Dictionary<VerificationFlowAction, IActionFlowExecutioner>();
|
private readonly Dictionary<VerificationFlowAction, IActionFlowExecutioner> _actionExecutionerDictionary = new Dictionary<VerificationFlowAction, IActionFlowExecutioner>();
|
||||||
|
|
||||||
public VerificationActionsFlowHelper(
|
public VerificationActionsFlowHelper(IKeyConnectorService keyConnectorService,
|
||||||
IPasswordRepromptService passwordRepromptService,
|
IPasswordRepromptService passwordRepromptService,
|
||||||
ICryptoService cryptoService,
|
ICryptoService cryptoService,
|
||||||
IUserVerificationService userVerificationService)
|
IUserVerificationService userVerificationService)
|
||||||
{
|
{
|
||||||
|
_keyConnectorService = keyConnectorService;
|
||||||
_passwordRepromptService = passwordRepromptService;
|
_passwordRepromptService = passwordRepromptService;
|
||||||
_cryptoService = cryptoService;
|
_cryptoService = cryptoService;
|
||||||
_userVerificationService = userVerificationService;
|
_userVerificationService = userVerificationService;
|
||||||
@@ -108,7 +110,7 @@ namespace Bit.App.Utilities
|
|||||||
|
|
||||||
public async Task ValidateAndExecuteAsync()
|
public async Task ValidateAndExecuteAsync()
|
||||||
{
|
{
|
||||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync(true)
|
var verificationType = await _userVerificationService.HasMasterPasswordAsync()
|
||||||
? VerificationType.MasterPassword
|
? VerificationType.MasterPassword
|
||||||
: VerificationType.OTP;
|
: VerificationType.OTP;
|
||||||
|
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task PutDeleteCipherAsync(string id);
|
Task PutDeleteCipherAsync(string id);
|
||||||
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
||||||
Task RefreshIdentityTokenAsync();
|
Task RefreshIdentityTokenAsync();
|
||||||
Task<SsoPrevalidateResponse> PreValidateSsoAsync(string identifier);
|
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
||||||
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
||||||
TRequest body, bool authed, bool hasResponse, Action<HttpRequestMessage> alterRequest, bool logoutOnUnauthorized = true);
|
TRequest body, bool authed, bool hasResponse, Action<HttpRequestMessage> alterRequest, bool logoutOnUnauthorized = true);
|
||||||
Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default);
|
Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default);
|
||||||
@@ -72,10 +72,10 @@ namespace Bit.Core.Abstractions
|
|||||||
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
||||||
OrganizationUserResetPasswordEnrollmentRequest request);
|
OrganizationUserResetPasswordEnrollmentRequest request);
|
||||||
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl);
|
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl);
|
||||||
Task PostMasterKeyToKeyConnectorAsync(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||||
Task PostSetKeyConnectorKeyAsync(SetKeyConnectorKeyRequest request);
|
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
||||||
Task PostConvertToKeyConnectorAsync();
|
Task PostConvertToKeyConnector();
|
||||||
Task PostLeaveOrganizationAsync(string id);
|
Task PostLeaveOrganization(string id);
|
||||||
|
|
||||||
Task<SendResponse> GetSendAsync(string id);
|
Task<SendResponse> GetSendAsync(string id);
|
||||||
Task<SendResponse> PostSendAsync(SendRequest request);
|
Task<SendResponse> PostSendAsync(SendRequest request);
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId);
|
Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId);
|
||||||
Task<AuthResult> LogInCompleteAsync(string email, string masterPassword, TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null);
|
Task<AuthResult> LogInCompleteAsync(string email, string masterPassword, TwoFactorProviderType twoFactorProvider, string twoFactorToken, bool? remember = null);
|
||||||
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken, string captchaToken, bool? remember = null);
|
||||||
Task<AuthResult> LogInPasswordlessAsync(bool authingWithSso, string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered);
|
Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered);
|
||||||
|
|
||||||
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
||||||
Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync();
|
Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync();
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ namespace Bit.Core.Abstractions
|
|||||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||||
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||||
Task ClearMasterKeyAsync(string userId = null);
|
Task ClearMasterKeyAsync(string userId = null);
|
||||||
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null);
|
Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey);
|
||||||
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
|
Task<UserKey> DecryptUserKeyWithMasterKeyAsync(MasterKey masterKey, EncString encUserKey = null, string userId = null);
|
||||||
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key);
|
Task<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key);
|
||||||
Task<string> HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
Task<string> HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||||
|
|||||||
@@ -6,6 +6,6 @@ namespace Bit.Core.Abstractions
|
|||||||
public interface IUserVerificationService
|
public interface IUserVerificationService
|
||||||
{
|
{
|
||||||
Task<bool> VerifyUser(string secret, VerificationType verificationType);
|
Task<bool> VerifyUser(string secret, VerificationType verificationType);
|
||||||
Task<bool> HasMasterPasswordAsync(bool checkMasterKeyHash = false);
|
Task<bool> HasMasterPasswordAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -86,8 +86,8 @@ namespace Bit.Core
|
|||||||
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
public static string VaultTimeoutKey(string userId) => $"vaultTimeout_{userId}";
|
||||||
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
public static string VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
||||||
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
||||||
public static string UserKeyAutoUnlockKey(string userId) => $"userKeyAutoUnlock_{userId}";
|
public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}";
|
||||||
public static string UserKeyBiometricUnlockKey(string userId) => $"userKeyBiometricUnlock_{userId}";
|
public static string UserKeyBiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
||||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||||
public static string FoldersKey(string userId) => $"folders_{userId}";
|
public static string FoldersKey(string userId) => $"folders_{userId}";
|
||||||
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
public static string CollectionsKey(string userId) => $"collections_{userId}";
|
||||||
|
|||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
namespace Bit.Core.Exceptions
|
|
||||||
{
|
|
||||||
public class MasterKeyNullException : Exception
|
|
||||||
{
|
|
||||||
public MasterKeyNullException()
|
|
||||||
: base("MasterKey is null.")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using System;
|
|
||||||
namespace Bit.Core.Exceptions
|
|
||||||
{
|
|
||||||
public class UserKeyNullException : Exception
|
|
||||||
{
|
|
||||||
public UserKeyNullException()
|
|
||||||
: base("UserKey is null.")
|
|
||||||
{
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System.Collections.Generic;
|
||||||
using System.Collections.Generic;
|
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
|
|
||||||
namespace Bit.Core.Models.Domain
|
namespace Bit.Core.Models.Domain
|
||||||
@@ -9,8 +8,6 @@ namespace Bit.Core.Models.Domain
|
|||||||
public bool TwoFactor { get; set; }
|
public bool TwoFactor { get; set; }
|
||||||
public bool CaptchaNeeded => !string.IsNullOrWhiteSpace(CaptchaSiteKey);
|
public bool CaptchaNeeded => !string.IsNullOrWhiteSpace(CaptchaSiteKey);
|
||||||
public string CaptchaSiteKey { get; set; }
|
public string CaptchaSiteKey { get; set; }
|
||||||
// TODO: PM-3287 - Remove after 3 releases of backwards compatibility - Target release 2023.12
|
|
||||||
[Obsolete("Use AccountDecryptionOptions to determine if the user does not have a MP")]
|
|
||||||
public bool ResetMasterPassword { get; set; }
|
public bool ResetMasterPassword { get; set; }
|
||||||
public bool ForcePasswordReset { get; set; }
|
public bool ForcePasswordReset { get; set; }
|
||||||
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders { get; set; }
|
public Dictionary<TwoFactorProviderType, Dictionary<string, object>> TwoFactorProviders { get; set; }
|
||||||
|
|||||||
@@ -211,12 +211,12 @@ namespace Bit.Core.Services
|
|||||||
return SendAsync<DeleteAccountRequest, object>(HttpMethod.Delete, "/accounts", request, true, false);
|
return SendAsync<DeleteAccountRequest, object>(HttpMethod.Delete, "/accounts", request, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task PostConvertToKeyConnectorAsync()
|
public Task PostConvertToKeyConnector()
|
||||||
{
|
{
|
||||||
return SendAsync<object, object>(HttpMethod.Post, "/accounts/convert-to-key-connector", null, true, false);
|
return SendAsync<object, object>(HttpMethod.Post, "/accounts/convert-to-key-connector", null, true, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task PostSetKeyConnectorKeyAsync(SetKeyConnectorKeyRequest request)
|
public Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request)
|
||||||
{
|
{
|
||||||
return SendAsync<SetKeyConnectorKeyRequest>(HttpMethod.Post, "/accounts/set-key-connector-key", request, true);
|
return SendAsync<SetKeyConnectorKeyRequest>(HttpMethod.Post, "/accounts/set-key-connector-key", request, true);
|
||||||
}
|
}
|
||||||
@@ -486,7 +486,7 @@ namespace Bit.Core.Services
|
|||||||
$"/organizations/{identifier}/auto-enroll-status", null, true, true);
|
$"/organizations/{identifier}/auto-enroll-status", null, true, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Task PostLeaveOrganizationAsync(string id)
|
public Task PostLeaveOrganization(string id)
|
||||||
{
|
{
|
||||||
return SendAsync<object, object>(HttpMethod.Post, $"/organizations/{id}/leave", null, true, false);
|
return SendAsync<object, object>(HttpMethod.Post, $"/organizations/{id}/leave", null, true, false);
|
||||||
}
|
}
|
||||||
@@ -541,7 +541,7 @@ namespace Bit.Core.Services
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task PostMasterKeyToKeyConnectorAsync(string keyConnectorUrl, KeyConnectorUserKeyRequest request)
|
public async Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request)
|
||||||
{
|
{
|
||||||
using (var requestMessage = new HttpRequestMessage())
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
{
|
{
|
||||||
@@ -627,7 +627,7 @@ namespace Bit.Core.Services
|
|||||||
return accessToken;
|
return accessToken;
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<SsoPrevalidateResponse> PreValidateSsoAsync(string identifier)
|
public async Task<SsoPrevalidateResponse> PreValidateSso(string identifier)
|
||||||
{
|
{
|
||||||
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
||||||
using (var requestMessage = new HttpRequestMessage())
|
using (var requestMessage = new HttpRequestMessage())
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
||||||
private MasterKey _masterKey;
|
private MasterKey _masterKey;
|
||||||
private UserKey _userKey;
|
|
||||||
|
|
||||||
private string _authedUserId;
|
private string _authedUserId;
|
||||||
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
||||||
@@ -201,13 +200,12 @@ namespace Bit.Core.Services
|
|||||||
return !await _policyService.EvaluateMasterPassword(strength.Value, masterPassword, _masterPasswordPolicy);
|
return !await _policyService.EvaluateMasterPassword(strength.Value, masterPassword, _masterPasswordPolicy);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthResult> LogInPasswordlessAsync(bool authingWithSso, string email, string accessCode, string authRequestId, byte[] decryptionKey, string encryptedAuthRequestKey, string masterKeyHash)
|
public async Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string encryptedAuthRequestKey, string masterKeyHash)
|
||||||
{
|
{
|
||||||
var decryptedKey = await _cryptoService.RsaDecryptAsync(encryptedAuthRequestKey, decryptionKey);
|
var decryptedKey = await _cryptoService.RsaDecryptAsync(encryptedAuthRequestKey, decryptionKey);
|
||||||
|
|
||||||
// If the user is already authenticated, we can just set the key
|
// On SSO flow user is already AuthN
|
||||||
// Note: We can't check for the existance of an access token here because the active user id may not be null
|
if (await _stateService.IsAuthenticatedAsync())
|
||||||
if (authingWithSso)
|
|
||||||
{
|
{
|
||||||
if (string.IsNullOrEmpty(masterKeyHash))
|
if (string.IsNullOrEmpty(masterKeyHash))
|
||||||
{
|
{
|
||||||
@@ -224,10 +222,10 @@ namespace Bit.Core.Services
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
// The approval device may not have a master key hash if it authenticated with a passwordless method
|
|
||||||
if (string.IsNullOrEmpty(masterKeyHash) && decryptionKey != null)
|
if (string.IsNullOrEmpty(masterKeyHash) && decryptionKey != null)
|
||||||
{
|
{
|
||||||
return await LogInHelperAsync(email, accessCode, null, null, null, null, null, null, null, null, null, authRequestId: authRequestId, userKey2FA: new UserKey(decryptedKey));
|
await _cryptoService.SetUserKeyAsync(new UserKey(decryptedKey));
|
||||||
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
var decKeyHash = await _cryptoService.RsaDecryptAsync(masterKeyHash, decryptionKey);
|
var decKeyHash = await _cryptoService.RsaDecryptAsync(masterKeyHash, decryptionKey);
|
||||||
@@ -238,13 +236,7 @@ namespace Bit.Core.Services
|
|||||||
public async Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId)
|
public async Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId)
|
||||||
{
|
{
|
||||||
SelectedTwoFactorProviderType = null;
|
SelectedTwoFactorProviderType = null;
|
||||||
var result = await LogInHelperAsync(null, null, null, code, codeVerifier, redirectUrl, null, orgId: orgId);
|
return await LogInHelperAsync(null, null, null, code, codeVerifier, redirectUrl, null, orgId: orgId);
|
||||||
if (result.ForcePasswordReset)
|
|
||||||
{
|
|
||||||
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.AdminForcePasswordReset);
|
|
||||||
}
|
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken,
|
public async Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken,
|
||||||
@@ -255,7 +247,7 @@ namespace Bit.Core.Services
|
|||||||
CaptchaToken = captchaToken;
|
CaptchaToken = captchaToken;
|
||||||
}
|
}
|
||||||
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _masterKey,
|
var result = await LogInHelperAsync(Email, MasterPasswordHash, LocalMasterPasswordHash, Code, CodeVerifier, SsoRedirectUrl, _masterKey,
|
||||||
twoFactorProvider, twoFactorToken, remember, CaptchaToken, authRequestId: AuthRequestId, userKey2FA: _userKey);
|
twoFactorProvider, twoFactorToken, remember, CaptchaToken, authRequestId: AuthRequestId);
|
||||||
|
|
||||||
// If we successfully authenticated and we have a saved _2faForcePasswordResetReason reason from LogInAsync()
|
// If we successfully authenticated and we have a saved _2faForcePasswordResetReason reason from LogInAsync()
|
||||||
if (!string.IsNullOrEmpty(_authedUserId) && _2faForcePasswordResetReason.HasValue)
|
if (!string.IsNullOrEmpty(_authedUserId) && _2faForcePasswordResetReason.HasValue)
|
||||||
@@ -400,7 +392,7 @@ namespace Bit.Core.Services
|
|||||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||||
string code, string codeVerifier, string redirectUrl, MasterKey masterKey,
|
string code, string codeVerifier, string redirectUrl, MasterKey masterKey,
|
||||||
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
TwoFactorProviderType? twoFactorProvider = null, string twoFactorToken = null, bool? remember = null,
|
||||||
string captchaToken = null, string orgId = null, string authRequestId = null, UserKey userKey2FA = null)
|
string captchaToken = null, string orgId = null, string authRequestId = null)
|
||||||
{
|
{
|
||||||
var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email);
|
var storedTwoFactorToken = await _tokenService.GetTwoFactorTokenAsync(email);
|
||||||
var appId = await _appIdService.GetAppIdAsync();
|
var appId = await _appIdService.GetAppIdAsync();
|
||||||
@@ -465,7 +457,6 @@ namespace Bit.Core.Services
|
|||||||
CodeVerifier = codeVerifier;
|
CodeVerifier = codeVerifier;
|
||||||
SsoRedirectUrl = redirectUrl;
|
SsoRedirectUrl = redirectUrl;
|
||||||
_masterKey = _setCryptoKeys ? masterKey : null;
|
_masterKey = _setCryptoKeys ? masterKey : null;
|
||||||
_userKey = userKey2FA;
|
|
||||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||||
@@ -547,10 +538,6 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||||
}
|
}
|
||||||
else if (userKey2FA != null)
|
|
||||||
{
|
|
||||||
await _cryptoService.SetUserKeyAsync(userKey2FA);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Decrypt UserKey with MasterKey
|
// Decrypt UserKey with MasterKey
|
||||||
masterKey ??= await _stateService.GetMasterKeyAsync();
|
masterKey ??= await _stateService.GetMasterKeyAsync();
|
||||||
|
|||||||
@@ -252,7 +252,7 @@ namespace Bit.Core.Services
|
|||||||
{
|
{
|
||||||
if (!await _cryptoService.HasUserKeyAsync())
|
if (!await _cryptoService.HasUserKeyAsync())
|
||||||
{
|
{
|
||||||
throw new UserKeyNullException();
|
throw new Exception("No key.");
|
||||||
}
|
}
|
||||||
var decCiphers = new List<CipherView>();
|
var decCiphers = new List<CipherView>();
|
||||||
async Task decryptAndAddCipherAsync(Cipher cipher)
|
async Task decryptAndAddCipherAsync(Cipher cipher)
|
||||||
|
|||||||
@@ -6,7 +6,6 @@ using System.Text;
|
|||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Enums;
|
using Bit.Core.Enums;
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Response;
|
using Bit.Core.Models.Response;
|
||||||
using Bit.Core.Utilities;
|
using Bit.Core.Utilities;
|
||||||
@@ -64,7 +63,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null)
|
public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null)
|
||||||
{
|
{
|
||||||
var userKey = await GetUserKeyAsync(userId);
|
var userKey = await GetUserKeyAsync();
|
||||||
if (userKey != null)
|
if (userKey != null)
|
||||||
{
|
{
|
||||||
return userKey;
|
return userKey;
|
||||||
@@ -72,7 +71,7 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
// Legacy support: encryption used to be done with the master key (derived from master password).
|
// Legacy support: encryption used to be done with the master key (derived from master password).
|
||||||
// Users who have not migrated will have a null user key and must use the master key instead.
|
// Users who have not migrated will have a null user key and must use the master key instead.
|
||||||
return new UserKey((await GetMasterKeyAsync(userId)).Key);
|
return new UserKey((await GetMasterKeyAsync()).Key);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> HasUserKeyAsync(string userId = null)
|
public async Task<bool> HasUserKeyAsync(string userId = null)
|
||||||
@@ -153,13 +152,9 @@ namespace Bit.Core.Services
|
|||||||
return _stateService.SetMasterKeyAsync(null, userId);
|
return _stateService.SetMasterKeyAsync(null, userId);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey, UserKey userKey = null)
|
public async Task<Tuple<UserKey, EncString>> EncryptUserKeyWithMasterKeyAsync(MasterKey masterKey)
|
||||||
{
|
{
|
||||||
userKey ??= await GetUserKeyAsync();
|
var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync();
|
||||||
if (userKey == null)
|
|
||||||
{
|
|
||||||
throw new UserKeyNullException();
|
|
||||||
}
|
|
||||||
return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
|
return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -168,7 +163,7 @@ namespace Bit.Core.Services
|
|||||||
masterKey ??= await GetMasterKeyAsync(userId);
|
masterKey ??= await GetMasterKeyAsync(userId);
|
||||||
if (masterKey == null)
|
if (masterKey == null)
|
||||||
{
|
{
|
||||||
throw new MasterKeyNullException();
|
throw new Exception("No master key found.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (encUserKey == null)
|
if (encUserKey == null)
|
||||||
@@ -1011,10 +1006,6 @@ namespace Bit.Core.Services
|
|||||||
// Decrypt
|
// Decrypt
|
||||||
var masterKey = new MasterKey(Convert.FromBase64String(oldKey));
|
var masterKey = new MasterKey(Convert.FromBase64String(oldKey));
|
||||||
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
|
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
|
||||||
if (encryptedUserKey == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
var userKey = await DecryptUserKeyWithMasterKeyAsync(
|
var userKey = await DecryptUserKeyWithMasterKeyAsync(
|
||||||
masterKey,
|
masterKey,
|
||||||
new EncString(encryptedUserKey),
|
new EncString(encryptedUserKey),
|
||||||
@@ -1074,11 +1065,8 @@ namespace Bit.Core.Services
|
|||||||
var encPin = await EncryptAsync(pin, userKey);
|
var encPin = await EncryptAsync(pin, userKey);
|
||||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||||
}
|
}
|
||||||
// Clear old key only if not needed for bio/auto migration
|
// Clear old key
|
||||||
if (await _stateService.GetKeyEncryptedAsync() != null)
|
|
||||||
{
|
|
||||||
await _stateService.SetEncKeyEncryptedAsync(null);
|
await _stateService.SetEncKeyEncryptedAsync(null);
|
||||||
}
|
|
||||||
return userKey;
|
return userKey;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -4,7 +4,6 @@ using System.Linq;
|
|||||||
using System.Text.RegularExpressions;
|
using System.Text.RegularExpressions;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Bit.Core.Abstractions;
|
using Bit.Core.Abstractions;
|
||||||
using Bit.Core.Exceptions;
|
|
||||||
using Bit.Core.Models.Data;
|
using Bit.Core.Models.Data;
|
||||||
using Bit.Core.Models.Domain;
|
using Bit.Core.Models.Domain;
|
||||||
using Bit.Core.Models.Request;
|
using Bit.Core.Models.Request;
|
||||||
@@ -81,7 +80,7 @@ namespace Bit.Core.Services
|
|||||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||||
if (!hasKey)
|
if (!hasKey)
|
||||||
{
|
{
|
||||||
throw new UserKeyNullException();
|
throw new Exception("No key.");
|
||||||
}
|
}
|
||||||
var decFolders = new List<FolderView>();
|
var decFolders = new List<FolderView>();
|
||||||
async Task decryptAndAddFolderAsync(Folder folder)
|
async Task decryptAndAddFolderAsync(Folder folder)
|
||||||
|
|||||||
@@ -68,14 +68,14 @@ namespace Bit.Core.Services
|
|||||||
try
|
try
|
||||||
{
|
{
|
||||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.EncKeyB64);
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.EncKeyB64);
|
||||||
await _apiService.PostMasterKeyToKeyConnectorAsync(organization.KeyConnectorUrl, keyConnectorRequest);
|
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
throw new Exception("Unable to reach Key Connector", e);
|
throw new Exception("Unable to reach Key Connector", e);
|
||||||
}
|
}
|
||||||
|
|
||||||
await _apiService.PostConvertToKeyConnectorAsync();
|
await _apiService.PostConvertToKeyConnector();
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> UserNeedsMigrationAsync()
|
public async Task<bool> UserNeedsMigrationAsync()
|
||||||
@@ -95,15 +95,13 @@ namespace Bit.Core.Services
|
|||||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
|
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
|
||||||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||||
|
|
||||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||||
newMasterKey,
|
|
||||||
await _cryptoService.MakeUserKeyAsync());
|
|
||||||
|
|
||||||
await _cryptoService.SetUserKeyAsync(newUserKey);
|
await _cryptoService.SetUserKeyAsync(newUserKey);
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
await _apiService.PostMasterKeyToKeyConnectorAsync(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
|
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
|
||||||
}
|
}
|
||||||
catch (Exception e)
|
catch (Exception e)
|
||||||
{
|
{
|
||||||
@@ -117,9 +115,9 @@ namespace Bit.Core.Services
|
|||||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||||
};
|
};
|
||||||
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
var setPasswordRequest = new SetKeyConnectorKeyRequest(
|
||||||
newProtectedUserKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
newProtectedPrivateKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
|
||||||
);
|
);
|
||||||
await _apiService.PostSetKeyConnectorKeyAsync(setPasswordRequest);
|
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1535,7 +1535,6 @@ namespace Bit.Core.Services
|
|||||||
SetUserKeyAutoUnlockAsync(null, userId),
|
SetUserKeyAutoUnlockAsync(null, userId),
|
||||||
SetUserKeyBiometricUnlockAsync(null, userId),
|
SetUserKeyBiometricUnlockAsync(null, userId),
|
||||||
SetProtectedPinAsync(null, userId),
|
SetProtectedPinAsync(null, userId),
|
||||||
SetPinKeyEncryptedUserKeyAsync(null, userId),
|
|
||||||
SetKeyHashAsync(null, userId),
|
SetKeyHashAsync(null, userId),
|
||||||
SetOrgKeysEncryptedAsync(null, userId),
|
SetOrgKeysEncryptedAsync(null, userId),
|
||||||
SetPrivateKeyEncryptedAsync(null, userId),
|
SetPrivateKeyEncryptedAsync(null, userId),
|
||||||
|
|||||||
@@ -68,20 +68,15 @@ namespace Bit.Core.Services
|
|||||||
await _platformUtilsService.ShowDialogAsync(errorMessage);
|
await _platformUtilsService.ShowDialogAsync(errorMessage);
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task<bool> HasMasterPasswordAsync(bool checkMasterKeyHash = false)
|
public async Task<bool> HasMasterPasswordAsync()
|
||||||
{
|
{
|
||||||
async Task<bool> CheckMasterKeyHashAsync()
|
|
||||||
{
|
|
||||||
return !checkMasterKeyHash || await _cryptoService.GetMasterKeyHashAsync() != null;
|
|
||||||
};
|
|
||||||
|
|
||||||
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
|
||||||
if (decryptOptions != null)
|
if (decryptOptions != null)
|
||||||
{
|
{
|
||||||
return decryptOptions.HasMasterPassword && await CheckMasterKeyHashAsync();
|
return decryptOptions.HasMasterPassword;
|
||||||
}
|
}
|
||||||
|
|
||||||
return !await _keyConnectorService.GetUsesKeyConnectorAsync() && await CheckMasterKeyHashAsync();
|
return !await _keyConnectorService.GetUsesKeyConnectorAsync();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,13 +59,6 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
public async Task<bool> IsLockedAsync(string userId = null)
|
public async Task<bool> IsLockedAsync(string userId = null)
|
||||||
{
|
{
|
||||||
// When checking if we need to lock inactive account, we don't want to
|
|
||||||
// set the key, so we only check if the account has an auto unlock key
|
|
||||||
if (userId != null && await _stateService.GetActiveUserIdAsync() != userId)
|
|
||||||
{
|
|
||||||
return await _cryptoService.HasAutoUnlockKeyAsync(userId);
|
|
||||||
}
|
|
||||||
|
|
||||||
var biometricSet = await IsBiometricLockSetAsync(userId);
|
var biometricSet = await IsBiometricLockSetAsync(userId);
|
||||||
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
||||||
{
|
{
|
||||||
@@ -74,11 +67,14 @@ namespace Bit.Core.Services
|
|||||||
|
|
||||||
if (!await _cryptoService.HasUserKeyAsync(userId))
|
if (!await _cryptoService.HasUserKeyAsync(userId))
|
||||||
{
|
{
|
||||||
if (!await _cryptoService.HasAutoUnlockKeyAsync(userId))
|
if (await _cryptoService.HasAutoUnlockKeyAsync(userId))
|
||||||
|
{
|
||||||
|
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId));
|
||||||
|
}
|
||||||
|
else
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId), userId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check again to verify auto key was set
|
// Check again to verify auto key was set
|
||||||
|
|||||||
@@ -313,7 +313,7 @@ namespace Bit.iOS.Autofill
|
|||||||
// Add a timeout to resolve keyboard not always showing up.
|
// Add a timeout to resolve keyboard not always showing up.
|
||||||
await Task.Delay(250);
|
await Task.Delay(250);
|
||||||
var passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
var passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||||
if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync())
|
if (!await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||||
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
|
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
|
||||||
@@ -511,11 +511,11 @@ namespace Bit.iOS.Autofill
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null, bool authingWithSso = false)
|
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, appOptions, authingWithSso);
|
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)
|
||||||
@@ -632,8 +632,8 @@ namespace Bit.iOS.Autofill
|
|||||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
{
|
{
|
||||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.8bit.bitwarden.autofill</string>
|
<string>com.8bit.bitwarden.autofill</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2023.8.0</string>
|
<string>2023.7.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleLocalizations</key>
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ namespace Bit.iOS.Autofill.Utilities
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await passwordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -367,8 +367,7 @@ namespace Bit.iOS.Core.Controllers
|
|||||||
await _stateService.SetBiometricLockedAsync(!success);
|
await _stateService.SetBiometricLockedAsync(!success);
|
||||||
if (success)
|
if (success)
|
||||||
{
|
{
|
||||||
var userKey = await _cryptoService.GetBiometricUnlockKeyAsync();
|
DoContinue();
|
||||||
await SetKeyAndContinueAsync(userKey);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -245,6 +245,7 @@ namespace Bit.iOS.Core.Utilities
|
|||||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||||
|
|
||||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||||
|
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
|
||||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||||
ServiceContainer.Resolve<IUserVerificationService>());
|
ServiceContainer.Resolve<IUserVerificationService>());
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.8bit.bitwarden.find-login-action-extension</string>
|
<string>com.8bit.bitwarden.find-login-action-extension</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2023.8.0</string>
|
<string>2023.7.1</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleLocalizations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
|
|||||||
@@ -533,11 +533,11 @@ namespace Bit.iOS.Extension
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null, bool authingWithSso = false)
|
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, appOptions, authingWithSso);
|
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)
|
||||||
@@ -654,8 +654,8 @@ namespace Bit.iOS.Extension
|
|||||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
{
|
{
|
||||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
|||||||
@@ -126,7 +126,7 @@ namespace Bit.iOS.Extension
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!await _controller.PasswordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await _controller.PasswordRepromptService.ShowPasswordPromptAsync())
|
||||||
{
|
{
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>XPC!</string>
|
<string>XPC!</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2023.8.0</string>
|
<string>2023.7.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>MinimumOSVersion</key>
|
<key>MinimumOSVersion</key>
|
||||||
|
|||||||
@@ -348,9 +348,9 @@ namespace Bit.iOS.ShareExtension
|
|||||||
LogoutIfAuthed();
|
LogoutIfAuthed();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null, bool authingWithSso = false)
|
private void LaunchLoginWithDevice(AuthRequestType authRequestType, string email = null)
|
||||||
{
|
{
|
||||||
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, _appOptions.Value, authingWithSso);
|
var loginWithDevicePage = new LoginPasswordlessRequestPage(email, authRequestType, _appOptions.Value);
|
||||||
SetupAppAndApplyResources(loginWithDevicePage);
|
SetupAppAndApplyResources(loginWithDevicePage);
|
||||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||||
{
|
{
|
||||||
@@ -438,8 +438,8 @@ namespace Bit.iOS.ShareExtension
|
|||||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||||
{
|
{
|
||||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||||
}
|
}
|
||||||
|
|
||||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>com.8bit.bitwarden</string>
|
<string>com.8bit.bitwarden</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
<string>2023.8.0</string>
|
<string>2023.7.1</string>
|
||||||
<key>CFBundleVersion</key>
|
<key>CFBundleVersion</key>
|
||||||
<string>1</string>
|
<string>1</string>
|
||||||
<key>CFBundleIconName</key>
|
<key>CFBundleIconName</key>
|
||||||
|
|||||||
Reference in New Issue
Block a user