1
0
mirror of https://github.com/bitwarden/mobile synced 2025-12-05 23:53:33 +00:00

[PM-1208] Add TDE flows for new users (#2655)

* [PM-1208] Create new user on SSO. Logout if not password is setup or has pending admin auth request.

* [PM-1208] Fix new user UserKey decryption.

* [PM-1208] Add new user continue to vault logic. Auto enrol user on continue.

* [PM-1208] Trust device only if needed

* [PM-1208] Add logic for New User SSO.

* [PM-1208] Add logic for New User SSO (missing file).
This commit is contained in:
André Bispo
2023-08-03 18:20:51 +01:00
committed by GitHub
parent 78788276ef
commit c75bcccf20
15 changed files with 271 additions and 70 deletions

View File

@@ -164,6 +164,13 @@ namespace Bit.App.Pages
public async Task InitAsync()
{
var pendingRequest = await _stateService.GetPendingAdminAuthRequestAsync();
if (pendingRequest != null)
{
await _vaultTimeoutService.LogOutAsync();
return;
}
_pinStatus = await _vaultTimeoutService.IsPinLockSetAsync();
var ephemeralPinSet = await _stateService.GetUserKeyPinEphemeralAsync()
@@ -173,6 +180,17 @@ namespace Bit.App.Pages
BiometricEnabled = await _vaultTimeoutService.IsBiometricLockSetAsync() && await _cryptoService.HasEncryptedUserKeyAsync();
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
if (await _stateService.IsAuthenticatedAsync()
&& decryptOptions?.TrustedDeviceOption != null
&& !decryptOptions.HasMasterPassword
&& !BiometricEnabled
&& !PinEnabled)
{
await _vaultTimeoutService.LogOutAsync();
return;
}
// Users with key connector and without biometric or pin has no MP to unlock with
_usingKeyConnector = await _keyConnectorService.GetUsesKeyConnector();
if (_usingKeyConnector && !(BiometricEnabled || PinEnabled))

View File

@@ -34,7 +34,7 @@
Text="{u:I18n Continue}"
StyleClass="btn-primary"
Command="{Binding ContinueCommand}"
IsVisible="{Binding ContinueEnabled}"/>
IsVisible="{Binding IsNewUser}"/>
<Button
x:Name="_approveWithMyOtherDevice"
Text="{u:I18n ApproveWithMyOtherDevice}"

View File

@@ -19,9 +19,10 @@ namespace Bit.App.Pages
{
InitializeComponent();
_vm = BindingContext as LoginApproveDeviceViewModel;
_vm.LogInWithMasterPasswordAction = () => StartLogInWithMasterPassword().FireAndForget();
_vm.LogInWithMasterPasswordAction = () => StartLogInWithMasterPasswordAsync().FireAndForget();
_vm.LogInWithDeviceAction = () => StartLoginWithDeviceAsync().FireAndForget();
_vm.RequestAdminApprovalAction = () => RequestAdminApprovalAsync().FireAndForget();
_vm.ContinueToVaultAction = () => ContinueToVaultAsync().FireAndForget();
_vm.CloseAction = () => { Navigation.PopModalAsync(); };
_vm.Page = this;
_appOptions = appOptions;
@@ -40,7 +41,17 @@ namespace Bit.App.Pages
}
}
private async Task StartLogInWithMasterPassword()
private async Task ContinueToVaultAsync()
{
if (AppHelpers.SetAlternateMainPage(_appOptions))
{
return;
}
var previousPage = await AppHelpers.ClearPreviousPage();
Application.Current.MainPage = new TabsPage(_appOptions, previousPage);
}
private async Task StartLogInWithMasterPasswordAsync()
{
var page = new LockPage(_appOptions);
await Navigation.PushModalAsync(new NavigationPage(page));

View File

@@ -1,4 +1,5 @@
using System;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
using System.Windows.Input;
using Bit.App.Abstractions;
@@ -6,6 +7,7 @@ using Bit.App.Resources;
using Bit.App.Utilities.AccountManagement;
using Bit.Core.Abstractions;
using Bit.Core.Enums;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Services;
using Bit.Core.Utilities;
@@ -21,11 +23,12 @@ namespace Bit.App.Pages
private bool _approveWithMyOtherDeviceEnabled;
private bool _requestAdminApprovalEnabled;
private bool _approveWithMasterPasswordEnabled;
private bool _continueEnabled;
private string _email;
private readonly IStateService _stateService;
private readonly IApiService _apiService;
private IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly IAuthService _authService;
private readonly ISyncService _syncService;
public ICommand ApproveWithMyOtherDeviceCommand { get; }
public ICommand RequestAdminApprovalCommand { get; }
@@ -35,6 +38,7 @@ namespace Bit.App.Pages
public Action LogInWithMasterPasswordAction { get; set; }
public Action LogInWithDeviceAction { get; set; }
public Action RequestAdminApprovalAction { get; set; }
public Action ContinueToVaultAction { get; set; }
public Action CloseAction { get; set; }
public LoginApproveDeviceViewModel()
@@ -42,6 +46,8 @@ namespace Bit.App.Pages
_stateService = ServiceContainer.Resolve<IStateService>();
_apiService = ServiceContainer.Resolve<IApiService>();
_deviceTrustCryptoService = ServiceContainer.Resolve<IDeviceTrustCryptoService>();
_authService = ServiceContainer.Resolve<IAuthService>();
_syncService = ServiceContainer.Resolve<ISyncService>();
PageTitle = AppResources.LoggedIn;
@@ -57,7 +63,7 @@ namespace Bit.App.Pages
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
ContinueCommand = new AsyncCommand(InitAsync,
ContinueCommand = new AsyncCommand(CreateNewSsoUserAsync,
onException: ex => HandleException(ex),
allowsMultipleExecutions: false);
}
@@ -79,20 +85,18 @@ namespace Bit.App.Pages
public bool RequestAdminApprovalEnabled
{
get => _requestAdminApprovalEnabled;
set => SetProperty(ref _requestAdminApprovalEnabled, value);
set => SetProperty(ref _requestAdminApprovalEnabled, value,
additionalPropertyNames: new[] { nameof(IsNewUser) });
}
public bool ApproveWithMasterPasswordEnabled
{
get => _approveWithMasterPasswordEnabled;
set => SetProperty(ref _approveWithMasterPasswordEnabled, value);
set => SetProperty(ref _approveWithMasterPasswordEnabled, value,
additionalPropertyNames: new[] { nameof(IsNewUser) });
}
public bool ContinueEnabled
{
get => _continueEnabled;
set => SetProperty(ref _continueEnabled, value);
}
public bool IsNewUser => !RequestAdminApprovalEnabled && !ApproveWithMasterPasswordEnabled;
public string Email
{
@@ -117,9 +121,18 @@ namespace Bit.App.Pages
{
HandleException(ex);
}
}
// TODO: Change this expression to, Appear if the browser is trusted and shared the key with the app
ContinueEnabled = !RequestAdminApprovalEnabled && !ApproveWithMasterPasswordEnabled && !ApproveWithMyOtherDeviceEnabled;
public async Task CreateNewSsoUserAsync()
{
await _authService.CreateNewSsoUserAsync(await _stateService.GetRememberedOrgIdentifierAsync());
if (RememberThisDevice)
{
await _deviceTrustCryptoService.TrustDeviceAsync();
}
_syncService.FullSyncAsync(true).FireAndForget();
await Device.InvokeOnMainThreadAsync(ContinueToVaultAction);
}
private async Task SetDeviceTrustAndInvokeAsync(Action action)

View File

@@ -209,17 +209,12 @@ namespace Bit.App.Pages
if (response.TwoFactor)
{
StartTwoFactorAction?.Invoke();
return;
}
else if (response.ResetMasterPassword)
{
StartSetPasswordAction?.Invoke();
}
else if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
}
else if (decryptOptions?.TrustedDeviceOption != null)
if (decryptOptions?.TrustedDeviceOption != null)
{
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)
@@ -235,18 +230,52 @@ namespace Bit.App.Pages
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
}
else if (pendingRequest != null)
{
var authRequest = await _authService.GetPasswordlessLoginRequestByIdAsync(pendingRequest.Id);
if (authRequest != null && authRequest.RequestApproved != null && authRequest.RequestApproved.Value)
{
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(
() => _platformUtilsService.ShowToast("info", null, AppResources.LoginApproved));
await _stateService.SetPendingAdminAuthRequestAsync(null);
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
}
}
else
{
StartDeviceApprovalOptionsAction?.Invoke();
}
}
else
{
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 (response.ResetMasterPassword || (decryptOptions?.RequireSetPassword ?? false))
{
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
StartSetPasswordAction?.Invoke();
return;
}
if (response.ForcePasswordReset)
{
UpdateTempPasswordAction?.Invoke();
return;
}
_syncService.FullSyncAsync(true).FireAndForget();
SsoAuthSuccessAction?.Invoke();
}
catch (Exception e)
catch (Exception)
{
await _deviceActionService.HideLoadingAsync();
await _platformUtilsService.ShowDialogAsync(AppResources.LoginSsoError,

View File

@@ -3784,6 +3784,15 @@ namespace Bit.App.Resources {
}
}
/// <summary>
/// Looks up a localized string similar to Login approved.
/// </summary>
public static string LoginApproved {
get {
return ResourceManager.GetString("LoginApproved", resourceCulture);
}
}
/// <summary>
/// Looks up a localized string similar to Login attempt by {0} on {1}.
/// </summary>

View File

@@ -2750,4 +2750,7 @@ Do you want to switch to this account?</value>
<data name="CannotEditMultipleURIsAtOnce" xml:space="preserve">
<value>Cannot edit multiple URIs at once</value>
</data>
<data name="LoginApproved" xml:space="preserve">
<value>Login approved</value>
</data>
</root>

View File

@@ -38,6 +38,7 @@ namespace Bit.Core.Abstractions
Task<PasswordlessLoginResponse> GetPasswordlessLoginResquestAsync(string id, string accessCode);
Task<PasswordlessLoginResponse> PasswordlessLoginAsync(string id, string pubKey, bool requestApproved);
Task<PasswordlessLoginResponse> PasswordlessCreateLoginRequestAsync(string email, AuthRequestType authRequestType);
Task CreateNewSsoUserAsync(string organizationSsoId);
void LogOut(Action callback);
void Init();

View File

@@ -1,6 +1,7 @@
using System;
using System.Threading.Tasks;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Response;
namespace Bit.Core.Abstractions
{
@@ -10,7 +11,8 @@ namespace Bit.Core.Abstractions
Task<bool> GetUsesKeyConnector();
Task<bool> UserNeedsMigration();
Task MigrateUser();
Task GetAndSetKey(string url);
Task GetAndSetKeyAsync(string url);
Task<Organization> GetManagingOrganization();
Task ConvertNewUserToKeyConnectorAsync(string orgId, IdentityTokenResponse tokenResponse);
}
}

