mirror of
https://github.com/bitwarden/mobile
synced 2026-01-18 08:23:15 +00:00
[PM-2289] [PM-2293] TDE Login with device Admin Request (#2642)
This commit is contained in:
@@ -91,7 +91,7 @@ namespace Bit.Core.Abstractions
|
||||
Task<PasswordlessLoginResponse> GetAuthRequestAsync(string id);
|
||||
Task<PasswordlessLoginResponse> GetAuthResponseAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string key, string masterPasswordHash, string deviceIdentifier, bool requestApproved);
|
||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest);
|
||||
Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest, AuthRequestType authRequestType);
|
||||
Task<bool> GetKnownDeviceAsync(string email, string deviceIdentifier);
|
||||
Task<DeviceResponse> GetDeviceByIdentifierAsync(string deviceIdentifier);
|
||||
Task<DeviceResponse> UpdateTrustedDeviceKeysAsync(string deviceIdentifier, TrustedDeviceKeysRequest deviceRequest);
|
||||
|
||||
@@ -32,7 +32,10 @@ namespace Bit.Core.Abstractions
|
||||
Task<List<PasswordlessLoginResponse>> GetPasswordlessLoginRequestsAsync();
|
||||
Task<List<PasswordlessLoginResponse>> GetActivePasswordlessLoginRequestsAsync();
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id);
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode);
|
||||
/// <summary>
|
||||
/// Gets a passwordless login request by <paramref name="id"/> and <paramref name="accessCode"/>. No authentication required.
|
||||
/// </summary>
|
||||
Task<PasswordlessLoginResponse> GetPasswordlessLoginResquestAsync(string id, string accessCode);
|
||||
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
|
||||
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType);
|
||||
|
||||
|
||||
@@ -183,6 +183,8 @@ namespace Bit.Core.Abstractions
|
||||
Task<string> GetPreLoginEmailAsync();
|
||||
Task SetPreLoginEmailAsync(string value);
|
||||
Task<AccountDecryptionOptions> GetAccountDecryptionOptions(string userId = null);
|
||||
Task<PendingAdminAuthRequest> GetPendingAdminAuthRequestAsync(string userId = null);
|
||||
Task SetPendingAdminAuthRequestAsync(PendingAdminAuthRequest value, string userId = null);
|
||||
string GetLocale();
|
||||
void SetLocale(string locale);
|
||||
ConfigResponse GetConfigs();
|
||||
|
||||
@@ -124,6 +124,7 @@ namespace Bit.Core
|
||||
public static string PushCurrentTokenKey(string userId) => $"pushCurrentToken_{userId}";
|
||||
public static string ShouldConnectToWatchKey(string userId) => $"shouldConnectToWatch_{userId}";
|
||||
public static string ScreenCaptureAllowedKey(string userId) => $"screenCaptureAllowed_{userId}";
|
||||
public static string PendingAdminAuthRequest(string userId) => $"pendingAdminAuthRequest_{userId}";
|
||||
[Obsolete]
|
||||
public static string KeyKey(string userId) => $"key_{userId}";
|
||||
[Obsolete]
|
||||
|
||||
10
src/Core/Models/Domain/PendingAdminAuthRequest.cs
Normal file
10
src/Core/Models/Domain/PendingAdminAuthRequest.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using System;
|
||||
namespace Bit.Core.Models.Domain
|
||||
{
|
||||
public class PendingAdminAuthRequest
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public byte[] PrivateKey { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -591,9 +591,9 @@ namespace Bit.Core.Services
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Get, $"/auth-requests/{id}/response?code={accessCode}", null, false, true);
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest)
|
||||
public Task<PasswordlessLoginResponse> PostCreateRequestAsync(PasswordlessCreateLoginRequest passwordlessCreateLoginRequest, AuthRequestType authRequestType)
|
||||
{
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Post, $"/auth-requests", passwordlessCreateLoginRequest, false, true);
|
||||
return SendAsync<object, PasswordlessLoginResponse>(HttpMethod.Post, authRequestType == AuthRequestType.AdminApproval ? "/auth-requests/admin-request" : "/auth-requests", passwordlessCreateLoginRequest, authRequestType == AuthRequestType.AdminApproval, true);
|
||||
}
|
||||
|
||||
public Task<PasswordlessLoginResponse> PutAuthRequestAsync(string id, string encKey, string encMasterPasswordHash, string deviceIdentifier, bool requestApproved)
|
||||
|
||||
@@ -198,11 +198,34 @@ namespace Bit.Core.Services
|
||||
return !await _policyService.EvaluateMasterPassword(strength.Value, masterPassword, _masterPasswordPolicy);
|
||||
}
|
||||
|
||||
public async Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string userKeyCiphered, string localHashedPasswordCiphered)
|
||||
public async Task<AuthResult> LogInPasswordlessAsync(string email, string accessCode, string authRequestId, byte[] decryptionKey, string encryptedAuthRequestKey, string masterKeyHash)
|
||||
{
|
||||
var decKey = await _cryptoService.RsaDecryptAsync(userKeyCiphered, decryptionKey);
|
||||
var decPasswordHash = await _cryptoService.RsaDecryptAsync(localHashedPasswordCiphered, decryptionKey);
|
||||
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decPasswordHash), null, null, null, new MasterKey(decKey), null, null,
|
||||
var decryptedKey = await _cryptoService.RsaDecryptAsync(encryptedAuthRequestKey, decryptionKey);
|
||||
|
||||
// On SSO flow user is already AuthN
|
||||
if (await _stateService.IsAuthenticatedAsync())
|
||||
{
|
||||
if (string.IsNullOrEmpty(masterKeyHash))
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(new UserKey(decryptedKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(new MasterKey(decryptedKey));
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
await _deviceTrustCryptoService.TrustDeviceIfNeededAsync();
|
||||
return null;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(masterKeyHash) && decryptionKey != null)
|
||||
{
|
||||
await _cryptoService.SetUserKeyAsync(new UserKey(decryptedKey));
|
||||
return null;
|
||||
}
|
||||
|
||||
var decKeyHash = await _cryptoService.RsaDecryptAsync(masterKeyHash, decryptionKey);
|
||||
return await LogInHelperAsync(email, accessCode, Encoding.UTF8.GetString(decKeyHash), null, null, null, new MasterKey(decryptedKey), null, null,
|
||||
null, null, authRequestId: authRequestId);
|
||||
}
|
||||
|
||||
@@ -474,10 +497,12 @@ namespace Bit.Core.Services
|
||||
_messagingService.Send("accountAdded");
|
||||
if (_setCryptoKeys)
|
||||
{
|
||||
|
||||
if (localHashedPassword != null)
|
||||
{
|
||||
await _cryptoService.SetPasswordHashAsync(localHashedPassword);
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
|
||||
if (code == null || tokenResponse.Key != null)
|
||||
@@ -505,6 +530,14 @@ namespace Bit.Core.Services
|
||||
}
|
||||
}
|
||||
|
||||
// Login with Device
|
||||
if (masterKey != null && !string.IsNullOrEmpty(authRequestId))
|
||||
{
|
||||
await _cryptoService.SetMasterKeyAsync(masterKey);
|
||||
var userKey = await _cryptoService.DecryptUserKeyWithMasterKeyAsync(masterKey);
|
||||
await _cryptoService.SetUserKeyAsync(userKey);
|
||||
}
|
||||
|
||||
// User doesn't have a key pair yet (old account), let's generate one for them.
|
||||
if (tokenResponse.PrivateKey == null)
|
||||
{
|
||||
@@ -557,7 +590,6 @@ namespace Bit.Core.Services
|
||||
);
|
||||
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
_authedUserId = _tokenService.GetUserId();
|
||||
@@ -594,14 +626,14 @@ namespace Bit.Core.Services
|
||||
var activeRequests = requests.Where(r => !r.IsAnswered && !r.IsExpired).OrderByDescending(r => r.CreationDate).ToList();
|
||||
return await PopulateFingerprintPhrasesAsync(activeRequests);
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginRequestByIdAsync(string id)
|
||||
{
|
||||
var response = await _apiService.GetAuthRequestAsync(id);
|
||||
return await PopulateFingerprintPhraseAsync(response, await _stateService.GetEmailAsync());
|
||||
}
|
||||
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResponseAsync(string id, string accessCode)
|
||||
/// <inheritdoc />
|
||||
public async Task<PasswordlessLoginResponse> GetPasswordlessLoginResquestAsync(string id, string accessCode)
|
||||
{
|
||||
return await _apiService.GetAuthResponseAsync(id, accessCode);
|
||||
}
|
||||
@@ -631,7 +663,7 @@ namespace Bit.Core.Services
|
||||
var publicB64 = Convert.ToBase64String(keyPair.Item1);
|
||||
var accessCode = await _passwordGenerationService.GeneratePasswordAsync(PasswordGenerationOptions.CreateDefault.WithLength(25));
|
||||
var passwordlessCreateLoginRequest = new PasswordlessCreateLoginRequest(email, publicB64, deviceId, accessCode, authRequestType, fingerprintPhrase);
|
||||
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest);
|
||||
var response = await _apiService.PostCreateRequestAsync(passwordlessCreateLoginRequest, authRequestType);
|
||||
|
||||
if (response != null)
|
||||
{
|
||||
|
||||
@@ -336,12 +336,16 @@ namespace Bit.Core.Services
|
||||
|
||||
public async Task<string> GetUserKeyMasterKeyAsync(string userId = null)
|
||||
{
|
||||
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyKey(userId), false);
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
return await _storageMediatorService.GetAsync<string>(Constants.UserKeyKey(reconciledOptions.UserId), false);
|
||||
}
|
||||
|
||||
public async Task SetUserKeyMasterKeyAsync(string value, string userId = null)
|
||||
{
|
||||
await _storageMediatorService.SaveAsync(Constants.UserKeyKey(userId), value, false);
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultInMemoryOptionsAsync());
|
||||
await _storageMediatorService.SaveAsync(Constants.UserKeyKey(reconciledOptions.UserId), value, false);
|
||||
}
|
||||
|
||||
public async Task<bool> CanAccessPremiumAsync(string userId = null)
|
||||
@@ -1347,6 +1351,20 @@ namespace Bit.Core.Services
|
||||
await _storageMediatorService.SaveAsync(Constants.ShouldTrustDevice, value);
|
||||
}
|
||||
|
||||
public async Task<PendingAdminAuthRequest> GetPendingAdminAuthRequestAsync(string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
return await _storageMediatorService.GetAsync<PendingAdminAuthRequest>(Constants.PendingAdminAuthRequest(reconciledOptions.UserId), true);
|
||||
}
|
||||
|
||||
public async Task SetPendingAdminAuthRequestAsync(PendingAdminAuthRequest value, string userId = null)
|
||||
{
|
||||
var reconciledOptions = ReconcileOptions(new StorageOptions { UserId = userId },
|
||||
await GetDefaultStorageOptionsAsync());
|
||||
await _storageMediatorService.SaveAsync(Constants.PendingAdminAuthRequest(reconciledOptions.UserId), value, true);
|
||||
}
|
||||
|
||||
public ConfigResponse GetConfigs()
|
||||
{
|
||||
return _storageMediatorService.Get<ConfigResponse>(Constants.ConfigsKey);
|
||||
|
||||
Reference in New Issue
Block a user