2525package com .yubico .webauthn .data
2626
2727import com .fasterxml .jackson .core .`type` .TypeReference
28+ import com .fasterxml .jackson .databind .JsonNode
2829import com .fasterxml .jackson .databind .ObjectMapper
2930import com .fasterxml .jackson .databind .exc .ValueInstantiationException
3031import com .fasterxml .jackson .databind .json .JsonMapper
3132import com .fasterxml .jackson .databind .node .BooleanNode
33+ import com .fasterxml .jackson .databind .node .JsonNodeFactory
3234import com .fasterxml .jackson .databind .node .ObjectNode
3335import com .fasterxml .jackson .databind .node .TextNode
3436import com .fasterxml .jackson .datatype .jdk8 .Jdk8Module
@@ -44,6 +46,7 @@ import com.yubico.webauthn.extension.appid.Generators._
4446import org .junit .runner .RunWith
4547import org .scalacheck .Arbitrary
4648import org .scalacheck .Arbitrary .arbitrary
49+ import org .scalacheck .Gen
4750import org .scalatest .funspec .AnyFunSpec
4851import org .scalatest .matchers .should .Matchers
4952import org .scalatestplus .junit .JUnitRunner
@@ -62,6 +65,7 @@ class JsonIoSpec
6265 .builder()
6366 .addModule(new Jdk8Module ())
6467 .build()
68+ val jf : JsonNodeFactory = JsonNodeFactory .instance
6569
6670 describe(" The class" ) {
6771
@@ -133,52 +137,6 @@ class JsonIoSpec
133137 }
134138
135139 describe(" The class PublicKeyCredential" ) {
136- it(
137- " has an alternative parseRegistrationResponseJson function as an alias."
138- ) {
139- def test [A ](tpe : TypeReference [A ])(implicit a : Arbitrary [A ]): Unit = {
140- forAll { value : A =>
141- val encoded : String = json.writeValueAsString(value)
142- val decoded : A = json.readValue(encoded, tpe)
143- val altDecoded =
144- PublicKeyCredential .parseRegistrationResponseJson(encoded)
145- val altRecoded : String = json.writeValueAsString(altDecoded)
146-
147- altDecoded should equal(decoded)
148- altRecoded should equal(encoded)
149- }
150- }
151- test(
152- new TypeReference [PublicKeyCredential [
153- AuthenticatorAttestationResponse ,
154- ClientRegistrationExtensionOutputs ,
155- ]]() {}
156- )
157- }
158-
159- it(
160- " has an alternative parseAuthenticationResponseJson function as an alias."
161- ) {
162- def test [A ](tpe : TypeReference [A ])(implicit a : Arbitrary [A ]): Unit = {
163- forAll { value : A =>
164- val encoded : String = json.writeValueAsString(value)
165- val decoded : A = json.readValue(encoded, tpe)
166- val altDecoded =
167- PublicKeyCredential .parseAssertionResponseJson(encoded)
168- val altRecoded : String = json.writeValueAsString(altDecoded)
169-
170- altDecoded should equal(decoded)
171- altRecoded should equal(encoded)
172- }
173- }
174- test(
175- new TypeReference [PublicKeyCredential [
176- AuthenticatorAssertionResponse ,
177- ClientAssertionExtensionOutputs ,
178- ]]() {}
179- )
180- }
181-
182140 it(" allows rawId to be present without id." ) {
183141 def test [P <: PublicKeyCredential [_, _]](tpe : TypeReference [P ])(implicit
184142 a : Arbitrary [P ]
@@ -416,6 +374,92 @@ class JsonIoSpec
416374 }
417375 }
418376
377+ describe(" The function PublicKeyCredential.parseRegistrationResponseJson" ) {
378+ it(" can parse registration responses." ) {
379+ def test [A ](tpe : TypeReference [A ])(implicit a : Arbitrary [A ]): Unit = {
380+ forAll { value : A =>
381+ val encoded : String = json.writeValueAsString(value)
382+ val decoded : A = json.readValue(encoded, tpe)
383+ val altDecoded =
384+ PublicKeyCredential .parseRegistrationResponseJson(encoded)
385+ val altRecoded : String = json.writeValueAsString(altDecoded)
386+
387+ altDecoded should equal(decoded)
388+ altRecoded should equal(encoded)
389+ }
390+ }
391+
392+ test(
393+ new TypeReference [PublicKeyCredential [
394+ AuthenticatorAttestationResponse ,
395+ ClientRegistrationExtensionOutputs ,
396+ ]]() {}
397+ )
398+ }
399+
400+ describe(""" tolerates and ignores the "response" sub-attribute:""" ) {
401+ def test [T <: JsonNode ](attrName : String , genAttrValue : Gen [T ]): Unit = {
402+ type P = PublicKeyCredential [
403+ AuthenticatorAttestationResponse ,
404+ ClientRegistrationExtensionOutputs ,
405+ ]
406+ it(s " ${attrName}. " ) {
407+ forAll(
408+ arbitrary[P ],
409+ genAttrValue,
410+ ) { (value : P , attrValue : T ) =>
411+ val tree : ObjectNode = json.valueToTree(value)
412+ tree
413+ .get(" response" )
414+ .asInstanceOf [ObjectNode ]
415+ .set(attrName, attrValue)
416+ val encoded = json.writeValueAsString(tree)
417+ val decoded =
418+ PublicKeyCredential .parseRegistrationResponseJson(encoded)
419+ val recoded : ObjectNode = json.valueToTree[ObjectNode ](decoded)
420+ recoded.has(attrName) should be(false )
421+ }
422+ }
423+ }
424+
425+ test(
426+ " publicKeyAlgorithm" ,
427+ arbitraryCOSEAlgorithmIdentifier.arbitrary.map(i =>
428+ jf.numberNode(i.getId)
429+ ),
430+ )
431+
432+ test(
433+ " publicKey" ,
434+ arbitrary[String ].map(new TextNode (_)),
435+ )
436+ }
437+ }
438+
439+ describe(" The function PublicKeyCredential.parseAssertionResponseJson" ) {
440+ it(" can parse assertion responses." ) {
441+ def test [A ](tpe : TypeReference [A ])(implicit a : Arbitrary [A ]): Unit = {
442+ forAll { value : A =>
443+ val encoded : String = json.writeValueAsString(value)
444+ val decoded : A = json.readValue(encoded, tpe)
445+ val altDecoded =
446+ PublicKeyCredential .parseAssertionResponseJson(encoded)
447+ val altRecoded : String = json.writeValueAsString(altDecoded)
448+
449+ altDecoded should equal(decoded)
450+ altRecoded should equal(encoded)
451+ }
452+ }
453+
454+ test(
455+ new TypeReference [PublicKeyCredential [
456+ AuthenticatorAssertionResponse ,
457+ ClientAssertionExtensionOutputs ,
458+ ]]() {}
459+ )
460+ }
461+ }
462+
419463 describe(" The class PublicKeyCredentialCreationOptions" ) {
420464 it(""" has a toCredentialsCreateJson() method which returns a JSON object with the PublicKeyCredentialCreationOptions set as a top-level "publicKey" property.""" ) {
421465 forAll { pkcco : PublicKeyCredentialCreationOptions =>
0 commit comments