View File

@@ -0,0 +1,12 @@
using System;
using System.Threading.Tasks;
namespace Bit.Core.Abstractions
{
public interface IPasswordResetEnrollmentService
{
Task EnrollIfRequiredAsync(string organizationSsoId);
Task EnrollAsync(string organizationId);
}
}

View File

@@ -6,6 +6,8 @@ namespace Bit.Core.Models.Domain
public bool HasMasterPassword { get; set; }
public TrustedDeviceOption TrustedDeviceOption { get; set; }
public KeyConnectorOption KeyConnectorOption { get; set; }
public bool RequireSetPassword => !HasMasterPassword && KeyConnectorOption == null;
}
public class TrustedDeviceOption

View File

@@ -28,6 +28,7 @@ namespace Bit.Core.Services
private readonly IPasswordGenerationService _passwordGenerationService;
private readonly IPolicyService _policyService;
private readonly IDeviceTrustCryptoService _deviceTrustCryptoService;
private readonly IPasswordResetEnrollmentService _passwordResetEnrollmentService;
private readonly bool _setCryptoKeys;
private readonly LazyResolve<IWatchDeviceService> _watchDeviceService = new LazyResolve<IWatchDeviceService>();
@@ -52,6 +53,7 @@ namespace Bit.Core.Services
IPasswordGenerationService passwordGenerationService,
IPolicyService policyService,
IDeviceTrustCryptoService deviceTrustCryptoService,
IPasswordResetEnrollmentService passwordResetEnrollmentService,
bool setCryptoKeys = true)
{
_cryptoService = cryptoService;
@@ -67,6 +69,7 @@ namespace Bit.Core.Services
_passwordGenerationService = passwordGenerationService;
_policyService = policyService;
_deviceTrustCryptoService = deviceTrustCryptoService;
_passwordResetEnrollmentService = passwordResetEnrollmentService;
_setCryptoKeys = setCryptoKeys;
TwoFactorProviders = new Dictionary<TwoFactorProviderType, TwoFactorProvider>();
@@ -505,20 +508,21 @@ namespace Bit.Core.Services
await _cryptoService.SetUserKeyAsync(userKey);
}
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
var hasUserKey = await _cryptoService.HasUserKeyAsync();
if (decryptOptions?.TrustedDeviceOption != null && !hasUserKey)
{
var key = await _deviceTrustCryptoService.DecryptUserKeyWithDeviceKeyAsync(decryptOptions.TrustedDeviceOption.EncryptedPrivateKey, decryptOptions.TrustedDeviceOption.EncryptedUserKey);
if (key != null)
{
await _cryptoService.SetUserKeyAsync(key);
}
}
if (code == null || tokenResponse.Key != null)
{
var decryptOptions = await _stateService.GetAccountDecryptionOptions();
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
if (decryptOptions?.TrustedDeviceOption != null)
{
var key = await _deviceTrustCryptoService.DecryptUserKeyWithDeviceKeyAsync(decryptOptions.TrustedDeviceOption.EncryptedPrivateKey, decryptOptions.TrustedDeviceOption.EncryptedUserKey);
if (key != null)
{
await _cryptoService.SetUserKeyAsync(key);
}
}
else if (!string.IsNullOrEmpty(tokenResponse.KeyConnectorUrl) || !string.IsNullOrEmpty(decryptOptions?.KeyConnectorOption?.KeyConnectorUrl))
if (!string.IsNullOrEmpty(tokenResponse.KeyConnectorUrl) || !string.IsNullOrEmpty(decryptOptions?.KeyConnectorOption?.KeyConnectorUrl))
{
await _cryptoService.SetMasterKeyEncryptedUserKeyAsync(tokenResponse.Key);
@@ -558,37 +562,15 @@ namespace Bit.Core.Services
}
else if (tokenResponse.KeyConnectorUrl != null)
{
// SSO Key Connector Onboarding
var password = await _cryptoFunctionService.RandomBytesAsync(64);
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
newMasterKey,
await _cryptoService.MakeUserKeyAsync());
await _cryptoService.SetUserKeyAsync(newUserKey);
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
try
// New User has tokenResponse.Key == null
if (tokenResponse.Key == null)
{
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
await _keyConnectorService.ConvertNewUserToKeyConnectorAsync(orgId, tokenResponse);
}
catch (Exception e)
else
{
throw new Exception("Unable to reach Key Connector", e);
await _keyConnectorService.GetAndSetKeyAsync(tokenResponse.KeyConnectorUrl);
}
var keys = new KeysRequest
{
PublicKey = newPublicKey,
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
};
var setPasswordRequest = new SetKeyConnectorKeyRequest(
newProtectedPrivateKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
);
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
}
}
@@ -694,5 +676,22 @@ namespace Bit.Core.Services
passwordlessLogin.FingerprintPhrase = string.Join("-", await _cryptoService.GetFingerprintAsync(userEmail, CoreHelpers.Base64UrlDecode(passwordlessLogin.PublicKey)));
return passwordlessLogin;
}
public async Task CreateNewSsoUserAsync(string organizationSsoId)
{
var orgAutoEnrollStatusResponse = await _apiService.GetOrganizationAutoEnrollStatusAsync(organizationSsoId);
var randomBytes = _cryptoFunctionService.RandomBytes(64);
var userKey = new UserKey(randomBytes);
var (userPubKey, userPrivKey) = await _cryptoService.MakeKeyPairAsync(userKey);
await _apiService.PostAccountKeysAsync(new KeysRequest
{
PublicKey = userPubKey,
EncryptedPrivateKey = userPrivKey.EncryptedString
});
await _stateService.SetUserKeyAsync(userKey);
await _stateService.SetPrivateKeyEncryptedAsync(userPrivKey.EncryptedString);
await _passwordResetEnrollmentService.EnrollAsync(orgAutoEnrollStatusResponse.Id);
}
}
}

