Skip to content

Commit 391d706

Browse files
authored
Merge pull request #107 from Yubico/unrequested-extensions
Test that RP can opt into unrequested extensions
2 parents c66f1fd + ab3f02f commit 391d706

File tree

2 files changed

+79
-0
lines changed

2 files changed

+79
-0
lines changed

webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyAssertionSpec.scala

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,7 @@ class RelyingPartyAssertionSpec extends FunSpec with Matchers with ScalaCheckDri
118118
allowCredentials: Option[java.util.List[PublicKeyCredentialDescriptor]] = Some(List(PublicKeyCredentialDescriptor.builder().id(Defaults.credentialId).build()).asJava),
119119
allowOriginPort: Boolean = false,
120120
allowOriginSubdomain: Boolean = false,
121+
allowUnrequestedExtensions: Boolean = false,
121122
authenticatorData: ByteArray = Defaults.authenticatorData,
122123
callerTokenBindingId: Option[ByteArray] = None,
123124
challenge: ByteArray = Defaults.challenge,
@@ -192,6 +193,7 @@ class RelyingPartyAssertionSpec extends FunSpec with Matchers with ScalaCheckDri
192193
.allowOriginPort(allowOriginPort)
193194
.allowOriginSubdomain(allowOriginSubdomain)
194195
.allowUntrustedAttestation(false)
196+
.allowUnrequestedExtensions(allowUnrequestedExtensions)
195197
.validateSignatureCounter(validateSignatureCounter)
196198

197199
origins.map(_.asJava).foreach(builder.origins _)
@@ -988,6 +990,23 @@ class RelyingPartyAssertionSpec extends FunSpec with Matchers with ScalaCheckDri
988990
// }
989991
}
990992

