diff --git a/BlazorWasmDemo/Server/Controllers/UserController.cs b/BlazorWasmDemo/Server/Controllers/UserController.cs index 5b648d00..cce465ce 100644 --- a/BlazorWasmDemo/Server/Controllers/UserController.cs +++ b/BlazorWasmDemo/Server/Controllers/UserController.cs @@ -265,7 +265,7 @@ public async Task MakeAssertionAsync([FromBody] AuthenticatorAssertionRa _pendingAssertions.Remove(key); // 2. Get registered credential from database - var creds = _demoStorage.GetCredentialById(clientResponse.Id) ?? throw new Exception("Unknown credentials"); + var creds = _demoStorage.GetCredentialById(clientResponse.RawId) ?? throw new Exception("Unknown credentials"); // 3. Make the assertion var res = await _fido2.MakeAssertionAsync(new MakeAssertionParams diff --git a/Demo/Controller.cs b/Demo/Controller.cs index 367a30c1..1b745036 100644 --- a/Demo/Controller.cs +++ b/Demo/Controller.cs @@ -194,7 +194,7 @@ public async Task MakeAssertion([FromBody] AuthenticatorAssertionRaw var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database - var creds = DemoStorage.GetCredentialById(clientResponse.Id) ?? throw new Exception("Unknown credentials"); + var creds = DemoStorage.GetCredentialById(clientResponse.RawId) ?? throw new Exception("Unknown credentials"); // 3. Get credential counter from database var storedCounter = creds.SignCount; diff --git a/Demo/TestController.cs b/Demo/TestController.cs index 22758728..c12a1414 100644 --- a/Demo/TestController.cs +++ b/Demo/TestController.cs @@ -181,7 +181,7 @@ public async Task MakeAssertionTestAsync([FromBody] AuthenticatorAss var options = AssertionOptions.FromJson(jsonOptions); // 2. Get registered credential from database - var creds = _demoStorage.GetCredentialById(clientResponse.Id); + var creds = _demoStorage.GetCredentialById(clientResponse.RawId); // 3. Get credential counter from database var storedCounter = creds.SignCount; diff --git a/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs b/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs index 92d814c1..94f9e427 100644 --- a/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs +++ b/Src/Fido2.Models/AuthenticatorAssertionRawResponse.cs @@ -12,9 +12,11 @@ namespace Fido2NetLib; /// public class AuthenticatorAssertionRawResponse { - [JsonConverter(typeof(Base64UrlConverter))] + /// + /// A string containing the credential's identifier. Base64UrlEncoding of . + /// [JsonPropertyName("id"), Required] - public byte[] Id { get; init; } + public string Id { get; init; } // might be wrong to base64url encode this... [JsonConverter(typeof(Base64UrlConverter))] diff --git a/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs b/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs index 06cddf99..1a124b5c 100644 --- a/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs +++ b/Src/Fido2.Models/AuthenticatorAttestationRawResponse.cs @@ -9,9 +9,11 @@ namespace Fido2NetLib; public sealed class AuthenticatorAttestationRawResponse { - [JsonConverter(typeof(Base64UrlConverter))] + /// + /// A string containing the credential's identifier. Base64UrlEncoding of . + /// [JsonPropertyName("id"), Required] - public byte[] Id { get; init; } + public string Id { get; init; } [JsonConverter(typeof(Base64UrlConverter))] [JsonPropertyName("rawId"), Required] diff --git a/Src/Fido2/AuthenticatorAssertionResponse.cs b/Src/Fido2/AuthenticatorAssertionResponse.cs index bf94b4a5..f578338c 100644 --- a/Src/Fido2/AuthenticatorAssertionResponse.cs +++ b/Src/Fido2/AuthenticatorAssertionResponse.cs @@ -77,7 +77,7 @@ public async Task VerifyAsync( if (options.AllowCredentials != null && options.AllowCredentials.Any()) { // might need to transform x.Id and raw.id as described in https://www.w3.org/TR/webauthn/#publickeycredential - if (!options.AllowCredentials.Any(x => x.Id.SequenceEqual(Raw.Id))) + if (!options.AllowCredentials.Any(x => x.Id.SequenceEqual(Raw.RawId))) throw new Fido2VerificationException(Fido2ErrorCode.InvalidAssertionResponse, Fido2ErrorMessages.CredentialIdNotInAllowedCredentials); } @@ -87,7 +87,7 @@ public async Task VerifyAsync( if (UserHandle.Length is 0) throw new Fido2VerificationException(Fido2ErrorMessages.UserHandleIsEmpty); - if (await isUserHandleOwnerOfCredId(new IsUserHandleOwnerOfCredentialIdParams(Raw.Id, UserHandle), cancellationToken) is false) + if (await isUserHandleOwnerOfCredId(new IsUserHandleOwnerOfCredentialIdParams(Raw.RawId, UserHandle), cancellationToken) is false) { throw new Fido2VerificationException(Fido2ErrorCode.InvalidAssertionResponse, Fido2ErrorMessages.UserHandleNotOwnerOfPublicKey); } @@ -177,7 +177,7 @@ public async Task VerifyAsync( return new VerifyAssertionResult { - CredentialId = Raw.Id, + CredentialId = Raw.RawId, SignCount = authData.SignCount, IsBackedUp = authData.IsBackedUp diff --git a/Tests/Fido2.Tests/Attestation/Apple.cs b/Tests/Fido2.Tests/Attestation/Apple.cs index 06a8d704..22ceaf1f 100644 --- a/Tests/Fido2.Tests/Attestation/Apple.cs +++ b/Tests/Fido2.Tests/Attestation/Apple.cs @@ -224,7 +224,7 @@ public async Task TestApplePublicKeyMismatch() var attestationResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { diff --git a/Tests/Fido2.Tests/AuthenticatorResponse.cs b/Tests/Fido2.Tests/AuthenticatorResponse.cs index 8ce33b76..e7aaf7c5 100644 --- a/Tests/Fido2.Tests/AuthenticatorResponse.cs +++ b/Tests/Fido2.Tests/AuthenticatorResponse.cs @@ -75,7 +75,7 @@ public async Task TestAuthenticatorOriginsAsync(string origin, string expectedOr var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -181,7 +181,7 @@ public async Task TestAuthenticatorOriginsFail(string origin, string expectedOri var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -253,7 +253,7 @@ public void TestAuthenticatorAttestationRawResponse() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -284,7 +284,7 @@ public void TestAuthenticatorAttestationRawResponse() } }; Assert.Equal(PublicKeyCredentialType.PublicKey, rawResponse.Type); - Assert.Equal([0xf1, 0xd0], rawResponse.Id); + Assert.Equal("8dA", rawResponse.Id); Assert.Equal([0xf1, 0xd0], rawResponse.RawId); Assert.Equal([0xa0], rawResponse.Response.AttestationObject); Assert.Equal(clientDataJson, rawResponse.Response.ClientDataJson); @@ -311,7 +311,7 @@ public void TestAuthenticatorAttestationResponseNull() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = null }; @@ -398,7 +398,7 @@ public async Task TestAuthenticatorAttestationResponseInvalidType() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -457,9 +457,9 @@ public async Task TestAuthenticatorAttestationResponseInvalidType() } [Theory] - [InlineData(null)] - [InlineData(new byte[0])] - public async Task TestAuthenticatorAttestationResponseInvalidRawId(byte[] value) + [InlineData(null, null)] + [InlineData("", new byte[0])] + public async Task TestAuthenticatorAttestationResponseInvalidRawId(string value, byte[] rawValue) { var challenge = RandomNumberGenerator.GetBytes(128); var rp = "https://www.passwordless.dev"; @@ -474,7 +474,7 @@ public async Task TestAuthenticatorAttestationResponseInvalidRawId(byte[] value) { Type = PublicKeyCredentialType.PublicKey, Id = value, - RawId = value, + RawId = rawValue, Response = new AuthenticatorAttestationRawResponse.AttestationResponse { AttestationObject = new CborMap { @@ -546,7 +546,7 @@ public async Task TestAuthenticatorAttestationResponseInvalidRawType() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.Invalid, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -626,7 +626,7 @@ public async Task TestAuthenticatorAttestationResponseRpidMismatch() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -707,7 +707,7 @@ public async Task TestAuthenticatorAttestationResponseNotUserPresentAsync() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -790,7 +790,7 @@ public async Task TestAuthenticatorAttestationResponseBackupEligiblePolicyRequir var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -871,7 +871,7 @@ public async Task TestAuthenticatorAttestationResponseBackupEligiblePolicyDisall var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -952,7 +952,7 @@ public async Task TestAuthenticatorAttestationResponseNoAttestedCredentialData() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -1033,7 +1033,7 @@ public async Task TestAuthenticatorAttestationResponseUnknownAttestationType() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -1114,7 +1114,7 @@ public async Task TestAuthenticatorAttestationResponseNotUniqueCredId() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -1194,7 +1194,7 @@ public async Task TestAuthenticatorAttestationResponseUVRequired() var rawResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -1275,7 +1275,7 @@ public void TestAuthenticatorAssertionRawResponse() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1301,7 +1301,7 @@ public void TestAuthenticatorAssertionRawResponse() } }; Assert.Equal(PublicKeyCredentialType.PublicKey, assertionResponse.Type); - Assert.Equal([0xf1, 0xd0], assertionResponse.Id); + Assert.Equal("8dA", assertionResponse.Id); Assert.Equal([0xf1, 0xd0], assertionResponse.RawId); Assert.Equal([0xf1, 0xd0], assertionResponse.Response.AuthenticatorData); Assert.Equal([0xf1, 0xd0], assertionResponse.Response.Signature); @@ -1352,7 +1352,7 @@ public async Task TestAuthenticatorAssertionTypeNotPublicKey() { Response = assertion, Type = PublicKeyCredentialType.Invalid, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1504,7 +1504,7 @@ public async Task TestAuthenticatorAssertionRawIdMissing() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { AppID = false, @@ -1579,7 +1579,7 @@ public async Task TestAuthenticatorAssertionUserHandleEmpty() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1655,7 +1655,7 @@ public async Task TestAuthenticatorAssertionUserHandleNotOwnerOfPublicKey() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1731,7 +1731,7 @@ public async Task TestAuthenticatorAssertionTypeNotWebAuthnGet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -1809,7 +1809,7 @@ public async Task TestAuthenticatorAssertionAppId() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1886,7 +1886,7 @@ public async Task TestAuthenticatorAssertionInvalidRpIdHash() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -1964,7 +1964,7 @@ public async Task TestAuthenticatorAssertionUPRequirementNotMet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2041,7 +2041,7 @@ public async Task TestAuthenticatorAssertionUVPolicyNotMet() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2116,7 +2116,7 @@ public async Task TestAuthenticatorAssertionBEPolicyRequired() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2192,7 +2192,7 @@ public async Task TestAuthenticatorAssertionBEPolicyDisallow() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2268,7 +2268,7 @@ public async Task TestAuthenticatorAssertionBSPolicyRequired() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2344,7 +2344,7 @@ public async Task TestAuthenticatorAssertionBSPolicyDisallow() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs { @@ -2421,7 +2421,7 @@ public async Task TestAuthenticatorAssertionStoredPublicKeyMissing() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2497,7 +2497,7 @@ public async Task TestAuthenticatorAssertionInvalidSignature() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { @@ -2580,7 +2580,7 @@ public async Task TestAuthenticatorAssertionSignCountSignature() { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], ClientExtensionResults = new AuthenticationExtensionsClientOutputs() { diff --git a/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs b/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs index 6097498e..f03a60fb 100644 --- a/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs +++ b/Tests/Fido2.Tests/ExistingU2fRegistrationDataTests.cs @@ -14,7 +14,8 @@ public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() { // u2f registration with appId var appId = "https://localhost:44336"; - var keyHandleData = Base64Url.DecodeFromChars("2uzGTqu9XGoDQpRBhkv3qDYWzEEZrDjOHT94fHe3J9VXl6KpaY6jL1C4gCAVSBCWZejOn-EYSyXfiG7RDQqgKw"); + var keyHandleB64Data = "2uzGTqu9XGoDQpRBhkv3qDYWzEEZrDjOHT94fHe3J9VXl6KpaY6jL1C4gCAVSBCWZejOn-EYSyXfiG7RDQqgKw"; + var keyHandleData = Base64Url.DecodeFromChars(keyHandleB64Data); var publicKeyData = Base64Url.DecodeFromChars("BEKJkJiDzo8wlrYbAHmyz5a5vShbkStO58ZO7F-hy4fvBp6TowCZoV2dNGcxIN1yT18799bb_WuP0Yq_DSv5a-U"); //key as cbor @@ -36,7 +37,7 @@ public async Task TestFido2AssertionWithExistingU2fRegistrationWithAppId() var authResponse = new AuthenticatorAssertionRawResponse { - Id = keyHandleData, + Id = keyHandleB64Data, RawId = keyHandleData, Type = PublicKeyCredentialType.PublicKey, ClientExtensionResults = new AuthenticationExtensionsClientOutputs diff --git a/Tests/Fido2.Tests/Fido2Tests.cs b/Tests/Fido2.Tests/Fido2Tests.cs index c2b51bf2..e9b9b250 100644 --- a/Tests/Fido2.Tests/Fido2Tests.cs +++ b/Tests/Fido2.Tests/Fido2Tests.cs @@ -169,7 +169,7 @@ public async Task MakeAttestationResponseAsync() var attestationResponse = new AuthenticatorAttestationRawResponse { Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], Response = new AuthenticatorAttestationRawResponse.AttestationResponse { @@ -987,7 +987,7 @@ internal static async Task MakeAssertionResponseAsync( { Response = assertion, Type = PublicKeyCredentialType.PublicKey, - Id = [0xf1, 0xd0], + Id = "8dA", RawId = [0xf1, 0xd0], }; IsUserHandleOwnerOfCredentialIdAsync callback = (args, cancellationToken) =>