View File

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Models.Response;
namespace Bit.Core.Services
{
@@ -12,19 +13,21 @@ namespace Bit.Core.Services
private readonly ICryptoService _cryptoService;
private readonly ITokenService _tokenService;
private readonly IApiService _apiService;
private readonly ICryptoFunctionService _cryptoFunctionService;
private readonly IOrganizationService _organizationService;
public KeyConnectorService(IStateService stateService, ICryptoService cryptoService,
ITokenService tokenService, IApiService apiService, OrganizationService organizationService)
ITokenService tokenService, IApiService apiService, ICryptoFunctionService cryptoFunctionService, OrganizationService organizationService)
{
_stateService = stateService;
_cryptoService = cryptoService;
_tokenService = tokenService;
_apiService = apiService;
_cryptoFunctionService = cryptoFunctionService;
_organizationService = organizationService;
}
public async Task GetAndSetKey(string url)
public async Task GetAndSetKeyAsync(string url)
{
try
{
@@ -83,5 +86,40 @@ namespace Bit.Core.Services
return loggedInUsingSso && requiredByOrganization && userIsNotUsingKeyConnector;
}
public async Task ConvertNewUserToKeyConnectorAsync(string orgId, IdentityTokenResponse tokenResponse)
{
// SSO Key Connector Onboarding
var password = await _cryptoFunctionService.RandomBytesAsync(64);
var newMasterKey = await _cryptoService.MakeMasterKeyAsync(Convert.ToBase64String(password), _tokenService.GetEmail(), tokenResponse.KdfConfig);
var keyConnectorRequest = new KeyConnectorUserKeyRequest(newMasterKey.EncKeyB64);
await _cryptoService.SetMasterKeyAsync(newMasterKey);
var (newUserKey, newProtectedUserKey) = await _cryptoService.EncryptUserKeyWithMasterKeyAsync(
newMasterKey,
await _cryptoService.MakeUserKeyAsync());
await _cryptoService.SetUserKeyAsync(newUserKey);
try
{
await _apiService.PostUserKeyToKeyConnector(tokenResponse.KeyConnectorUrl, keyConnectorRequest);
}
catch (Exception e)
{
throw new Exception("Unable to reach Key Connector", e);
}
var (newPublicKey, newProtectedPrivateKey) = await _cryptoService.MakeKeyPairAsync();
var keys = new KeysRequest
{
PublicKey = newPublicKey,
EncryptedPrivateKey = newProtectedPrivateKey.EncryptedString
};
var setPasswordRequest = new SetKeyConnectorKeyRequest(
newProtectedPrivateKey.EncryptedString, keys, tokenResponse.KdfConfig, orgId
);
await _apiService.PostSetKeyConnectorKey(setPasswordRequest);
}
}
}

