mirror of
https://github.com/bitwarden/mobile
synced 2025-12-14 23:33:34 +00:00
Add example of delegated UI class
This commit is contained in:
65
src/Core/Utilities/Fido2/Fido2DelegatedUserInterface.cs
Normal file
65
src/Core/Utilities/Fido2/Fido2DelegatedUserInterface.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Bit.Core.Abstractions;
|
||||||
|
|
||||||
|
namespace Bit.Core.Utilities.Fido2
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This implementation is used when all interactions are delegated to the operating system.
|
||||||
|
/// Most often these decisions have already been made by the time the Authenticator is called.
|
||||||
|
///
|
||||||
|
/// This is only supported for assertion operations. Attestation requires the user to interact
|
||||||
|
/// with the app directly.
|
||||||
|
/// </summary>
|
||||||
|
public class Fido2DelegatedUserInterface : IFido2UserInterface
|
||||||
|
{
|
||||||
|
private string _cipherId = null;
|
||||||
|
private bool _userVerified = false;
|
||||||
|
private Func<Task> _ensureUnlockedVaultAsyncCallback;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the user has already picked a credential from a list of existing credentials.
|
||||||
|
/// Picking a credential also assumes user presence.
|
||||||
|
/// </summary>
|
||||||
|
public Fido2DelegatedUserInterface UserPickedCredential(string cipherId)
|
||||||
|
{
|
||||||
|
_cipherId = cipherId;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Indicates that the user was verified by the OS, e.g. by a fingerprint or face scan.
|
||||||
|
/// </summary>
|
||||||
|
public Fido2DelegatedUserInterface UserIsVerified()
|
||||||
|
{
|
||||||
|
_userVerified = true;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Fido2DelegatedUserInterface WithEnsureUnlockedVaultAsyncCallback(Func<Task> callback)
|
||||||
|
{
|
||||||
|
_ensureUnlockedVaultAsyncCallback = callback;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task<Fido2PickCredentialResult> PickCredentialAsync(Fido2PickCredentialParams parameters)
|
||||||
|
{
|
||||||
|
return Task.FromResult(new Fido2PickCredentialResult
|
||||||
|
{
|
||||||
|
CipherId = _cipherId,
|
||||||
|
UserVerified = _userVerified
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task EnsureUnlockedVaultAsync()
|
||||||
|
{
|
||||||
|
if (_ensureUnlockedVaultAsyncCallback != null)
|
||||||
|
{
|
||||||
|
return _ensureUnlockedVaultAsyncCallback();
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new Exception("No callback provided to ensure the vault is unlocked");
|
||||||
|
}
|
||||||
|
|
||||||
|
public Task InformExcludedCredential(string[] existingCipherIds) => throw new NotImplementedException();
|
||||||
|
public Task<Fido2ConfirmNewCredentialResult> ConfirmNewCredentialAsync(Fido2ConfirmNewCredentialParams parameters) => throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -21,6 +21,8 @@ namespace Bit.iOS.Autofill
|
|||||||
{
|
{
|
||||||
private readonly LazyResolve<ICipherService> _cipherService = new LazyResolve<ICipherService>();
|
private readonly LazyResolve<ICipherService> _cipherService = new LazyResolve<ICipherService>();
|
||||||
|
|
||||||
|
private readonly Fido2DelegatedUserInterface _userInterface = new Fido2DelegatedUserInterface();
|
||||||
|
|
||||||
private IFido2AuthenticatorService _fido2AuthService;
|
private IFido2AuthenticatorService _fido2AuthService;
|
||||||
private IFido2AuthenticatorService Fido2AuthService
|
private IFido2AuthenticatorService Fido2AuthService
|
||||||
{
|
{
|
||||||
@@ -29,7 +31,7 @@ namespace Bit.iOS.Autofill
|
|||||||
if (_fido2AuthService is null)
|
if (_fido2AuthService is null)
|
||||||
{
|
{
|
||||||
_fido2AuthService = ServiceContainer.Resolve<IFido2AuthenticatorService>();
|
_fido2AuthService = ServiceContainer.Resolve<IFido2AuthenticatorService>();
|
||||||
_fido2AuthService.Init(this);
|
_fido2AuthService.Init(_userInterface);
|
||||||
}
|
}
|
||||||
return _fido2AuthService;
|
return _fido2AuthService;
|
||||||
}
|
}
|
||||||
@@ -159,18 +161,20 @@ namespace Bit.iOS.Autofill
|
|||||||
}).ToArray();
|
}).ToArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public class UserInteractionRequiredException : Exception {}
|
||||||
|
|
||||||
private async Task ProvideCredentialWithoutUserInteractionAsync(ASPasskeyCredentialRequest passkeyCredentialRequest)
|
private async Task ProvideCredentialWithoutUserInteractionAsync(ASPasskeyCredentialRequest passkeyCredentialRequest)
|
||||||
{
|
{
|
||||||
InitAppIfNeeded();
|
InitAppIfNeeded();
|
||||||
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
await _stateService.Value.SetPasswordRepromptAutofillAsync(false);
|
||||||
await _stateService.Value.SetPasswordVerifiedAutofillAsync(false);
|
await _stateService.Value.SetPasswordVerifiedAutofillAsync(false);
|
||||||
if (!await IsAuthed() || await IsLocked())
|
|
||||||
{
|
|
||||||
CancelRequest(ASExtensionErrorCode.UserInteractionRequired);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_context.PasskeyCredentialRequest = passkeyCredentialRequest;
|
_context.PasskeyCredentialRequest = passkeyCredentialRequest;
|
||||||
await ProvideCredentialAsync(false);
|
|
||||||
|
try {
|
||||||
|
await ProvideCredentialAsync(false);
|
||||||
|
} catch (UserInteractionRequiredException) {
|
||||||
|
CancelRequest(ASExtensionErrorCode.UserInteractionRequired);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public async Task CompleteAssertionRequestAsync(string rpId, NSData userHandleData, NSData credentialIdData, string cipherId)
|
public async Task CompleteAssertionRequestAsync(string rpId, NSData userHandleData, NSData credentialIdData, string cipherId)
|
||||||
@@ -209,6 +213,10 @@ namespace Bit.iOS.Autofill
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
_userInterface.UserPickedCredential(cipherId);
|
||||||
|
// if (os.PerformedFaceID) {
|
||||||
|
_userInterface.UserIsVerified();
|
||||||
|
//}
|
||||||
|
|
||||||
ClipLogger.Log("fido2AssertionResult:" + fido2AssertionResult);
|
ClipLogger.Log("fido2AssertionResult:" + fido2AssertionResult);
|
||||||
|
|
||||||
|
|||||||
@@ -506,6 +506,13 @@ namespace Bit.iOS.Autofill
|
|||||||
|
|
||||||
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
private async Task ProvideCredentialAsync(bool userInteraction = true)
|
||||||
{
|
{
|
||||||
|
_userInterface.WithEnsureUnlockedVaultAsyncCallback(async () => {
|
||||||
|
if (!userInteraction && (!await IsAuthed() || await IsLocked())) {
|
||||||
|
throw new UserInteractionRequiredException();
|
||||||
|
}
|
||||||
|
|
||||||
|
await EnsureUnlockedVaultAsync();
|
||||||
|
});
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
ClipLogger.Log("ProvideCredentialAsync");
|
ClipLogger.Log("ProvideCredentialAsync");
|
||||||
|
|||||||
Reference in New Issue
Block a user