PM-7623 Fix proper implementation of IFido2GetAssertionUserInterface now that the Fido2ClientService is being used for passkey autofill (#3174)
This commit is contained in:
parent
bfa57ad888
commit
1bfe894181
|
@ -13,6 +13,7 @@ using Bit.Core.Resources.Localization;
|
|||
using Bit.Core.Utilities.Fido2;
|
||||
using Java.Security;
|
||||
using Bit.Core.Services;
|
||||
using Bit.App.Platforms.Android.Autofill;
|
||||
|
||||
namespace Bit.Droid.Autofill
|
||||
{
|
||||
|
@ -22,6 +23,7 @@ namespace Bit.Droid.Autofill
|
|||
public class CredentialProviderSelectionActivity : MauiAppCompatActivity
|
||||
{
|
||||
private LazyResolve<IFido2MediatorService> _fido2MediatorService = new LazyResolve<IFido2MediatorService>();
|
||||
private LazyResolve<IFido2AndroidGetAssertionUserInterface> _fido2GetAssertionUserInterface = new LazyResolve<IFido2AndroidGetAssertionUserInterface>();
|
||||
private LazyResolve<IVaultTimeoutService> _vaultTimeoutService = new LazyResolve<IVaultTimeoutService>();
|
||||
private LazyResolve<IStateService> _stateService = new LazyResolve<IStateService>();
|
||||
private LazyResolve<ICipherService> _cipherService = new LazyResolve<ICipherService>();
|
||||
|
@ -69,7 +71,7 @@ namespace Bit.Droid.Autofill
|
|||
var androidOrigin = AppInfoToOrigin(getRequest?.CallingAppInfo);
|
||||
var packageName = getRequest?.CallingAppInfo.PackageName;
|
||||
var appInfoOrigin = getRequest?.CallingAppInfo.Origin;
|
||||
|
||||
|
||||
if (appInfoOrigin is null)
|
||||
{
|
||||
await _deviceActionService.Value.DisplayAlertAsync(AppResources.ErrorReadingPasskey, AppResources.PasskeysNotSupportedForThisApp, AppResources.Ok);
|
||||
|
@ -77,12 +79,12 @@ namespace Bit.Droid.Autofill
|
|||
return;
|
||||
}
|
||||
|
||||
var userInterface = new Fido2GetAssertionUserInterface(
|
||||
cipherId: cipherId,
|
||||
userVerified: false,
|
||||
ensureUnlockedVaultCallback: EnsureUnlockedVaultAsync,
|
||||
hasVaultBeenUnlockedInThisTransaction: () => hasVaultBeenUnlockedInThisTransaction,
|
||||
verifyUserCallback: (cipherId, uvPreference) => VerifyUserAsync(cipherId, uvPreference, RpId, hasVaultBeenUnlockedInThisTransaction));
|
||||
_fido2GetAssertionUserInterface.Value.Init(
|
||||
cipherId,
|
||||
false,
|
||||
() => hasVaultBeenUnlockedInThisTransaction,
|
||||
RpId
|
||||
);
|
||||
|
||||
var clientAssertParams = new Fido2ClientAssertCredentialParams
|
||||
{
|
||||
|
@ -142,38 +144,6 @@ namespace Bit.Droid.Autofill
|
|||
}
|
||||
}
|
||||
|
||||
private async Task EnsureUnlockedVaultAsync()
|
||||
{
|
||||
if (!await _stateService.Value.IsAuthenticatedAsync() || await _vaultTimeoutService.Value.IsLockedAsync())
|
||||
{
|
||||
// this should never happen but just in case.
|
||||
throw new InvalidOperationException("Not authed or vault locked");
|
||||
}
|
||||
}
|
||||
|
||||
internal async Task<bool> VerifyUserAsync(string selectedCipherId, Fido2UserVerificationPreference userVerificationPreference, string rpId, bool vaultUnlockedDuringThisTransaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
var encrypted = await _cipherService.Value.GetAsync(selectedCipherId);
|
||||
var cipher = await encrypted.DecryptAsync();
|
||||
|
||||
var userVerification = await _userVerificationMediatorService.Value.VerifyUserForFido2Async(
|
||||
new Fido2UserVerificationOptions(
|
||||
cipher?.Reprompt == Bit.Core.Enums.CipherRepromptType.Password,
|
||||
userVerificationPreference,
|
||||
vaultUnlockedDuringThisTransaction,
|
||||
rpId)
|
||||
);
|
||||
return !userVerification.IsCancelled && userVerification.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private string AppInfoToOrigin(CallingAppInfo info)
|
||||
{
|
||||
var cert = info.SigningInfo.GetApkContentsSigners()[0].ToByteArray();
|
||||
|
|
|
@ -1,21 +1,77 @@
|
|||
using Bit.Core.Abstractions;
|
||||
using Bit.Core.Services;
|
||||
using Bit.Core.Utilities.Fido2;
|
||||
|
||||
namespace Bit.App.Platforms.Android.Autofill
|
||||
{
|
||||
//TODO: WIP: Temporary Dummy implementation
|
||||
public class Fido2GetAssertionUserInterface : IFido2GetAssertionUserInterface
|
||||
public interface IFido2AndroidGetAssertionUserInterface : IFido2GetAssertionUserInterface
|
||||
{
|
||||
public bool HasVaultBeenUnlockedInThisTransaction => true;
|
||||
void Init(string cipherId,
|
||||
bool userVerified,
|
||||
Func<bool> hasVaultBeenUnlockedInThisTransaction,
|
||||
string rpId);
|
||||
}
|
||||
|
||||
public Task EnsureUnlockedVaultAsync()
|
||||
public class Fido2GetAssertionUserInterface : Core.Utilities.Fido2.Fido2GetAssertionUserInterface, IFido2AndroidGetAssertionUserInterface
|
||||
{
|
||||
private readonly IStateService _stateService;
|
||||
private readonly IVaultTimeoutService _vaultTimeoutService;
|
||||
private readonly ICipherService _cipherService;
|
||||
private readonly IUserVerificationMediatorService _userVerificationMediatorService;
|
||||
|
||||
public Fido2GetAssertionUserInterface(IStateService stateService,
|
||||
IVaultTimeoutService vaultTimeoutService,
|
||||
ICipherService cipherService,
|
||||
IUserVerificationMediatorService userVerificationMediatorService)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
_stateService = stateService;
|
||||
_vaultTimeoutService = vaultTimeoutService;
|
||||
_cipherService = cipherService;
|
||||
_userVerificationMediatorService = userVerificationMediatorService;
|
||||
}
|
||||
|
||||
public Task<(string CipherId, bool UserVerified)> PickCredentialAsync(Fido2GetAssertionUserInterfaceCredential[] credentials)
|
||||
public void Init(string cipherId,
|
||||
bool userVerified,
|
||||
Func<bool> hasVaultBeenUnlockedInThisTransaction,
|
||||
string rpId)
|
||||
{
|
||||
var credential = credentials[0];
|
||||
return Task.FromResult<(string CipherId, bool UserVerified)>((credential.CipherId, true));
|
||||
Init(cipherId,
|
||||
userVerified,
|
||||
EnsureAuthenAndVaultUnlockedAsync,
|
||||
hasVaultBeenUnlockedInThisTransaction,
|
||||
(cipherId, userVerificationPreference) => VerifyUserAsync(cipherId, userVerificationPreference, rpId, hasVaultBeenUnlockedInThisTransaction()));
|
||||
}
|
||||
|
||||
public async Task EnsureAuthenAndVaultUnlockedAsync()
|
||||
{
|
||||
if (!await _stateService.IsAuthenticatedAsync() || await _vaultTimeoutService.IsLockedAsync())
|
||||
{
|
||||
// this should never happen but just in case.
|
||||
throw new InvalidOperationException("Not authed or vault locked");
|
||||
}
|
||||
}
|
||||
|
||||
private async Task<bool> VerifyUserAsync(string selectedCipherId, Fido2UserVerificationPreference userVerificationPreference, string rpId, bool vaultUnlockedDuringThisTransaction)
|
||||
{
|
||||
try
|
||||
{
|
||||
var encrypted = await _cipherService.GetAsync(selectedCipherId);
|
||||
var cipher = await encrypted.DecryptAsync();
|
||||
|
||||
var userVerification = await _userVerificationMediatorService.VerifyUserForFido2Async(
|
||||
new Fido2UserVerificationOptions(
|
||||
cipher?.Reprompt == Core.Enums.CipherRepromptType.Password,
|
||||
userVerificationPreference,
|
||||
vaultUnlockedDuringThisTransaction,
|
||||
rpId)
|
||||
);
|
||||
return !userVerification.IsCancelled && userVerification.Result;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LoggerHelper.LogEvenIfCantBeResolved(ex);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -110,6 +110,13 @@ namespace Bit.Droid
|
|||
userVerificationMediatorService);
|
||||
ServiceContainer.Register<IFido2AuthenticatorService>(fido2AuthenticatorService);
|
||||
|
||||
var fido2GetAssertionUserInterface = new Fido2GetAssertionUserInterface(
|
||||
ServiceContainer.Resolve<IStateService>(),
|
||||
ServiceContainer.Resolve<IVaultTimeoutService>(),
|
||||
ServiceContainer.Resolve<ICipherService>(),
|
||||
ServiceContainer.Resolve<IUserVerificationMediatorService>());
|
||||
ServiceContainer.Register<IFido2AndroidGetAssertionUserInterface>(fido2GetAssertionUserInterface);
|
||||
|
||||
var fido2MakeCredentialUserInterface = new Fido2MakeCredentialUserInterface(
|
||||
ServiceContainer.Resolve<IStateService>(),
|
||||
ServiceContainer.Resolve<IVaultTimeoutService>(),
|
||||
|
@ -124,7 +131,7 @@ namespace Bit.Droid
|
|||
ServiceContainer.Resolve<IEnvironmentService>(),
|
||||
ServiceContainer.Resolve<ICryptoFunctionService>(),
|
||||
ServiceContainer.Resolve<IFido2AuthenticatorService>(),
|
||||
new Fido2GetAssertionUserInterface(),
|
||||
fido2GetAssertionUserInterface,
|
||||
fido2MakeCredentialUserInterface);
|
||||
ServiceContainer.Register<IFido2ClientService>(fido2ClientService);
|
||||
|
||||
|
|
|
@ -12,11 +12,15 @@ namespace Bit.Core.Utilities.Fido2
|
|||
/// </summary>
|
||||
public class Fido2GetAssertionUserInterface : IFido2GetAssertionUserInterface
|
||||
{
|
||||
private readonly string _cipherId;
|
||||
private readonly bool _userVerified = false;
|
||||
private readonly Func<Task> _ensureUnlockedVaultCallback;
|
||||
private readonly Func<bool> _hasVaultBeenUnlockedInThisTransaction;
|
||||
private readonly Func<string, Fido2UserVerificationPreference, Task<bool>> _verifyUserCallback;
|
||||
protected string _cipherId;
|
||||
protected bool _userVerified = false;
|
||||
protected Func<Task> _ensureUnlockedVaultCallback;
|
||||
protected Func<bool> _hasVaultBeenUnlockedInThisTransaction;
|
||||
protected Func<string, Fido2UserVerificationPreference, Task<bool>> _verifyUserCallback;
|
||||
|
||||
public Fido2GetAssertionUserInterface()
|
||||
{
|
||||
}
|
||||
|
||||
/// <param name="cipherId">The cipherId for the credential that the user has already picker</param>
|
||||
/// <param name="userVerified">True if the user has already been verified by the operating system</param>
|
||||
|
@ -25,6 +29,15 @@ namespace Bit.Core.Utilities.Fido2
|
|||
Func<Task> ensureUnlockedVaultCallback,
|
||||
Func<bool> hasVaultBeenUnlockedInThisTransaction,
|
||||
Func<string, Fido2UserVerificationPreference, Task<bool>> verifyUserCallback)
|
||||
{
|
||||
Init(cipherId, userVerified, ensureUnlockedVaultCallback, hasVaultBeenUnlockedInThisTransaction, verifyUserCallback);
|
||||
}
|
||||
|
||||
protected void Init(string cipherId,
|
||||
bool userVerified,
|
||||
Func<Task> ensureUnlockedVaultCallback,
|
||||
Func<bool> hasVaultBeenUnlockedInThisTransaction,
|
||||
Func<string, Fido2UserVerificationPreference, Task<bool>> verifyUserCallback)
|
||||
{
|
||||
_cipherId = cipherId;
|
||||
_userVerified = userVerified;
|
||||
|
|
Loading…
Reference in New Issue