View File

@@ -0,0 +1,62 @@
using System;
using System.Threading.Tasks;
using Bit.Core.Abstractions;
using Bit.Core.Models.Domain;
using Bit.Core.Models.Request;
using Bit.Core.Utilities;
namespace Bit.Core.Services
{
public class PasswordResetEnrollmentService : IPasswordResetEnrollmentService
{
private readonly IApiService _apiService;
private readonly ICryptoService _cryptoService;
private readonly IOrganizationService _organizationService;
private readonly IStateService _stateService;
public PasswordResetEnrollmentService(IApiService apiService,
ICryptoService cryptoService,
IOrganizationService organizationService,
IStateService stateService)
{
_apiService = apiService;
_cryptoService = cryptoService;
_organizationService = organizationService;
_stateService = stateService;
}
public async Task EnrollIfRequiredAsync(string organizationSsoId)
{
var orgAutoEnrollStatusResponse = await _apiService.GetOrganizationAutoEnrollStatusAsync(organizationSsoId);
if (!orgAutoEnrollStatusResponse?.ResetPasswordEnabled ?? false)
{
await EnrollAsync(orgAutoEnrollStatusResponse.Id);
}
}
public async Task EnrollAsync(string organizationId)
{
var orgKeyResponse = await _apiService.GetOrganizationKeysAsync(organizationId);
if (orgKeyResponse == null)
{
throw new Exception("Organization keys missing");
}
var userId = await _stateService.GetActiveUserIdAsync();
var userKey = await _cryptoService.GetUserKeyAsync();
var orgPublicKey = CoreHelpers.Base64UrlDecode(orgKeyResponse.PublicKey);
var encryptedKey = await _cryptoService.RsaEncryptAsync(userKey.Key, orgPublicKey);
var resetRequest = new OrganizationUserResetPasswordEnrollmentRequest();
resetRequest.ResetPasswordKey = encryptedKey.EncryptedString;
await _apiService.PutOrganizationUserResetPasswordEnrollmentAsync(
organizationId,
userId,
resetRequest
);
}
}
}