993+
it("Succeeds if clientExtensionResults is not a subset of the extensions requested by the Relying Party, but the Relying Party has enabled allowing unrequested extensions.") {
994+
val extensionInputs = AssertionExtensionInputs.builder().build()
995+
val clientExtensionOutputs = ClientAssertionExtensionOutputs.builder().appid(true).build()
996+
997+
// forAll(unrequestedAssertionExtensions, minSuccessful(1)) { case (extensionInputs, clientExtensionOutputs) =>
998+
val steps = finishAssertion(
999+
allowUnrequestedExtensions = true,
1000+
requestedExtensions = extensionInputs,
1001+
clientExtensionResults = clientExtensionOutputs
1002+
)
1003+
val step: FinishAssertionSteps#Step14 = steps.begin.next.next.next.next.next.next.next.next.next.next.next.next.next.next
1004+
1005+
step.validations shouldBe a [Success[_]]
1006+
step.tryNext shouldBe a [Success[_]]
1007+
// }
1008+
}
1009+
9911010
it("Succeeds if clientExtensionResults is a subset of the extensions requested by the Relying Party.") {
9921011
forAll(subsetAssertionExtensions) { case (extensionInputs, clientExtensionOutputs) =>
9931012
val steps = finishAssertion(
@@ -1021,6 +1040,24 @@ class RelyingPartyAssertionSpec extends FunSpec with Matchers with ScalaCheckDri
10211040
}
10221041
}
10231042

1043+
it("Succeeds if authenticator extensions is not a subset of the extensions requested by the Relying Party, but the Relying Party has enabled allowing unrequested extensions.") {
1044+
forAll(anyAuthenticatorExtensions[AssertionExtensionInputs]) { case (extensionInputs: AssertionExtensionInputs, authenticatorExtensionOutputs: ObjectNode) =>
1045+
whenever(authenticatorExtensionOutputs.fieldNames().asScala.exists(id => !extensionInputs.getExtensionIds.contains(id))) {
1046+
val steps = finishAssertion(
1047+
allowUnrequestedExtensions = true,
1048+
requestedExtensions = extensionInputs,
1049+
authenticatorData = TestAuthenticator.makeAuthDataBytes(
1050+
extensionsCborBytes = Some(new ByteArray(JacksonCodecs.cbor.writeValueAsBytes(authenticatorExtensionOutputs)))
1051+
)
1052+
)
1053+
val step: FinishAssertionSteps#Step14 = steps.begin.next.next.next.next.next.next.next.next.next.next.next.next.next.next
1054+
1055+
step.validations shouldBe a [Success[_]]
1056+
step.tryNext shouldBe a [Success[_]]
1057+
}
1058+
}
1059+
}
1060+
10241061
it("Succeeds if authenticator extensions is a subset of the extensions requested by the Relying Party.") {
10251062
forAll(subsetAuthenticatorExtensions[AssertionExtensionInputs]) { case (extensionInputs: AssertionExtensionInputs, authenticatorExtensionOutputs: ObjectNode) =>
10261063
val steps = finishAssertion(

webauthn-server-core/src/test/scala/com/yubico/webauthn/RelyingPartyRegistrationSpec.scala

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,7 @@ class RelyingPartyRegistrationSpec extends FunSpec with Matchers with ScalaCheck
8888
private def finishRegistration(
8989
allowOriginPort: Boolean = false,
9090
allowOriginSubdomain: Boolean = false,
91+
allowUnrequestedExtensions: Boolean = false,
9192
allowUntrustedAttestation: Boolean = false,
9293
callerTokenBindingId: Option[ByteArray] = None,
9394
credentialId: Option[ByteArray] = None,
@@ -104,6 +105,7 @@ class RelyingPartyRegistrationSpec extends FunSpec with Matchers with ScalaCheck
104105
.preferredPubkeyParams(preferredPubkeyParams.asJava)
105106
.allowOriginPort(allowOriginPort)
106107
.allowOriginSubdomain(allowOriginSubdomain)
108+
.allowUnrequestedExtensions(allowUnrequestedExtensions)
107109
.allowUntrustedAttestation(allowUntrustedAttestation)
108110
.metadataService(metadataService.asJava)
109111

@@ -686,6 +688,24 @@ class RelyingPartyRegistrationSpec extends FunSpec with Matchers with ScalaCheck
686688
}
687689
}
688690

691+
ignore("Succeeds if clientExtensionResults is not a subset of the extensions requested by the Relying Party, but the Relying Party has enabled allowing unrequested extensions.") {
692+
forAll(anyRegistrationExtensions) { case (extensionInputs, clientExtensionOutputs) =>
693+
whenever(clientExtensionOutputs.getExtensionIds.asScala.exists(id => !extensionInputs.getExtensionIds.contains(id))) {
694+
val steps = finishRegistration(
695+
allowUnrequestedExtensions = true,
696+
testData = RegistrationTestData.Packed.BasicAttestation.copy(
697+
requestedExtensions = extensionInputs,
698+
clientExtensionResults = clientExtensionOutputs
699+
)
700+
)
701+
val step: FinishRegistrationSteps#Step12 = steps.begin.next.next.next.next.next.next.next.next.next.next.next
702+
703+
step.validations shouldBe a [Success[_]]
704+
step.tryNext shouldBe a [Success[_]]
705+
}
706+
}
707+
}
708+
689709
it("Succeeds if clientExtensionResults is a subset of the extensions requested by the Relying Party.") {
690710
forAll(subsetRegistrationExtensions) { case (extensionInputs, clientExtensionOutputs) =>
691711
val steps = finishRegistration(
@@ -725,6 +745,28 @@ class RelyingPartyRegistrationSpec extends FunSpec with Matchers with ScalaCheck
725745
}
726746
}
727747

748+
it("Succeeds if authenticator extensions is not a subset of the extensions requested by the Relying Party, but the Relying Party has enabled allowing unrequested extensions.") {
749+
forAll(anyAuthenticatorExtensions[RegistrationExtensionInputs]) { case (extensionInputs: RegistrationExtensionInputs, authenticatorExtensionOutputs: ObjectNode) =>
750+
whenever(authenticatorExtensionOutputs.fieldNames().asScala.exists(id => !extensionInputs.getExtensionIds.contains(id))) {
751+
val steps = finishRegistration(
752+
allowUnrequestedExtensions = true,
753+
testData = RegistrationTestData.Packed.BasicAttestation.copy(
754+
requestedExtensions = extensionInputs
755+
).editAuthenticatorData(
756+
authData => new ByteArray(
757+
authData.getBytes.updated(32, (authData.getBytes()(32) | 0x80).toByte) ++
758+
JacksonCodecs.cbor.writeValueAsBytes(authenticatorExtensionOutputs)
759+
)
760+
)
761+
)
762+
val step: FinishRegistrationSteps#Step12 = steps.begin.next.next.next.next.next.next.next.next.next.next.next
763+
764+
step.validations shouldBe a [Success[_]]
765+
step.tryNext shouldBe a [Success[_]]
766+
}
767+
}
768+
}
769+
728770
it("Succeeds if authenticator extensions is a subset of the extensions requested by the Relying Party.") {
729771
forAll(subsetAuthenticatorExtensions[RegistrationExtensionInputs]) { case (extensionInputs: RegistrationExtensionInputs, authenticatorExtensionOutputs: ObjectNode) =>
730772
val steps = finishRegistration(

0 commit comments

Comments
 (0)