mirror of
https://github.com/bitwarden/mobile.git
synced 2024-09-27 03:52:57 +02:00
PM-6468 Implemented copy TOTP if needed after using a Fido2 credential. Also added the Fido2MediatorService to have one point to interact with the authentication and also to add any new logic we need. (#3082)
This commit is contained in:
parent
faa515b415
commit
970d3c2621
@ -35,5 +35,6 @@ namespace Bit.Core.Abstractions
|
||||
Task SoftDeleteWithServerAsync(string id);
|
||||
Task RestoreWithServerAsync(string id);
|
||||
Task<string> CreateNewLoginForPasskeyAsync(Fido2ConfirmNewCredentialParams newPasskeyParams);
|
||||
Task CopyTotpCodeIfNeededAsync(CipherView cipher);
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +0,0 @@
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IFido2AuthenticationService
|
||||
{
|
||||
Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams);
|
||||
}
|
||||
}
|
14
src/Core/Abstractions/IFido2MediatorService.cs
Normal file
14
src/Core/Abstractions/IFido2MediatorService.cs
Normal file
@ -0,0 +1,14 @@
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
|
||||
namespace Bit.Core.Abstractions
|
||||
{
|
||||
public interface IFido2MediatorService
|
||||
{
|
||||
Task<Fido2ClientCreateCredentialResult> CreateCredentialAsync(Fido2ClientCreateCredentialParams createCredentialParams);
|
||||
Task<Fido2ClientAssertCredentialResult> AssertCredentialAsync(Fido2ClientAssertCredentialParams assertCredentialParams);
|
||||
|
||||
Task<Fido2AuthenticatorMakeCredentialResult> MakeCredentialAsync(Fido2AuthenticatorMakeCredentialParams makeCredentialParams, IFido2MakeCredentialUserInterface userInterface);
|
||||
Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams, IFido2GetAssertionUserInterface userInterface);
|
||||
Task<Fido2AuthenticatorDiscoverableCredentialMetadata[]> SilentCredentialDiscoveryAsync(string rpId);
|
||||
}
|
||||
}
|
@ -34,6 +34,8 @@ namespace Bit.Core.Services
|
||||
private readonly II18nService _i18nService;
|
||||
private readonly Func<ISearchService> _searchService;
|
||||
private readonly IConfigService _configService;
|
||||
private readonly ITotpService _totpService;
|
||||
private readonly IClipboardService _clipboardService;
|
||||
private readonly string _clearCipherCacheKey;
|
||||
private readonly string[] _allClearCipherCacheKeys;
|
||||
private Dictionary<string, HashSet<string>> _domainMatchBlacklist = new Dictionary<string, HashSet<string>>
|
||||
@ -53,6 +55,8 @@ namespace Bit.Core.Services
|
||||
II18nService i18nService,
|
||||
Func<ISearchService> searchService,
|
||||
IConfigService configService,
|
||||
ITotpService totpService,
|
||||
IClipboardService clipboardService,
|
||||
string clearCipherCacheKey,
|
||||
string[] allClearCipherCacheKeys)
|
||||
{
|
||||
@ -65,6 +69,8 @@ namespace Bit.Core.Services
|
||||
_i18nService = i18nService;
|
||||
_searchService = searchService;
|
||||
_configService = configService;
|
||||
_totpService = totpService;
|
||||
_clipboardService = clipboardService;
|
||||
_clearCipherCacheKey = clearCipherCacheKey;
|
||||
_allClearCipherCacheKeys = allClearCipherCacheKeys;
|
||||
}
|
||||
@ -1315,6 +1321,22 @@ namespace Bit.Core.Services
|
||||
return encryptedCipher.Id;
|
||||
}
|
||||
|
||||
public async Task CopyTotpCodeIfNeededAsync(CipherView cipher)
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(cipher?.Login?.Totp)
|
||||
||
|
||||
await _stateService.GetDisableAutoTotpCopyAsync() == true)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (cipher.OrganizationUseTotp || await _stateService.CanAccessPremiumAsync())
|
||||
{
|
||||
var totpCode = await _totpService.GetCodeAsync(cipher.Login.Totp);
|
||||
await _clipboardService.CopyTextAsync(totpCode);
|
||||
}
|
||||
}
|
||||
|
||||
private class CipherLocaleComparer : IComparer<CipherView>
|
||||
{
|
||||
private readonly II18nService _i18nService;
|
||||
|
@ -1,18 +0,0 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class Fido2AuthenticationService : IFido2AuthenticationService
|
||||
{
|
||||
public Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams)
|
||||
{
|
||||
// TODO: IMPLEMENT this
|
||||
return Task.FromResult(new Fido2AuthenticatorGetAssertionResult
|
||||
{
|
||||
AuthenticatorData = new byte[32],
|
||||
Signature = new byte[8]
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
@ -198,7 +198,8 @@ namespace Bit.Core.Services
|
||||
SelectedCredential = new Fido2AuthenticatorGetAssertionSelectedCredential
|
||||
{
|
||||
Id = selectedCredentialId.GuidToRawFormat(),
|
||||
UserHandle = selectedFido2Credential.UserHandleValue
|
||||
UserHandle = selectedFido2Credential.UserHandleValue,
|
||||
Cipher = selectedCipher
|
||||
},
|
||||
AuthenticatorData = authenticatorData,
|
||||
Signature = signature
|
||||
|
@ -206,7 +206,8 @@ namespace Bit.Core.Services
|
||||
Id = CoreHelpers.Base64UrlEncode(getAssertionResult.SelectedCredential.Id),
|
||||
RawId = getAssertionResult.SelectedCredential.Id,
|
||||
Signature = getAssertionResult.Signature,
|
||||
UserHandle = getAssertionResult.SelectedCredential.UserHandle
|
||||
UserHandle = getAssertionResult.SelectedCredential.UserHandle,
|
||||
Cipher = getAssertionResult.SelectedCredential.Cipher
|
||||
};
|
||||
}
|
||||
catch (InvalidStateError)
|
||||
|
60
src/Core/Services/Fido2MediatorService.cs
Normal file
60
src/Core/Services/Fido2MediatorService.cs
Normal file
@ -0,0 +1,60 @@
|
||||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
|
||||
namespace Bit.Core.Services
|
||||
{
|
||||
public class Fido2MediatorService : IFido2MediatorService
|
||||
{
|
||||
private readonly IFido2AuthenticatorService _fido2AuthenticatorService;
|
||||
private readonly IFido2ClientService _fido2ClientService;
|
||||
private readonly ICipherService _cipherService;
|
||||
|
||||
public Fido2MediatorService(IFido2AuthenticatorService fido2AuthenticatorService,
|
||||
IFido2ClientService fido2ClientService,
|
||||
ICipherService cipherService)
|
||||
{
|
||||
_fido2AuthenticatorService = fido2AuthenticatorService;
|
||||
_fido2ClientService = fido2ClientService;
|
||||
_cipherService = cipherService;
|
||||
}
|
||||
|
||||
public async Task<Fido2ClientAssertCredentialResult> AssertCredentialAsync(Fido2ClientAssertCredentialParams assertCredentialParams)
|
||||
{
|
||||
var result = await _fido2ClientService.AssertCredentialAsync(assertCredentialParams);
|
||||
|
||||
if (result?.Cipher != null)
|
||||
{
|
||||
await _cipherService.CopyTotpCodeIfNeededAsync(result.Cipher);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Fido2ClientCreateCredentialResult> CreateCredentialAsync(Fido2ClientCreateCredentialParams createCredentialParams)
|
||||
{
|
||||
return _fido2ClientService.CreateCredentialAsync(createCredentialParams);
|
||||
}
|
||||
|
||||
public async Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams, IFido2GetAssertionUserInterface userInterface)
|
||||
{
|
||||
var result = await _fido2AuthenticatorService.GetAssertionAsync(assertionParams, userInterface);
|
||||
|
||||
if (result?.SelectedCredential?.Cipher != null)
|
||||
{
|
||||
await _cipherService.CopyTotpCodeIfNeededAsync(result.SelectedCredential.Cipher);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public Task<Fido2AuthenticatorMakeCredentialResult> MakeCredentialAsync(Fido2AuthenticatorMakeCredentialParams makeCredentialParams, IFido2MakeCredentialUserInterface userInterface)
|
||||
{
|
||||
return _fido2AuthenticatorService.MakeCredentialAsync(makeCredentialParams, userInterface);
|
||||
}
|
||||
|
||||
public Task<Fido2AuthenticatorDiscoverableCredentialMetadata[]> SilentCredentialDiscoveryAsync(string rpId)
|
||||
{
|
||||
return _fido2AuthenticatorService.SilentCredentialDiscoveryAsync(rpId);
|
||||
}
|
||||
}
|
||||
}
|
@ -1,4 +1,6 @@
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
using Bit.Core.Models.View;
|
||||
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class Fido2AuthenticatorGetAssertionResult
|
||||
{
|
||||
@ -14,6 +16,8 @@
|
||||
|
||||
#nullable enable
|
||||
public byte[]? UserHandle { get; set; }
|
||||
|
||||
public CipherView? Cipher { get; set; }
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,3 +1,5 @@
|
||||
using Bit.Core.Models.View;
|
||||
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
/// <summary>
|
||||
@ -38,5 +40,10 @@ namespace Bit.Core.Utilities.Fido2
|
||||
/// return a user handle.
|
||||
/// </summary>
|
||||
public byte[]? UserHandle { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// The selected cipher login item that has the credential
|
||||
/// </summary>
|
||||
public CipherView? Cipher { get; set; }
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ namespace Bit.Core.Utilities
|
||||
var messagingService = Resolve<IMessagingService>("messagingService");
|
||||
var cryptoFunctionService = Resolve<ICryptoFunctionService>("cryptoFunctionService");
|
||||
var cryptoService = Resolve<ICryptoService>("cryptoService");
|
||||
var clipboardService = Resolve<IClipboardService>();
|
||||
var logger = Resolve<ILogger>();
|
||||
|
||||
SearchService searchService = null;
|
||||
@ -43,8 +44,9 @@ namespace Bit.Core.Utilities
|
||||
var settingsService = new SettingsService(stateService);
|
||||
var fileUploadService = new FileUploadService(apiService);
|
||||
var configService = new ConfigService(apiService, stateService, logger);
|
||||
var totpService = new TotpService(cryptoFunctionService);
|
||||
var cipherService = new CipherService(cryptoService, stateService, settingsService, apiService,
|
||||
fileUploadService, storageService, i18nService, () => searchService, configService, clearCipherCacheKey,
|
||||
fileUploadService, storageService, i18nService, () => searchService, configService, totpService, clipboardService, clearCipherCacheKey,
|
||||
allClearCipherCacheKeys);
|
||||
var folderService = new FolderService(cryptoService, stateService, apiService, i18nService, cipherService);
|
||||
var collectionService = new CollectionService(cryptoService, stateService, i18nService);
|
||||
@ -76,7 +78,6 @@ namespace Bit.Core.Utilities
|
||||
return Task.CompletedTask;
|
||||
});
|
||||
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,
|
||||
@ -115,7 +116,6 @@ namespace Bit.Core.Utilities
|
||||
Register<IUsernameGenerationService>(usernameGenerationService);
|
||||
Register<IDeviceTrustCryptoService>(deviceTrustCryptoService);
|
||||
Register<IPasswordResetEnrollmentService>(passwordResetEnrollmentService);
|
||||
Register<IFido2AuthenticationService>(new Fido2AuthenticationService());
|
||||
}
|
||||
|
||||
public static void Register<T>(string serviceName, T obj)
|
||||
|
@ -20,7 +20,7 @@ namespace Bit.iOS.Autofill
|
||||
{
|
||||
public partial class CredentialProviderViewController : ASCredentialProviderViewController, IAccountsManagerHost
|
||||
{
|
||||
private readonly LazyResolve<IFido2AuthenticatorService> _fido2AuthService = new LazyResolve<IFido2AuthenticatorService>();
|
||||
private readonly LazyResolve<IFido2MediatorService> _fido2MediatorService = new LazyResolve<IFido2MediatorService>();
|
||||
private readonly LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
|
||||
private readonly LazyResolve<IUserVerificationMediatorService> _userVerificationMediatorService = new LazyResolve<IUserVerificationMediatorService>();
|
||||
private readonly LazyResolve<ICipherService> _cipherService = new LazyResolve<ICipherService>();
|
||||
@ -96,7 +96,7 @@ namespace Bit.iOS.Autofill
|
||||
|
||||
try
|
||||
{
|
||||
var result = await _fido2AuthService.Value.MakeCredentialAsync(new Bit.Core.Utilities.Fido2.Fido2AuthenticatorMakeCredentialParams
|
||||
var result = await _fido2MediatorService.Value.MakeCredentialAsync(new Bit.Core.Utilities.Fido2.Fido2AuthenticatorMakeCredentialParams
|
||||
{
|
||||
Hash = passkeyRegistrationRequest.ClientDataHash.ToArray(),
|
||||
CredTypesAndPubKeyAlgs = GetCredTypesAndPubKeyAlgs(passkeyRegistrationRequest.SupportedAlgorithms),
|
||||
@ -202,7 +202,7 @@ namespace Bit.iOS.Autofill
|
||||
|
||||
try
|
||||
{
|
||||
var fido2AssertionResult = await _fido2AuthService.Value.GetAssertionAsync(new Bit.Core.Utilities.Fido2.Fido2AuthenticatorGetAssertionParams
|
||||
var fido2AssertionResult = await _fido2MediatorService.Value.GetAssertionAsync(new Bit.Core.Utilities.Fido2.Fido2AuthenticatorGetAssertionParams
|
||||
{
|
||||
RpId = rpId,
|
||||
Hash = _context.PasskeyCredentialRequest.ClientDataHash.ToArray(),
|
||||
|
@ -46,7 +46,7 @@ namespace Bit.iOS.Autofill
|
||||
LazyResolve<IPlatformUtilsService> _platformUtilsService = new LazyResolve<IPlatformUtilsService>();
|
||||
LazyResolve<ILogger> _logger = new LazyResolve<ILogger>();
|
||||
LazyResolve<IUserVerificationMediatorService> _userVerificationMediatorService = new LazyResolve<IUserVerificationMediatorService>();
|
||||
LazyResolve<IFido2AuthenticatorService> _fido2AuthenticatorService = new LazyResolve<IFido2AuthenticatorService>();
|
||||
LazyResolve<IFido2MediatorService> _fido2MediatorService = new LazyResolve<IFido2MediatorService>();
|
||||
|
||||
bool _alreadyLoadItemsOnce = false;
|
||||
bool _isLoading;
|
||||
@ -177,7 +177,7 @@ namespace Bit.iOS.Autofill
|
||||
|
||||
try
|
||||
{
|
||||
var fido2AssertionResult = await _fido2AuthenticatorService.Value.GetAssertionAsync(new Fido2AuthenticatorGetAssertionParams
|
||||
var fido2AssertionResult = await _fido2MediatorService.Value.GetAssertionAsync(new Fido2AuthenticatorGetAssertionParams
|
||||
{
|
||||
RpId = Context.PasskeyCredentialRequestParameters.RelyingPartyIdentifier,
|
||||
Hash = Context.PasskeyCredentialRequestParameters.ClientDataHash.ToArray(),
|
||||
|
@ -203,11 +203,17 @@ namespace Bit.iOS.Core.Utilities
|
||||
ServiceContainer.Resolve<IUserVerificationService>());
|
||||
ServiceContainer.Register<IUserVerificationMediatorService>(userVerificationMediatorService);
|
||||
|
||||
ServiceContainer.Register<IFido2AuthenticatorService>(new Fido2AuthenticatorService(
|
||||
var fido2AuthenticatorService = new Fido2AuthenticatorService(
|
||||
ServiceContainer.Resolve<ICipherService>(),
|
||||
ServiceContainer.Resolve<ISyncService>(),
|
||||
ServiceContainer.Resolve<ICryptoFunctionService>(),
|
||||
userVerificationMediatorService));
|
||||
userVerificationMediatorService);
|
||||
ServiceContainer.Register<IFido2AuthenticatorService>(fido2AuthenticatorService);
|
||||
|
||||
ServiceContainer.Register<IFido2MediatorService>(new Fido2MediatorService(
|
||||
fido2AuthenticatorService,
|
||||
null, // iOS doesn't use IFido2ClientService so no need to have it in memory
|
||||
ServiceContainer.Resolve<ICipherService>()));
|
||||
|
||||
ServiceContainer.Register<IWatchDeviceService>(new WatchDeviceService(ServiceContainer.Resolve<ICipherService>(),
|
||||
ServiceContainer.Resolve<IEnvironmentService>(),
|
||||
|
Loading…
Reference in New Issue
Block a user