View File

@@ -53,7 +53,7 @@ namespace Bit.Core.Utilities
cryptoFunctionService);
searchService = new SearchService(cipherService, sendService);
var policyService = new PolicyService(stateService, organizationService);
var keyConnectorService = new KeyConnectorService(stateService, cryptoService, tokenService, apiService,
var keyConnectorService = new KeyConnectorService(stateService, cryptoService, tokenService, apiService, cryptoFunctionService,
organizationService);
var vaultTimeoutService = new VaultTimeoutService(cryptoService, stateService, platformUtilsService,
folderService, cipherService, collectionService, searchService, messagingService, tokenService,
@@ -78,9 +78,10 @@ namespace Bit.Core.Utilities
var passwordGenerationService = new PasswordGenerationService(cryptoService, stateService, cryptoFunctionService, policyService);
var totpService = new TotpService(cryptoFunctionService);
var deviceTrustCryptoService = new DeviceTrustCryptoService(apiService, appIdService, cryptoFunctionService, cryptoService, stateService);
var passwordResetEnrollmentService = new PasswordResetEnrollmentService(apiService, cryptoService, organizationService, stateService);
var authService = new AuthService(cryptoService, cryptoFunctionService, apiService, stateService,
tokenService, appIdService, i18nService, platformUtilsService, messagingService, vaultTimeoutService,
keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService);
keyConnectorService, passwordGenerationService, policyService, deviceTrustCryptoService, passwordResetEnrollmentService);
var exportService = new ExportService(folderService, cipherService, cryptoService);
var auditService = new AuditService(cryptoFunctionService, apiService);
var environmentService = new EnvironmentService(apiService, stateService, conditionedRunner);
@@ -116,6 +117,7 @@ namespace Bit.Core.Utilities
Register<IUsernameGenerationService>(usernameGenerationService);
Register<IConfigService>(configService);
Register<IDeviceTrustCryptoService>(deviceTrustCryptoService);
Register<IPasswordResetEnrollmentService>(passwordResetEnrollmentService);
}
public static void Register<T>(string serviceName, T obj)