diff --git a/Src/Fido2.Models/CredentialCreateOptions.cs b/Src/Fido2.Models/CredentialCreateOptions.cs index 94e41c54..0295f202 100644 --- a/Src/Fido2.Models/CredentialCreateOptions.cs +++ b/Src/Fido2.Models/CredentialCreateOptions.cs @@ -283,7 +283,14 @@ public bool RequireResidentKey set { _requireResidentKey = value; - _residentKey = value ? ResidentKeyRequirement.Required : ResidentKeyRequirement.Discouraged; + if (value) + { + _residentKey = ResidentKeyRequirement.Required; + } + else if (_residentKey is not ResidentKeyRequirement.Preferred) + { + _residentKey = ResidentKeyRequirement.Discouraged; + } } } diff --git a/Tests/Fido2.Tests/Fido2Tests.cs b/Tests/Fido2.Tests/Fido2Tests.cs index e9b9b250..ea9c35e3 100644 --- a/Tests/Fido2.Tests/Fido2Tests.cs +++ b/Tests/Fido2.Tests/Fido2Tests.cs @@ -417,6 +417,66 @@ public void TestStringIsSerializable() Assert.Equal(z1, z2); } + [Fact] + public void AuthenticatorSelection_ResidentKeyPreferred_SurvivesJsonRoundTrip() + { + // Arrange: Create AuthenticatorSelection with ResidentKey = Preferred + var original = new AuthenticatorSelection + { + ResidentKey = ResidentKeyRequirement.Preferred, + UserVerification = UserVerificationRequirement.Preferred + }; + + // Act: Serialize to JSON and deserialize back + var json = JsonSerializer.Serialize(original); + var deserialized = JsonSerializer.Deserialize(json); + + // Assert: ResidentKey should still be Preferred after round-trip + Assert.Equal(ResidentKeyRequirement.Preferred, deserialized.ResidentKey); + } + + [Fact] + public void AuthenticatorSelection_RequireResidentKeyTrue_SetsResidentKeyToRequired() + { + // Arrange & Act: Create AuthenticatorSelection with RequireResidentKey = true +#pragma warning disable CS0618 // RequireResidentKey is obsolete + var selection = new AuthenticatorSelection + { + RequireResidentKey = true + }; + + // Assert: ResidentKey should be Required when RequireResidentKey is true + Assert.Equal(ResidentKeyRequirement.Required, selection.ResidentKey); + Assert.True(selection.RequireResidentKey); +#pragma warning restore CS0618 + } + + [Fact] + public void CredentialCreateOptions_ResidentKeyPreferred_SurvivesJsonRoundTrip() + { + // Arrange: This JSON has residentKey: "preferred" which should be preserved + const string json = """ + { + "rp": { "id": "some.rp.id", "name": "Some name" }, + "user": { "name": "someuserid", "id": "NjVmZGNiOTJiZjQyZjZmZDE0YzViODVk", "displayName": "The User 1234" }, + "challenge": "kauVQPwQtf4BlhOFObDfTQ", + "pubKeyCredParams": [ { "type": "public-key", "alg": -7 }, { "type": "public-key", "alg": -257 } ], + "timeout": 60000, + "attestation": "none", + "attestationFormats": [], + "authenticatorSelection": { "residentKey": "preferred", "requireResidentKey": false, "userVerification": "preferred" }, + "hints": [], + "excludeCredentials": [] + } + """; + + // Act: Deserialize the JSON + var options = CredentialCreateOptions.FromJson(json); + + // Assert: ResidentKey should be Preferred, not Discouraged + Assert.Equal(ResidentKeyRequirement.Preferred, options.AuthenticatorSelection.ResidentKey); + } + [Fact] public async Task TestFido2AssertionAsync() {