mirror of
https://github.com/bitwarden/mobile
synced 2025-12-15 07:43:37 +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 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
|
public interface IFido2UserInterface
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -49,5 +80,12 @@ namespace Bit.Core.Abstractions
|
|||||||
/// <param name="existingCipherIds">The IDs of the excluded credentials.</param>
|
/// <param name="existingCipherIds">The IDs of the excluded credentials.</param>
|
||||||
/// <returns>When user has confirmed the message</returns>
|
/// <returns>When user has confirmed the message</returns>
|
||||||
Task InformExcludedCredential(string[] existingCipherIds);
|
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 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)
|
public async Task<Fido2AuthenticatorGetAssertionResult> GetAssertionAsync(Fido2AuthenticatorGetAssertionParams assertionParams)
|
||||||
|
|||||||
@@ -3,22 +3,22 @@ namespace Bit.Core.Utilities.Fido2
|
|||||||
public class Fido2AuthenticatorMakeCredentialParams
|
public class Fido2AuthenticatorMakeCredentialParams
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The caller’s RP ID, as determined by the user agent and the client. */
|
/// The Relying Party's PublicKeyCredentialRpEntity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string RpId { get; set; }
|
public PublicKeyCredentialRpEntity RpEntity { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The Relying Party's PublicKeyCredentialRpEntity. */
|
/// The Relying Party's PublicKeyCredentialRpEntity.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public PublicKeyCredentialUserEntity UserEntity { get; set; }
|
public PublicKeyCredentialUserEntity UserEntity { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The hash of the serialized client data, provided by the client. */
|
/// The hash of the serialized client data, provided by the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public byte[] Hash { get; set; }
|
public byte[] Hash { get; set; }
|
||||||
|
|
||||||
/// <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. */
|
/// 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>
|
||||||
public PublicKeyCredentialAlgorithmDescriptor[] CredTypesAndPubKeyAlgs { get; set; }
|
public PublicKeyCredentialAlgorithmDescriptor[] CredTypesAndPubKeyAlgs { get; set; }
|
||||||
|
|
||||||
@@ -33,12 +33,12 @@ namespace Bit.Core.Utilities.Fido2
|
|||||||
public bool RequireResidentKey { get; set; }
|
public bool RequireResidentKey { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The effective user verification requirement for assertion, a Boolean value provided by the client. */
|
/// The effective user verification requirement for assertion, a Boolean value provided by the client.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool RequireUserVerification { get; set; }
|
public bool RequireUserVerification { get; set; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option. */
|
/// CTAP2 authenticators support setting this to false, but we only support the WebAuthn authenticator model which does not have that option.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
// public bool RequireUserPresence { get; set; } // Always required
|
// public bool RequireUserPresence { get; set; } // Always required
|
||||||
|
|
||||||
|
|||||||
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
|
Algorithm = -7 // ES256
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
mParams.RpId = "bitwarden.com";
|
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||||
mParams.RequireUserVerification = false;
|
mParams.RequireUserVerification = false;
|
||||||
mParams.ExcludeCredentialDescriptorList = [
|
mParams.ExcludeCredentialDescriptorList = [
|
||||||
new PublicKeyCredentialDescriptor {
|
new PublicKeyCredentialDescriptor {
|
||||||
@@ -96,7 +96,7 @@ namespace Bit.Core.Test.Services
|
|||||||
Algorithm = -7 // ES256
|
Algorithm = -7 // ES256
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
mParams.RpId = "bitwarden.com";
|
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||||
mParams.RequireUserVerification = false;
|
mParams.RequireUserVerification = false;
|
||||||
mParams.ExcludeCredentialDescriptorList = [
|
mParams.ExcludeCredentialDescriptorList = [
|
||||||
new PublicKeyCredentialDescriptor {
|
new PublicKeyCredentialDescriptor {
|
||||||
@@ -126,7 +126,7 @@ namespace Bit.Core.Test.Services
|
|||||||
Algorithm = -7 // ES256
|
Algorithm = -7 // ES256
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
mParams.RpId = "bitwarden.com";
|
mParams.RpEntity = new PublicKeyCredentialRpEntity { Id = "bitwarden.com" };
|
||||||
mParams.RequireUserVerification = false;
|
mParams.RequireUserVerification = false;
|
||||||
mParams.ExcludeCredentialDescriptorList = [
|
mParams.ExcludeCredentialDescriptorList = [
|
||||||
new PublicKeyCredentialDescriptor {
|
new PublicKeyCredentialDescriptor {
|
||||||
@@ -146,6 +146,74 @@ namespace Bit.Core.Test.Services
|
|||||||
|
|
||||||
#endregion
|
#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)
|
private byte[] RandomBytes(int length)
|
||||||
{
|
{
|
||||||
var bytes = new byte[length];
|
var bytes = new byte[length];
|
||||||
|
|||||||
Reference in New Issue
Block a user