mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +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);
|
||||
|
||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
|
||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||
ServiceContainer.Resolve<IUserVerificationService>());
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
<?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-permission android:name="android.permission.INTERNET" />
|
||||
<uses-permission android:name="android.permission.NFC" />
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.App.Abstractions
|
||||
{
|
||||
@@ -7,7 +6,7 @@ namespace Bit.App.Abstractions
|
||||
{
|
||||
string[] ProtectedFields { get; }
|
||||
|
||||
Task<bool> PromptAndCheckPasswordIfNeededAsync(CipherRepromptType repromptType = CipherRepromptType.Password);
|
||||
Task<bool> ShowPasswordPromptAsync();
|
||||
|
||||
Task<(string password, bool valid)> ShowPasswordPromptAndGetItAsync();
|
||||
}
|
||||
|
||||
@@ -50,13 +50,13 @@ namespace Bit.App.Pages
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@ namespace Bit.App.Pages
|
||||
private LoginPasswordlessRequestViewModel _vm;
|
||||
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();
|
||||
_appOptions = appOptions;
|
||||
@@ -21,7 +21,6 @@ namespace Bit.App.Pages
|
||||
_vm.Page = this;
|
||||
_vm.Email = email;
|
||||
_vm.AuthRequestType = authRequestType;
|
||||
_vm.AuthingWithSso = authingWithSso;
|
||||
_vm.StartTwoFactorAction = () => Device.BeginInvokeOnMainThread(async () => await StartTwoFactorAsync());
|
||||
_vm.LogInSuccessAction = () => Device.BeginInvokeOnMainThread(async () => await LogInSuccessAsync());
|
||||
_vm.UpdateTempPasswordAction = () => Device.BeginInvokeOnMainThread(async () => await UpdateTempPasswordAsync());
|
||||
|
||||
@@ -12,7 +12,6 @@ using Bit.App.Utilities;
|
||||
using Bit.Core;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Services;
|
||||
@@ -81,7 +80,6 @@ namespace Bit.App.Pages
|
||||
public Action LogInSuccessAction { get; set; }
|
||||
public Action UpdateTempPasswordAction { get; set; }
|
||||
public Action CloseAction { get; set; }
|
||||
public bool AuthingWithSso { get; set; }
|
||||
|
||||
public ICommand CreatePasswordlessLoginCommand { get; }
|
||||
public ICommand CloseCommand { get; }
|
||||
@@ -235,7 +233,7 @@ namespace Bit.App.Pages
|
||||
try
|
||||
{
|
||||
PasswordlessLoginResponse response = null;
|
||||
if (AuthingWithSso)
|
||||
if (await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
response = await _authService.GetPasswordlessLoginRequestByIdAsync(_requestId);
|
||||
}
|
||||
@@ -244,14 +242,14 @@ namespace Bit.App.Pages
|
||||
response = await _authService.GetPasswordlessLoginResquestAsync(_requestId, _requestAccessCode);
|
||||
}
|
||||
|
||||
if (response?.RequestApproved != true)
|
||||
if (response.RequestApproved == null || !response.RequestApproved.Value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
||||
@@ -278,10 +276,6 @@ namespace Bit.App.Pages
|
||||
await HandleLoginCompleteAsync();
|
||||
}
|
||||
}
|
||||
catch (ApiException ex) when (ex.Error?.StatusCode == System.Net.HttpStatusCode.BadRequest)
|
||||
{
|
||||
HandleException(ex);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
StartCheckLoginRequestStatus();
|
||||
@@ -349,6 +343,16 @@ namespace Bit.App.Pages
|
||||
_requestAccessCode = response.RequestAccessCode;
|
||||
_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);
|
||||
|
||||
var response = await _apiService.PreValidateSsoAsync(OrgIdentifier);
|
||||
var response = await _apiService.PreValidateSso(OrgIdentifier);
|
||||
|
||||
if (string.IsNullOrWhiteSpace(response?.Token))
|
||||
{
|
||||
@@ -211,46 +211,38 @@ namespace Bit.App.Pages
|
||||
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 (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
||||
// If user doesn't have a MP, but has reset password permission, they must set a MP
|
||||
if (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
else if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
{
|
||||
// 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 (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
else
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
return;
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
||||
if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
return;
|
||||
}
|
||||
// Device is trusted and has keys, so we can decrypt
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for pending Admin Auth requests before navigating to device approval options
|
||||
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
|
||||
if (pendingRequest != null)
|
||||
else if (pendingRequest != null)
|
||||
{
|
||||
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())
|
||||
{
|
||||
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
|
||||
// have one and they aren't using key connector.
|
||||
// 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();
|
||||
return;
|
||||
}
|
||||
|
||||
if (response.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
SsoAuthSuccessAction?.Invoke();
|
||||
}
|
||||
|
||||
@@ -178,10 +178,7 @@ namespace Bit.App.Pages
|
||||
Email = Email.Trim().ToLower();
|
||||
var kdfConfig = new KdfConfig(KdfType.PBKDF2_SHA256, Constants.Pbkdf2Iterations, null, null);
|
||||
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(MasterPassword, Email, kdfConfig);
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
||||
newMasterKey,
|
||||
await _cryptoService.MakeUserKeyAsync()
|
||||
);
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||
var hashedPassword = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey);
|
||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||
var request = new RegisterRequest
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Bit.App.Pages
|
||||
{
|
||||
await _deviceActionService.ShowLoadingAsync(AppResources.Loading);
|
||||
|
||||
await _apiService.PostLeaveOrganizationAsync(Organization.Id);
|
||||
await _apiService.PostLeaveOrganization(Organization.Id);
|
||||
await _syncService.FullSyncAsync(true);
|
||||
|
||||
await _deviceActionService.HideLoadingAsync();
|
||||
|
||||
@@ -169,8 +169,7 @@ namespace Bit.App.Pages
|
||||
var masterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.ServerAuthorization);
|
||||
var localMasterPasswordHash = await _cryptoService.HashMasterKeyAsync(MasterPassword, newMasterKey, HashPurpose.LocalAuthorization);
|
||||
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey,
|
||||
await _cryptoService.GetUserKeyAsync() ?? await _cryptoService.MakeUserKeyAsync());
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||
|
||||
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync(newUserKey);
|
||||
var request = new SetPasswordRequest
|
||||
|
||||
@@ -18,6 +18,7 @@ namespace Bit.App.Pages
|
||||
|
||||
private TwoFactorPageViewModel _vm;
|
||||
private bool _inited;
|
||||
private bool _authingWithSso;
|
||||
private string _orgIdentifier;
|
||||
|
||||
public TwoFactorPage(bool? authingWithSso = false, AppOptions appOptions = null, string orgIdentifier = null)
|
||||
|
||||
@@ -39,6 +39,7 @@ namespace Bit.App.Pages
|
||||
private TwoFactorProviderType? _selectedProviderType;
|
||||
private string _totpInstruction;
|
||||
private string _webVaultUrl = "https://vault.bitwarden.com";
|
||||
private bool _authingWithSso = false;
|
||||
private bool _enableContinue = false;
|
||||
private bool _showContinue = true;
|
||||
|
||||
@@ -143,6 +144,8 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
_authingWithSso = _authService.AuthingWithSso();
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(_environmentService.BaseUrl))
|
||||
{
|
||||
_webVaultUrl = _environmentService.BaseUrl;
|
||||
@@ -324,80 +327,47 @@ namespace Bit.App.Pages
|
||||
_messagingService.Send("listenYubiKeyOTP", false);
|
||||
_broadcasterService.Unsubscribe(nameof(TwoFactorPage));
|
||||
|
||||
if (decryptOptions?.TrustedDeviceOption != null)
|
||||
if (_authingWithSso && result.ResetMasterPassword)
|
||||
{
|
||||
if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
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 (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
}
|
||||
else if (result.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
}
|
||||
else if (await _deviceTrustCryptoService.IsDeviceTrustedAsync())
|
||||
{
|
||||
// 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 (!decryptOptions.HasMasterPassword &&
|
||||
decryptOptions.TrustedDeviceOption.HasManageResetPasswordPermission)
|
||||
{
|
||||
StartSetPasswordAction?.Invoke();
|
||||
return;
|
||||
}
|
||||
// Update temp password only if the device is trusted and therefore has a decrypted User Key set
|
||||
if (result.ForcePasswordReset)
|
||||
{
|
||||
UpdateTempPasswordAction?.Invoke();
|
||||
return;
|
||||
}
|
||||
|
||||
// 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 (authRequest?.RequestApproved == true)
|
||||
{
|
||||
var authResult = await _authService.LogInPasswordlessAsync(true, await _stateService.GetActiveUserEmailAsync(), authRequest.RequestAccessCode, pendingRequest.Id, pendingRequest.PrivateKey, authRequest.Key, authRequest.MasterPasswordHash);
|
||||
if (authResult == null && await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
await Xamarin.Essentials.MainThread.InvokeOnMainThreadAsync(
|
||||
() => _platformUtilsService.ShowToast("info", null, AppResources.LoginApproved));
|
||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
await TwoFactorAuthSuccessAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
await _stateService.SetPendingAdminAuthRequestAsync(null);
|
||||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
await TwoFactorAuthSuccessAsync();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
StartDeviceApprovalOptionsAction?.Invoke();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// 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))
|
||||
else
|
||||
{
|
||||
// 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;
|
||||
await TwoFactorAuthSuccessAsync();
|
||||
}
|
||||
|
||||
_syncService.FullSyncAsync(true).FireAndForget();
|
||||
await TwoFactorAuthSuccessAsync();
|
||||
}
|
||||
catch (ApiException e)
|
||||
{
|
||||
|
||||
@@ -65,7 +65,7 @@ namespace Bit.App.Pages
|
||||
_initialized = true;
|
||||
FileFormatSelectedIndex = FileFormatOptions.FindIndex(k => k.Key == "json");
|
||||
DisablePrivateVaultPolicyEnabled = await _policyService.PolicyAppliesToUser(PolicyType.DisablePersonalVaultExport);
|
||||
UseOTPVerification = !await _userVerificationService.HasMasterPasswordAsync(true);
|
||||
UseOTPVerification = !await _userVerificationService.HasMasterPasswordAsync();
|
||||
|
||||
if (UseOTPVerification)
|
||||
{
|
||||
@@ -163,7 +163,7 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync(true)
|
||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync()
|
||||
? VerificationType.MasterPassword
|
||||
: VerificationType.OTP;
|
||||
if (!await _userVerificationService.VerifyUser(Secret, verificationType))
|
||||
|
||||
@@ -82,7 +82,7 @@ namespace Bit.App.Pages
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -698,12 +698,12 @@ namespace Bit.App.Pages
|
||||
|
||||
public async Task<bool> PromptPasswordAsync()
|
||||
{
|
||||
if (_passwordReprompted)
|
||||
if (Cipher.Reprompt == CipherRepromptType.None || _passwordReprompted)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return _passwordReprompted = await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(Cipher.Reprompt);
|
||||
return _passwordReprompted = await _passwordRepromptService.ShowPasswordPromptAsync();
|
||||
}
|
||||
|
||||
private async Task<bool> CanCloneAsync()
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace Bit.App.Pages
|
||||
|
||||
if (_appOptions?.OtpData != null)
|
||||
{
|
||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -208,7 +208,7 @@ namespace Bit.App.Pages
|
||||
}
|
||||
else if (selection == AppResources.Autofill || selection == AppResources.AutofillAndSave)
|
||||
{
|
||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace Bit.App.Pages
|
||||
|
||||
var cipher = listItem.Cipher;
|
||||
|
||||
if (!await _passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
if (cipher.Reprompt != CipherRepromptType.None && !await _passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Bit.App.Abstractions;
|
||||
using Bit.App.Resources;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
|
||||
namespace Bit.App.Services
|
||||
{
|
||||
@@ -19,13 +18,8 @@ namespace Bit.App.Services
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -44,10 +38,5 @@ namespace Bit.App.Services
|
||||
|
||||
return await _cryptoService.CompareAndUpdateKeyHashAsync(password, null);
|
||||
}
|
||||
|
||||
private async Task<bool> ShouldByPassMasterPasswordRepromptAsync()
|
||||
{
|
||||
return await _cryptoService.GetMasterKeyHashAsync() is null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -99,55 +99,60 @@ namespace Bit.App.Utilities
|
||||
{
|
||||
await page.Navigation.PushModalAsync(new NavigationPage(new CipherDetailsPage(cipher.Id)));
|
||||
}
|
||||
else if (selection == AppResources.Edit
|
||||
&&
|
||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
else if (selection == AppResources.Edit)
|
||||
{
|
||||
await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id)));
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
await page.Navigation.PushModalAsync(new NavigationPage(new CipherAddEditPage(cipher.Id)));
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.CopyUsername)
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Type == CipherType.Login ? cipher.Login.Username : cipher.Fido2Key.UserName);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Username);
|
||||
}
|
||||
else if (selection == AppResources.CopyPassword
|
||||
&&
|
||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
else if (selection == AppResources.CopyPassword)
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
||||
}
|
||||
else if (selection == AppResources.CopyTotp
|
||||
&&
|
||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
if (!string.IsNullOrWhiteSpace(totp))
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
await clipboardService.CopyTextAsync(totp);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
||||
await clipboardService.CopyTextAsync(cipher.Login.Password);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Password);
|
||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedPassword, cipher.Id);
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.CopyTotp)
|
||||
{
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
var totpService = ServiceContainer.Resolve<ITotpService>("totpService");
|
||||
var totp = await totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
if (!string.IsNullOrWhiteSpace(totp))
|
||||
{
|
||||
await clipboardService.CopyTextAsync(totp);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.VerificationCodeTotp);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.Launch && cipher.CanLaunch)
|
||||
{
|
||||
platformUtilsService.LaunchUri(cipher.LaunchUri);
|
||||
}
|
||||
else if (selection == AppResources.CopyNumber
|
||||
&&
|
||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
else if (selection == AppResources.CopyNumber)
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Number);
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Card.Number);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.Number);
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.CopySecurityCode
|
||||
&&
|
||||
await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(cipher.Reprompt))
|
||||
else if (selection == AppResources.CopySecurityCode)
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode);
|
||||
eventService.CollectAsync(EventType.Cipher_ClientCopiedCardCode, cipher.Id).FireAndForget();
|
||||
if (cipher.Reprompt == CipherRepromptType.None || await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
await clipboardService.CopyTextAsync(cipher.Card.Code);
|
||||
platformUtilsService.ShowToastForCopiedValue(AppResources.SecurityCode);
|
||||
var task = eventService.CollectAsync(Core.Enums.EventType.Cipher_ClientCopiedCardCode, cipher.Id);
|
||||
}
|
||||
}
|
||||
else if (selection == AppResources.CopyNotes)
|
||||
{
|
||||
|
||||
@@ -60,6 +60,7 @@ namespace Bit.App.Utilities
|
||||
/// </summary>
|
||||
public class VerificationActionsFlowHelper : IVerificationActionsFlowHelper
|
||||
{
|
||||
private readonly IKeyConnectorService _keyConnectorService;
|
||||
private readonly IPasswordRepromptService _passwordRepromptService;
|
||||
private readonly ICryptoService _cryptoService;
|
||||
private readonly IUserVerificationService _userVerificationService;
|
||||
@@ -71,11 +72,12 @@ namespace Bit.App.Utilities
|
||||
|
||||
private readonly Dictionary<VerificationFlowAction, IActionFlowExecutioner> _actionExecutionerDictionary = new Dictionary<VerificationFlowAction, IActionFlowExecutioner>();
|
||||
|
||||
public VerificationActionsFlowHelper(
|
||||
public VerificationActionsFlowHelper(IKeyConnectorService keyConnectorService,
|
||||
IPasswordRepromptService passwordRepromptService,
|
||||
ICryptoService cryptoService,
|
||||
IUserVerificationService userVerificationService)
|
||||
{
|
||||
_keyConnectorService = keyConnectorService;
|
||||
_passwordRepromptService = passwordRepromptService;
|
||||
_cryptoService = cryptoService;
|
||||
_userVerificationService = userVerificationService;
|
||||
@@ -108,7 +110,7 @@ namespace Bit.App.Utilities
|
||||
|
||||
public async Task ValidateAndExecuteAsync()
|
||||
{
|
||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync(true)
|
||||
var verificationType = await _userVerificationService.HasMasterPasswordAsync()
|
||||
? VerificationType.MasterPassword
|
||||
: VerificationType.OTP;
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ namespace Bit.Core.Abstractions
|
||||
Task PutDeleteCipherAsync(string id);
|
||||
Task<CipherResponse> PutRestoreCipherAsync(string id);
|
||||
Task RefreshIdentityTokenAsync();
|
||||
Task<SsoPrevalidateResponse> PreValidateSsoAsync(string identifier);
|
||||
Task<SsoPrevalidateResponse> PreValidateSso(string identifier);
|
||||
Task<TResponse> SendAsync<TRequest, TResponse>(HttpMethod method, string path,
|
||||
TRequest body, bool authed, bool hasResponse, Action<HttpRequestMessage> alterRequest, bool logoutOnUnauthorized = true);
|
||||
Task<HttpResponseMessage> SendAsync(HttpRequestMessage requestMessage, CancellationToken cancellationToken = default);
|
||||
@@ -72,10 +72,10 @@ namespace Bit.Core.Abstractions
|
||||
Task PutOrganizationUserResetPasswordEnrollmentAsync(string orgId, string userId,
|
||||
OrganizationUserResetPasswordEnrollmentRequest request);
|
||||
Task<KeyConnectorUserKeyResponse> GetMasterKeyFromKeyConnectorAsync(string keyConnectorUrl);
|
||||
Task PostMasterKeyToKeyConnectorAsync(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||
Task PostSetKeyConnectorKeyAsync(SetKeyConnectorKeyRequest request);
|
||||
Task PostConvertToKeyConnectorAsync();
|
||||
Task PostLeaveOrganizationAsync(string id);
|
||||
Task PostUserKeyToKeyConnector(string keyConnectorUrl, KeyConnectorUserKeyRequest request);
|
||||
Task PostSetKeyConnectorKey(SetKeyConnectorKeyRequest request);
|
||||
Task PostConvertToKeyConnector();
|
||||
Task PostLeaveOrganization(string id);
|
||||
|
||||
Task<SendResponse> GetSendAsync(string id);
|
||||
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> 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> 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>> GetActivePasswordlessLoginRequestsAsync();
|
||||
|
||||
@@ -26,7 +26,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<MasterKey> GetMasterKeyAsync(string userId = null);
|
||||
Task<MasterKey> MakeMasterKeyAsync(string password, string email, KdfConfig kdfConfig);
|
||||
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<Tuple<SymmetricCryptoKey, EncString>> MakeDataEncKeyAsync(SymmetricCryptoKey key);
|
||||
Task<string> HashMasterKeyAsync(string password, MasterKey key, HashPurpose hashPurpose = HashPurpose.ServerAuthorization);
|
||||
|
||||
@@ -6,6 +6,6 @@ namespace Bit.Core.Abstractions
|
||||
public interface IUserVerificationService
|
||||
{
|
||||
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 VaultTimeoutActionKey(string userId) => $"vaultTimeoutAction_{userId}";
|
||||
public static string MasterKeyEncryptedUserKeyKey(string userId) => $"masterKeyEncryptedUserKey_{userId}";
|
||||
public static string UserKeyAutoUnlockKey(string userId) => $"userKeyAutoUnlock_{userId}";
|
||||
public static string UserKeyBiometricUnlockKey(string userId) => $"userKeyBiometricUnlock_{userId}";
|
||||
public static string UserKeyAutoUnlockKey(string userId) => $"autoUnlock_{userId}";
|
||||
public static string UserKeyBiometricUnlockKey(string userId) => $"biometricUnlock_{userId}";
|
||||
public static string CiphersKey(string userId) => $"ciphers_{userId}";
|
||||
public static string FoldersKey(string userId) => $"folders_{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;
|
||||
|
||||
namespace Bit.Core.Models.Domain
|
||||
@@ -9,8 +8,6 @@ namespace Bit.Core.Models.Domain
|
||||
public bool TwoFactor { get; set; }
|
||||
public bool CaptchaNeeded => !string.IsNullOrWhiteSpace(CaptchaSiteKey);
|
||||
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 ForcePasswordReset { 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);
|
||||
}
|
||||
|
||||
public Task PostConvertToKeyConnectorAsync()
|
||||
public Task PostConvertToKeyConnector()
|
||||
{
|
||||
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);
|
||||
}
|
||||
@@ -486,7 +486,7 @@ namespace Bit.Core.Services
|
||||
$"/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);
|
||||
}
|
||||
@@ -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())
|
||||
{
|
||||
@@ -627,7 +627,7 @@ namespace Bit.Core.Services
|
||||
return accessToken;
|
||||
}
|
||||
|
||||
public async Task<SsoPrevalidateResponse> PreValidateSsoAsync(string identifier)
|
||||
public async Task<SsoPrevalidateResponse> PreValidateSso(string identifier)
|
||||
{
|
||||
var path = "/account/prevalidate?domainHint=" + WebUtility.UrlEncode(identifier);
|
||||
using (var requestMessage = new HttpRequestMessage())
|
||||
|
||||
@@ -33,7 +33,6 @@ namespace Bit.Core.Services
|
||||
|
||||
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
|
||||
private MasterKey _masterKey;
|
||||
private UserKey _userKey;
|
||||
|
||||
private string _authedUserId;
|
||||
private MasterPasswordPolicyOptions _masterPasswordPolicy;
|
||||
@@ -201,13 +200,12 @@ namespace Bit.Core.Services
|
||||
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);
|
||||
|
||||
// If the user is already authenticated, we can just set the key
|
||||
// Note: We can't check for the existance of an access token here because the active user id may not be null
|
||||
if (authingWithSso)
|
||||
// On SSO flow user is already AuthN
|
||||
if (await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
if (string.IsNullOrEmpty(masterKeyHash))
|
||||
{
|
||||
@@ -224,10 +222,10 @@ namespace Bit.Core.Services
|
||||
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)
|
||||
{
|
||||
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);
|
||||
@@ -238,13 +236,7 @@ namespace Bit.Core.Services
|
||||
public async Task<AuthResult> LogInSsoAsync(string code, string codeVerifier, string redirectUrl, string orgId)
|
||||
{
|
||||
SelectedTwoFactorProviderType = null;
|
||||
var result = await LogInHelperAsync(null, null, null, code, codeVerifier, redirectUrl, null, orgId: orgId);
|
||||
if (result.ForcePasswordReset)
|
||||
{
|
||||
await _stateService.SetForcePasswordResetReasonAsync(ForcePasswordResetReason.AdminForcePasswordReset);
|
||||
}
|
||||
|
||||
return result;
|
||||
return await LogInHelperAsync(null, null, null, code, codeVerifier, redirectUrl, null, orgId: orgId);
|
||||
}
|
||||
|
||||
public async Task<AuthResult> LogInTwoFactorAsync(TwoFactorProviderType twoFactorProvider, string twoFactorToken,
|
||||
@@ -255,7 +247,7 @@ namespace Bit.Core.Services
|
||||
CaptchaToken = captchaToken;
|
||||
}
|
||||
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 (!string.IsNullOrEmpty(_authedUserId) && _2faForcePasswordResetReason.HasValue)
|
||||
@@ -400,7 +392,7 @@ namespace Bit.Core.Services
|
||||
private async Task<AuthResult> LogInHelperAsync(string email, string hashedPassword, string localHashedPassword,
|
||||
string code, string codeVerifier, string redirectUrl, MasterKey masterKey,
|
||||
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 appId = await _appIdService.GetAppIdAsync();
|
||||
@@ -465,7 +457,6 @@ namespace Bit.Core.Services
|
||||
CodeVerifier = codeVerifier;
|
||||
SsoRedirectUrl = redirectUrl;
|
||||
_masterKey = _setCryptoKeys ? masterKey : null;
|
||||
_userKey = userKey2FA;
|
||||
TwoFactorProvidersData = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
result.TwoFactorProviders = response.TwoFactorResponse.TwoFactorProviders2;
|
||||
CaptchaToken = response.TwoFactorResponse.CaptchaToken;
|
||||
@@ -547,10 +538,6 @@ namespace Bit.Core.Services
|
||||
{
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
}
|
||||
else if (userKey2FA != null)
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(userKey2FA);
|
||||
}
|
||||
|
||||
// Decrypt UserKey with MasterKey
|
||||
masterKey ??= await _stateService.GetMasterKeyAsync();
|
||||
|
||||
@@ -252,7 +252,7 @@ namespace Bit.Core.Services
|
||||
{
|
||||
if (!await _cryptoService.HasUserKeyAsync())
|
||||
{
|
||||
throw new UserKeyNullException();
|
||||
throw new Exception("No key.");
|
||||
}
|
||||
var decCiphers = new List<CipherView>();
|
||||
async Task decryptAndAddCipherAsync(Cipher cipher)
|
||||
|
||||
@@ -6,7 +6,6 @@ using System.Text;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Enums;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Response;
|
||||
using Bit.Core.Utilities;
|
||||
@@ -64,7 +63,7 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<UserKey> GetUserKeyWithLegacySupportAsync(string userId = null)
|
||||
{
|
||||
var userKey = await GetUserKeyAsync(userId);
|
||||
var userKey = await GetUserKeyAsync();
|
||||
if (userKey != null)
|
||||
{
|
||||
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).
|
||||
// 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)
|
||||
@@ -153,13 +152,9 @@ namespace Bit.Core.Services
|
||||
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();
|
||||
if (userKey == null)
|
||||
{
|
||||
throw new UserKeyNullException();
|
||||
}
|
||||
var userKey = await GetUserKeyAsync() ?? await MakeUserKeyAsync();
|
||||
return await BuildProtectedSymmetricKeyAsync(masterKey, userKey.Key, keyBytes => new UserKey(keyBytes));
|
||||
}
|
||||
|
||||
@@ -168,7 +163,7 @@ namespace Bit.Core.Services
|
||||
masterKey ??= await GetMasterKeyAsync(userId);
|
||||
if (masterKey == null)
|
||||
{
|
||||
throw new MasterKeyNullException();
|
||||
throw new Exception("No master key found.");
|
||||
}
|
||||
|
||||
if (encUserKey == null)
|
||||
@@ -1011,10 +1006,6 @@ namespace Bit.Core.Services
|
||||
// Decrypt
|
||||
var masterKey = new MasterKey(Convert.FromBase64String(oldKey));
|
||||
var encryptedUserKey = await _stateService.GetEncKeyEncryptedAsync(userId);
|
||||
if (encryptedUserKey == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
var userKey = await DecryptUserKeyWithMasterKeyAsync(
|
||||
masterKey,
|
||||
new EncString(encryptedUserKey),
|
||||
@@ -1074,11 +1065,8 @@ namespace Bit.Core.Services
|
||||
var encPin = await EncryptAsync(pin, userKey);
|
||||
await _stateService.SetProtectedPinAsync(encPin.EncryptedString);
|
||||
}
|
||||
// Clear old key only if not needed for bio/auto migration
|
||||
if (await _stateService.GetKeyEncryptedAsync() != null)
|
||||
{
|
||||
await _stateService.SetEncKeyEncryptedAsync(null);
|
||||
}
|
||||
// Clear old key
|
||||
await _stateService.SetEncKeyEncryptedAsync(null);
|
||||
return userKey;
|
||||
}
|
||||
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Linq;
|
||||
using System.Text.RegularExpressions;
|
||||
using System.Threading.Tasks;
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Exceptions;
|
||||
using Bit.Core.Models.Data;
|
||||
using Bit.Core.Models.Domain;
|
||||
using Bit.Core.Models.Request;
|
||||
@@ -81,7 +80,7 @@ namespace Bit.Core.Services
|
||||
var hasKey = await _cryptoService.HasUserKeyAsync();
|
||||
if (!hasKey)
|
||||
{
|
||||
throw new UserKeyNullException();
|
||||
throw new Exception("No key.");
|
||||
}
|
||||
var decFolders = new List<FolderView>();
|
||||
async Task decryptAndAddFolderAsync(Folder folder)
|
||||
|
||||
@@ -68,14 +68,14 @@ namespace Bit.Core.Services
|
||||
try
|
||||
{
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(masterKey.EncKeyB64);
|
||||
await _apiService.PostMasterKeyToKeyConnectorAsync(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||
await _apiService.PostUserKeyToKeyConnector(organization.KeyConnectorUrl, keyConnectorRequest);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception("Unable to reach Key Connector", e);
|
||||
}
|
||||
|
||||
await _apiService.PostConvertToKeyConnectorAsync();
|
||||
await _apiService.PostConvertToKeyConnector();
|
||||
}
|
||||
|
||||
public async Task<bool> UserNeedsMigrationAsync()
|
||||
@@ -95,15 +95,13 @@ namespace Bit.Core.Services
|
||||
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
|
||||
await _cryptoService.SetMasterKeyAsync(newMasterKey);
|
||||
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
|
||||
newMasterKey,
|
||||
await _cryptoService.MakeUserKeyAsync());
|
||||
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(newMasterKey);
|
||||
|
||||
await _cryptoService.SetUserKeyAsync(newUserKey);
|
||||
|
||||
try
|
||||
{
|
||||
await _apiService.PostMasterKeyToKeyConnectorAsync(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
|
||||
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
@@ -117,9 +115,9 @@ namespace Bit.Core.Services
|
||||
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
|
||||
};
|
||||
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),
|
||||
SetUserKeyBiometricUnlockAsync(null, userId),
|
||||
SetProtectedPinAsync(null, userId),
|
||||
SetPinKeyEncryptedUserKeyAsync(null, userId),
|
||||
SetKeyHashAsync(null, userId),
|
||||
SetOrgKeysEncryptedAsync(null, userId),
|
||||
SetPrivateKeyEncryptedAsync(null, userId),
|
||||
|
||||
@@ -68,20 +68,15 @@ namespace Bit.Core.Services
|
||||
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();
|
||||
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)
|
||||
{
|
||||
// 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);
|
||||
if (biometricSet && await _stateService.GetBiometricLockedAsync(userId))
|
||||
{
|
||||
@@ -74,11 +67,14 @@ namespace Bit.Core.Services
|
||||
|
||||
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;
|
||||
}
|
||||
await _cryptoService.SetUserKeyAsync(await _cryptoService.GetAutoUnlockKeyAsync(userId), userId);
|
||||
}
|
||||
|
||||
// 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.
|
||||
await Task.Delay(250);
|
||||
var passwordRepromptService = ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService");
|
||||
if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync())
|
||||
if (!await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
var err = new NSError(new NSString("ASExtensionErrorDomain"),
|
||||
Convert.ToInt32(ASExtensionErrorCode.UserCanceled), null);
|
||||
@@ -511,11 +511,11 @@ namespace Bit.iOS.Autofill
|
||||
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 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.ApplyResourcesTo(loginWithDevicePage);
|
||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||
@@ -632,8 +632,8 @@ namespace Bit.iOS.Autofill
|
||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||
{
|
||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden.autofill</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2023.8.0</string>
|
||||
<string>2023.7.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
|
||||
@@ -33,7 +33,7 @@ namespace Bit.iOS.Autofill.Utilities
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await passwordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
||||
if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await passwordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -367,8 +367,7 @@ namespace Bit.iOS.Core.Controllers
|
||||
await _stateService.SetBiometricLockedAsync(!success);
|
||||
if (success)
|
||||
{
|
||||
var userKey = await _cryptoService.GetBiometricUnlockKeyAsync();
|
||||
await SetKeyAndContinueAsync(userKey);
|
||||
DoContinue();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -245,6 +245,7 @@ namespace Bit.iOS.Core.Utilities
|
||||
ServiceContainer.Register<IDeleteAccountActionFlowExecutioner>("deleteAccountActionFlowExecutioner", deleteAccountActionFlowExecutioner);
|
||||
|
||||
var verificationActionsFlowHelper = new VerificationActionsFlowHelper(
|
||||
ServiceContainer.Resolve<IKeyConnectorService>("keyConnectorService"),
|
||||
ServiceContainer.Resolve<IPasswordRepromptService>("passwordRepromptService"),
|
||||
ServiceContainer.Resolve<ICryptoService>("cryptoService"),
|
||||
ServiceContainer.Resolve<IUserVerificationService>());
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden.find-login-action-extension</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2023.8.0</string>
|
||||
<string>2023.7.1</string>
|
||||
<key>CFBundleLocalizations</key>
|
||||
<array>
|
||||
<string>en</string>
|
||||
|
||||
@@ -533,11 +533,11 @@ namespace Bit.iOS.Extension
|
||||
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 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.ApplyResourcesTo(loginWithDevicePage);
|
||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||
@@ -654,8 +654,8 @@ namespace Bit.iOS.Extension
|
||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||
{
|
||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||
|
||||
@@ -126,7 +126,7 @@ namespace Bit.iOS.Extension
|
||||
return;
|
||||
}
|
||||
|
||||
if (!await _controller.PasswordRepromptService.PromptAndCheckPasswordIfNeededAsync(item.Reprompt))
|
||||
if (item.Reprompt != Bit.Core.Enums.CipherRepromptType.None && !await _controller.PasswordRepromptService.ShowPasswordPromptAsync())
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<key>CFBundlePackageType</key>
|
||||
<string>XPC!</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2023.8.0</string>
|
||||
<string>2023.7.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>MinimumOSVersion</key>
|
||||
|
||||
@@ -348,9 +348,9 @@ namespace Bit.iOS.ShareExtension
|
||||
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);
|
||||
if (loginWithDevicePage.BindingContext is LoginPasswordlessRequestViewModel vm)
|
||||
{
|
||||
@@ -438,8 +438,8 @@ namespace Bit.iOS.ShareExtension
|
||||
if (loginApproveDevicePage.BindingContext is LoginApproveDeviceViewModel vm)
|
||||
{
|
||||
vm.LogInWithMasterPasswordAction = () => DismissViewController(false, () => PerformSegue("lockPasswordSegue", this));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email, true));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email, true));
|
||||
vm.RequestAdminApprovalAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AdminApproval, vm.Email));
|
||||
vm.LogInWithDeviceAction = () => DismissViewController(false, () => LaunchLoginWithDevice(AuthRequestType.AuthenticateAndUnlock, vm.Email));
|
||||
}
|
||||
|
||||
var navigationPage = new NavigationPage(loginApproveDevicePage);
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
<key>CFBundleIdentifier</key>
|
||||
<string>com.8bit.bitwarden</string>
|
||||
<key>CFBundleShortVersionString</key>
|
||||
<string>2023.8.0</string>
|
||||
<string>2023.7.1</string>
|
||||
<key>CFBundleVersion</key>
|
||||
<string>1</string>
|
||||
<key>CFBundleIconName</key>
|
||||
|
||||
Reference in New Issue
Block a user