mirror of
https://github.com/bitwarden/mobile
synced 2025-12-05 23:53:33 +00:00
[PM-5731] feat: add new credential confirmaiton
This commit is contained in:
@@ -34,6 +34,37 @@ namespace Bit.Core.Abstractions
|
||||
public bool UserVerified { get; set; }
|
||||
}
|
||||
|
||||
public struct Fido2ConfirmNewCredentialParams
|
||||
{
|
||||
///<summary>
|
||||
/// The name of the credential.
|
||||
///</summary>
|
||||
public string CredentialName { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The name of the user.
|
||||
///</summary>
|
||||
public string UserName { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the user must be verified before completing the operation.
|
||||
/// </summary>
|
||||
public bool UserVerification { get; set; }
|
||||
}
|
||||
|
||||
public struct Fido2ConfirmNewCredentialResult
|
||||
{
|
||||
/// <summary>
|
||||
/// The name of the user.
|
||||
/// </summary>
|
||||
public string CipherId { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not the user was verified.
|
||||
/// </summary>
|
||||
public bool UserVerified { get; set; }
|
||||
}
|
||||
|
||||
public interface IFido2UserInterface
|
||||
{
|
||||
/// <summary>
|
||||
@@ -49,5 +80,12 @@ namespace Bit.Core.Abstractions
|
||||
/// <param name="existingCipherIds">The IDs of the excluded credentials.</param>
|
||||
/// <returns>When user has confirmed the message</returns>
|
||||
Task InformExcludedCredential(string[] existingCipherIds);
|
||||
|
||||
/// <summary>
|
||||
/// Ask the user to confirm the creation of a new credential.
|
||||
/// </summary>
|
||||
/// <param name="confirmNewCredentialParams">The parameters to use when asking the user to confirm the creation of a new credential.</param>
|
||||
/// <returns>The ID of the cipher where the new credential should be saved.</returns>
|
||||
Task<Fido2ConfirmNewCredentialResult> ConfirmNewCredentialAsync(Fido2ConfirmNewCredentialParams confirmNewCredentialParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,7 +48,20 @@ namespace Bit.Core.Services
|
||||
throw new NotAllowedError();
|
||||
}
|
||||
|
||||
throw new NotImplementedException();
|
||||
var response = await _userInterface.ConfirmNewCredentialAsync(new Fido2ConfirmNewCredentialParams {
|
||||
CredentialName = makeCredentialParams.RpEntity.Name,
|
||||
UserName = makeCredentialParams.UserEntity.Name,
|
||||
UserVerification = makeCredentialParams.RequireUserVerification
|
||||
});
|
||||
|
||||
return new Fido2AuthenticatorMakeCredentialResult
|
||||
{
|
||||
CredentialId = GuidToRawFormat(Guid.NewGuid().ToString()),
|
||||
AttestationObject = Array.Empty<byte>(),
|
||||
AuthData = Array.Empty<byte>(),
|
||||
PublicKey = Array.Empty<byte>(),
|
||||
PublicKeyAlgorithm = (int) Fido2AlgorithmIdentifier.ES256,
|
||||
};
|
||||
}
|
||||
|
||||
public async Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams)
|
||||
|
||||
@@ -2,44 +2,44 @@ namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class Fido2AuthenticatorMakeCredentialParams
|
||||
{
|
||||
///<summary>
|
||||
/// The caller’s RP ID, as determined by the user agent and the client. */
|
||||
///</summary>
|
||||
public string RpId { get; set; }
|
||||
/// <summary>
|
||||
/// The Relying Party's PublicKeyCredentialRpEntity.
|
||||
/// </summary>
|
||||
public PublicKeyCredentialRpEntity RpEntity { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The Relying Party's PublicKeyCredentialRpEntity. */
|
||||
///</summary>
|
||||
/// <summary>
|
||||
/// The Relying Party's PublicKeyCredentialRpEntity.
|
||||
/// </summary>
|
||||
public PublicKeyCredentialUserEntity UserEntity { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The hash of the serialized client data, provided by the client. */
|
||||
///</summary>
|
||||
/// <summary>
|
||||
/// The hash of the serialized client data, provided by the client.
|
||||
/// </summary>
|
||||
public byte[] Hash { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// A sequence of pairs of PublicKeyCredentialType and public key algorithms (COSEAlgorithmIdentifier) requested by the Relying Party. This sequence is ordered from most preferred to least preferred. The authenticator makes a best-effort to create the most preferred credential that it can. */
|
||||
///</summary>
|
||||
/// <summary>
|
||||
/// A sequence of pairs of PublicKeyCredentialType and public key algorithms (COSEAlgorithmIdentifier) requested by the Relying Party. This sequence is ordered from most preferred to least preferred. The authenticator makes a best-effort to create the most preferred credential that it can.
|
||||
/// </summary>
|
||||
public PublicKeyCredentialAlgorithmDescriptor[] CredTypesAndPubKeyAlgs { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// <summary>
|
||||
///An OPTIONAL list of PublicKeyCredentialDescriptor objects provided by the Relying Party with the intention that, if any of these are known to the authenticator, it SHOULD NOT create a new credential. excludeCredentialDescriptorList contains a list of known credentials.
|
||||
///</summary>
|
||||
/// </summary>
|
||||
public PublicKeyCredentialDescriptor[] ExcludeCredentialDescriptorList { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// <summary>
|
||||
/// The effective resident key requirement for credential creation, a Boolean value determined by the client. Resident is synonymous with discoverable. */
|
||||
///</summary>
|
||||
/// </summary>
|
||||
public bool RequireResidentKey { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// The effective user verification requirement for assertion, a Boolean value provided by the client. */
|
||||
///</summary>
|
||||
/// <summary>
|
||||
/// The effective user verification requirement for assertion, a Boolean value provided by the client.
|
||||
/// </summary>
|
||||
public bool RequireUserVerification { get; set; }
|
||||
|
||||
///<summary>
|
||||
/// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option. */
|
||||
///</summary>
|
||||
/// <summary>
|
||||
/// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option.
|
||||
/// </summary>
|
||||
// public bool RequireUserPresence { get; set; } // Always required
|
||||
|
||||
/// <summary>
|
||||
|
||||
9
src/Core/Utilities/Fido2/PublicKeyCredentialRpEntity.cs
Normal file
9
src/Core/Utilities/Fido2/PublicKeyCredentialRpEntity.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
namespace Bit.Core.Utilities.Fido2
|
||||
{
|
||||
public class PublicKeyCredentialRpEntity
|
||||
{
|
||||
public string Id { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
}
|
||||
}
|
||||
@@ -59,7 +59,7 @@ namespace Bit.Core.Test.Services
|
||||
Algorithm = -7 // ES256
|
||||
}
|
||||
];
|
||||
mParams.RpId = "bitwarden.com";
|
||||
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||
mParams.RequireUserVerification = false;
|
||||
mParams.ExcludeCredentialDescriptorList = [
|
||||
new PublicKeyCredentialDescriptor {
|
||||
@@ -96,7 +96,7 @@ namespace Bit.Core.Test.Services
|
||||
Algorithm = -7 // ES256
|
||||
}
|
||||
];
|
||||
mParams.RpId = "bitwarden.com";
|
||||
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||
mParams.RequireUserVerification = false;
|
||||
mParams.ExcludeCredentialDescriptorList = [
|
||||
new PublicKeyCredentialDescriptor {
|
||||
@@ -126,7 +126,7 @@ namespace Bit.Core.Test.Services
|
||||
Algorithm = -7 // ES256
|
||||
}
|
||||
];
|
||||
mParams.RpId = "bitwarden.com";
|
||||
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||
mParams.RequireUserVerification = false;
|
||||
mParams.ExcludeCredentialDescriptorList = [
|
||||
new PublicKeyCredentialDescriptor {
|
||||
@@ -146,6 +146,74 @@ namespace Bit.Core.Test.Services
|
||||
|
||||
#endregion
|
||||
|
||||
#region credential creation
|
||||
|
||||
[Theory]
|
||||
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
||||
public async Task MakeCredentialAsync_RequestsUserVerification_ParamsRequireUserVerification(SutProvider<Fido2AuthenticatorService> sutProvider, Fido2AuthenticatorMakeCredentialParams mParams)
|
||||
{
|
||||
// Common Arrange
|
||||
var credentialIds = new[] { Guid.NewGuid(), Guid.NewGuid() };
|
||||
List<CipherView> ciphers = [
|
||||
CreateCipherView(credentialIds[0].ToString(), "bitwarden.com", false),
|
||||
CreateCipherView(credentialIds[1].ToString(), "bitwarden.com", true)
|
||||
];
|
||||
mParams.CredTypesAndPubKeyAlgs = [
|
||||
new PublicKeyCredentialAlgorithmDescriptor {
|
||||
Type = "public-key",
|
||||
Algorithm = -7 // ES256
|
||||
}
|
||||
];
|
||||
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||
mParams.RequireUserVerification = false;
|
||||
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns(ciphers);
|
||||
|
||||
// Arrange
|
||||
mParams.RequireUserVerification = true;
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.MakeCredentialAsync(mParams);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IFido2UserInterface>().Received().ConfirmNewCredentialAsync(Arg.Is<Fido2ConfirmNewCredentialParams>(
|
||||
(p) => p.UserVerification == true
|
||||
));
|
||||
}
|
||||
|
||||
[Theory]
|
||||
[InlineCustomAutoData(new[] { typeof(SutProviderCustomization) })]
|
||||
public async Task MakeCredentialAsync_DoesNotRequestUserVerification_ParamsDoNotRequireUserVerification(SutProvider<Fido2AuthenticatorService> sutProvider, Fido2AuthenticatorMakeCredentialParams mParams)
|
||||
{
|
||||
// Common Arrange
|
||||
var credentialIds = new[] { Guid.NewGuid(), Guid.NewGuid() };
|
||||
List<CipherView> ciphers = [
|
||||
CreateCipherView(credentialIds[0].ToString(), "bitwarden.com", false),
|
||||
CreateCipherView(credentialIds[1].ToString(), "bitwarden.com", true)
|
||||
];
|
||||
mParams.CredTypesAndPubKeyAlgs = [
|
||||
new PublicKeyCredentialAlgorithmDescriptor {
|
||||
Type = "public-key",
|
||||
Algorithm = -7 // ES256
|
||||
}
|
||||
];
|
||||
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||
mParams.RequireUserVerification = false;
|
||||
sutProvider.GetDependency<ICipherService>().GetAllDecryptedAsync().Returns(ciphers);
|
||||
|
||||
// Arrange
|
||||
mParams.RequireUserVerification = false;
|
||||
|
||||
// Act
|
||||
await sutProvider.Sut.MakeCredentialAsync(mParams);
|
||||
|
||||
// Assert
|
||||
await sutProvider.GetDependency<IFido2UserInterface>().Received().ConfirmNewCredentialAsync(Arg.Is<Fido2ConfirmNewCredentialParams>(
|
||||
(p) => p.UserVerification == false
|
||||
));
|
||||
}
|
||||
|
||||
#endregion
|
||||
|
||||
private byte[] RandomBytes(int length)
|
||||
{
|
||||
var bytes = new byte[length];
|
||||
|
||||
Reference in New Issue
Block a user