[WebAuthN] Implement PublicKeyCredential’s [[DiscoverFromExternalSource]] with a...
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jan 2018 01:28:47 +0000 (01:28 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Thu, 25 Jan 2018 01:28:47 +0000 (01:28 +0000)
https://bugs.webkit.org/show_bug.cgi?id=182032
<rdar://problem/36459922>

Reviewed by Brent Fulgham.

Source/WebCore:

This patch implements PublicKeyCredential's [[DiscoverFromExternalSource]] from
https://www.w3.org/TR/webauthn/#getAssertion as of 5 December 2017. In order to
do testing, a dummy authenticator is implemented to exercise a failure and a
pass path. A number of dependencies need to be resolved later in order to comply
with the spec, which are marked by FIXME in the patch and tracked by proper
bugs. Those dependencies will be addressed once the first prototype is finshed.

Tests: http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html
       http/wpt/credential-management/credentialscontainer-store-basics.https.html
       http/wpt/webauthn/public-key-credential-get-failure.https.html
       http/wpt/webauthn/public-key-credential-get-success.https.html

* Modules/credentialmanagement/CredentialsContainer.cpp:
(WebCore::CredentialsContainer::get):
(WebCore::CredentialsContainer::isCreate):
Fixes some minor issues.
* Modules/webauthn/Authenticator.cpp:
(WebCore::Authenticator::getAssertion const):
* Modules/webauthn/Authenticator.h:
(WebCore::Authenticator::AssertionReturnBundle::AssertionReturnBundle):
* Modules/webauthn/PublicKeyCredential.cpp:
(WebCore::PublicKeyCredential::collectFromCredentialStore):
Changed a parameter type.
(WebCore::PublicKeyCredential::discoverFromExternalSource):
(WebCore::PublicKeyCredential::create):
Improved some comments.
* Modules/webauthn/PublicKeyCredential.h:
* Modules/webauthn/PublicKeyCredentialRequestOptions.h:
(): Deleted.
* bindings/js/JSAuthenticatorResponseCustom.cpp:
(WebCore::toJSNewlyCreated):

LayoutTests:

* http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https-expected.txt: Added.
* http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html: Added.
* http/tests/webauthn/public-key-credential-same-origin-with-ancestors-2.https-expected.txt:
* http/tests/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt:
* http/tests/webauthn/resources/last-layer-frame.https.html:
* http/wpt/credential-management/credentialscontainer-store-basics.https-expected.txt: Added.
* http/wpt/credential-management/credentialscontainer-store-basics.https.html: Added.
* http/wpt/webauthn/idl.https-expected.txt:
* http/wpt/webauthn/idl.https.html:
* http/wpt/webauthn/public-key-credential-create-success.https.html:
* http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt: Added.
* http/wpt/webauthn/public-key-credential-get-failure.https.html: Added.
* http/wpt/webauthn/public-key-credential-get-success.https-expected.txt: Added.
* http/wpt/webauthn/public-key-credential-get-success.https.html: Added.

git-svn-id: https://svn.webkit.org/repository/webkit/trunk@227589 268f45cc-cd09-0410-ab3c-d52691b4dbfc

23 files changed:
LayoutTests/ChangeLog
LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https-expected.txt [new file with mode: 0644]
LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html [new file with mode: 0644]
LayoutTests/http/tests/webauthn/public-key-credential-same-origin-with-ancestors-2.https-expected.txt
LayoutTests/http/tests/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt
LayoutTests/http/tests/webauthn/resources/last-layer-frame.https.html
LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https.html [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/idl.https-expected.txt
LayoutTests/http/wpt/webauthn/idl.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-create-success.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https-expected.txt [new file with mode: 0644]
LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https.html [new file with mode: 0644]
Source/WebCore/ChangeLog
Source/WebCore/Modules/credentialmanagement/CredentialsContainer.cpp
Source/WebCore/Modules/webauthn/Authenticator.cpp
Source/WebCore/Modules/webauthn/Authenticator.h
Source/WebCore/Modules/webauthn/PublicKeyCredential.cpp
Source/WebCore/Modules/webauthn/PublicKeyCredential.h
Source/WebCore/Modules/webauthn/PublicKeyCredentialRequestOptions.h
Source/WebCore/bindings/js/JSAuthenticatorResponseCustom.cpp

index dea7407..4d83214 100644 (file)
@@ -1,3 +1,26 @@
+2018-01-24  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement PublicKeyCredential’s [[DiscoverFromExternalSource]] with a dummy authenticator
+        https://bugs.webkit.org/show_bug.cgi?id=182032
+        <rdar://problem/36459922>
+
+        Reviewed by Brent Fulgham.
+
+        * http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https-expected.txt: Added.
+        * http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html: Added.
+        * http/tests/webauthn/public-key-credential-same-origin-with-ancestors-2.https-expected.txt:
+        * http/tests/webauthn/public-key-credential-same-origin-with-ancestors.https-expected.txt:
+        * http/tests/webauthn/resources/last-layer-frame.https.html:
+        * http/wpt/credential-management/credentialscontainer-store-basics.https-expected.txt: Added.
+        * http/wpt/credential-management/credentialscontainer-store-basics.https.html: Added.
+        * http/wpt/webauthn/idl.https-expected.txt:
+        * http/wpt/webauthn/idl.https.html:
+        * http/wpt/webauthn/public-key-credential-create-success.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt: Added.
+        * http/wpt/webauthn/public-key-credential-get-failure.https.html: Added.
+        * http/wpt/webauthn/public-key-credential-get-success.https-expected.txt: Added.
+        * http/wpt/webauthn/public-key-credential-get-success.https.html: Added.
+
 2018-01-24  Youenn Fablet  <youenn@apple.com>
 
         Opaque being-loaded responses should clone their body
diff --git a/LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https-expected.txt b/LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https-expected.txt
new file mode 100644 (file)
index 0000000..3cc8676
--- /dev/null
@@ -0,0 +1,35 @@
+Tests that PublicKeyCredential's [[get]] throws TypeError when invalid options are passed.
+
+On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
+
+
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Member PublicKeyCredentialRequestOptions.challenge is required and must be an instance of (ArrayBufferView or ArrayBuffer).
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Member PublicKeyCredentialDescriptor.type is required and must be an instance of PublicKeyCredentialType.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Member PublicKeyCredentialDescriptor.id is required and must be an instance of (ArrayBufferView or ArrayBuffer).
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Cannot convert a symbol to a number.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Cannot convert a symbol to a string.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Cannot convert a symbol to a string.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS navigator.credentials.get(invalidOptions) rejected promise  with TypeError: Type error.
+PASS successfullyParsed is true
+
+TEST COMPLETE
+
diff --git a/LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html b/LayoutTests/http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html
new file mode 100644 (file)
index 0000000..e6a8cb6
--- /dev/null
@@ -0,0 +1,80 @@
+<!DOCTYPE html>
+<html>
+<head>
+    <script src="/js-test-resources/js-test.js"></script>
+    <script src="./resources/util.js"></script>
+</head>
+<body>
+<p id="description"></p>
+<div id="console"></div>
+<script>
+    description("Tests that PublicKeyCredential's [[get]] throws TypeError when invalid options are passed.");
+
+    jsTestIsAsync = true;
+
+    const challenge = asciiToUint8Array("123456");
+    const allowCredential = { type: "public-key", id: asciiToUint8Array("123456") };
+
+    const missingAttributeVector = [
+        // missing top level attribute
+        [undefined],
+        // missing allowCredentials attribute
+        [challenge, undefined, undefined, [{ type: undefined, id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: undefined }]]
+    ];
+
+    const wrongTypeAttributeVector = [
+        // wrong challenge type
+        [1],
+        [true],
+        [null],
+        [Symbol()],
+        ["foo"],
+        [[ ]],
+        [{ }],
+        // wrong timeout type
+        [challenge, Symbol(), undefined, undefined],
+        // wrong rpId type
+        [challenge, undefined, Symbol(), undefined],
+        // wrong allowCredentials type
+        [challenge, undefined, undefined, [{ type: 1, id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: true, id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: null, id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: Symbol(), id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: "foo", id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: [ ], id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: { }, id: allowCredential.id }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: 1 }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: true }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: null }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: Symbol() }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: "foo" }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: [ ] }]],
+        [challenge, undefined, undefined, [{ type: allowCredential.type, id: { } }]]
+    ];
+
+    function makeOptions(attributes)
+    {
+        if (attributes.length == 1)
+            return { publicKey: { challenge: attributes[0] } };
+        return { publicKey: { challenge: attributes[0], timeout: attributes[1], rpId: attributes[2], allowCredentials: attributes[3] } };
+    }
+
+    function runTest(attributesVectors) {
+        attributesVectors.forEach(function(attributesVector) {
+            attributesVector.forEach(async function(attributes) {
+                invalidOptions = makeOptions(attributes);
+                await shouldReject('navigator.credentials.get(invalidOptions)');
+            });
+        });
+
+        finishJSTest();
+    }
+
+    const vectors = [];
+    vectors.push(missingAttributeVector);
+    vectors.push(wrongTypeAttributeVector);
+    runTest(vectors);
+</script>
+</body>
+</html>
index b75c5c0..3334c4d 100644 (file)
@@ -3,7 +3,7 @@ Tests that a frame that doesn't share the same origin with all its ancestors cou
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS Throw NotAllowedError.
+PASS Throw NotAllowedError for both PublicKeyCredential's [[create]] and [[get]].
 PASS successfullyParsed is true
 
 TEST COMPLETE
index b75c5c0..3334c4d 100644 (file)
@@ -3,7 +3,7 @@ Tests that a frame that doesn't share the same origin with all its ancestors cou
 On success, you will see a series of "PASS" messages, followed by "TEST COMPLETE".
 
 
-PASS Throw NotAllowedError.
+PASS Throw NotAllowedError for both PublicKeyCredential's [[create]] and [[get]].
 PASS successfullyParsed is true
 
 TEST COMPLETE
index 3aa2cc4..6d26e57 100644 (file)
@@ -7,7 +7,7 @@
         top.postMessage(messagePrefix, "https://127.0.0.1:8443");
     }
 
-    const options = {
+    const makeOptions = {
         publicKey: {
             rp: {
                 name: "example.com"
             user: {
                 name: "John Appleseed",
                 id: asciiToUint8Array("123456"),
-                displayName: "John",
+                displayName: "Appleseed",
             },
             challenge: asciiToUint8Array("123456"),
             pubKeyCredParams: [{ type: "public-key", alg: -7 }]
         }
     };
-    navigator.credentials.create(options).then(
+    const requestOptions = {
+        publicKey: {
+            challenge: asciiToUint8Array("123456"),
+        }
+    };
+
+    navigator.credentials.create(makeOptions).then(
         function(value) {
             messageToTop("Access granted. " + value);
         },
         function(exception) {
             if (exception.name == "NotAllowedError")
-                messageToTop("PASS Throw NotAllowedError.");
+                return navigator.credentials.get(requestOptions)
             else
                 messageToTop("Throw " + exception.name  + ".");
         }
-    );
+    ).then(function(value) {
+            messageToTop("Access granted. " + value);
+        },
+        function(exception) {
+            if (exception.name == "NotAllowedError")
+                messageToTop("PASS Throw NotAllowedError for both PublicKeyCredential's [[create]] and [[get]].");
+            else
+                messageToTop("Throw " + exception.name  + ".");
+    });
     </script>
 </head>
 </html>
diff --git a/LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https-expected.txt b/LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https-expected.txt
new file mode 100644 (file)
index 0000000..da7b28a
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS navigator.credentials.store(). 
+
diff --git a/LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https.html b/LayoutTests/http/wpt/credential-management/credentialscontainer-store-basics.https.html
new file mode 100644 (file)
index 0000000..01b9bbb
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<title>Credential Management API: store() basics.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script>
+    function asciiToUint8Array(str)
+    {
+        var chars = [];
+        for (var i = 0; i < str.length; ++i)
+            chars.push(str.charCodeAt(i));
+        return new Uint8Array(chars);
+    }
+
+    promise_test(async function(t) {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "localhost",
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: asciiToUint8Array("123456"),
+                    displayName: "Appleseed",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+            }
+        };
+
+        const credential = await navigator.credentials.create(options);
+        return promise_rejects(t, "NotSupportedError",
+            navigator.credentials.store(credential));
+    }, "navigator.credentials.store().");
+</script>
index 27ddb4a..6088ce1 100644 (file)
@@ -20,14 +20,14 @@ PASS PublicKeyCredential interface: attribute rawId
 PASS PublicKeyCredential interface: attribute response 
 PASS PublicKeyCredential interface: operation getClientExtensionResults() 
 PASS PublicKeyCredential interface: operation isUserVerifyingPlatformAuthenticatorAvailable() 
-PASS PublicKeyCredential must be primary interface of credential 
-PASS Stringification of credential 
-PASS PublicKeyCredential interface: credential must inherit property "rawId" with the proper type 
-PASS PublicKeyCredential interface: credential must inherit property "response" with the proper type 
-PASS PublicKeyCredential interface: credential must inherit property "getClientExtensionResults()" with the proper type 
-PASS PublicKeyCredential interface: credential must inherit property "isUserVerifyingPlatformAuthenticatorAvailable()" with the proper type 
-PASS Credential interface: credential must inherit property "id" with the proper type 
-PASS Credential interface: credential must inherit property "type" with the proper type 
+PASS PublicKeyCredential must be primary interface of createdCredential 
+PASS Stringification of createdCredential 
+PASS PublicKeyCredential interface: createdCredential must inherit property "rawId" with the proper type 
+PASS PublicKeyCredential interface: createdCredential must inherit property "response" with the proper type 
+PASS PublicKeyCredential interface: createdCredential must inherit property "getClientExtensionResults()" with the proper type 
+PASS PublicKeyCredential interface: createdCredential must inherit property "isUserVerifyingPlatformAuthenticatorAvailable()" with the proper type 
+PASS Credential interface: createdCredential must inherit property "id" with the proper type 
+PASS Credential interface: createdCredential must inherit property "type" with the proper type 
 PASS AuthenticatorResponse interface: existence and properties of interface object 
 PASS AuthenticatorResponse interface object length 
 PASS AuthenticatorResponse interface object name 
@@ -40,10 +40,10 @@ PASS AuthenticatorAttestationResponse interface object name
 PASS AuthenticatorAttestationResponse interface: existence and properties of interface prototype object 
 PASS AuthenticatorAttestationResponse interface: existence and properties of interface prototype object's "constructor" property 
 PASS AuthenticatorAttestationResponse interface: attribute attestationObject 
-PASS AuthenticatorAttestationResponse must be primary interface of credential.response 
-PASS Stringification of credential.response 
-PASS AuthenticatorAttestationResponse interface: credential.response must inherit property "attestationObject" with the proper type 
-PASS AuthenticatorResponse interface: credential.response must inherit property "clientDataJSON" with the proper type 
+PASS AuthenticatorAttestationResponse must be primary interface of createdCredential.response 
+PASS Stringification of createdCredential.response 
+PASS AuthenticatorAttestationResponse interface: createdCredential.response must inherit property "attestationObject" with the proper type 
+PASS AuthenticatorResponse interface: createdCredential.response must inherit property "clientDataJSON" with the proper type 
 PASS AuthenticatorAssertionResponse interface: existence and properties of interface object 
 PASS AuthenticatorAssertionResponse interface object length 
 PASS AuthenticatorAssertionResponse interface object name 
@@ -52,4 +52,10 @@ PASS AuthenticatorAssertionResponse interface: existence and properties of inter
 PASS AuthenticatorAssertionResponse interface: attribute authenticatorData 
 PASS AuthenticatorAssertionResponse interface: attribute signature 
 PASS AuthenticatorAssertionResponse interface: attribute userHandle 
+PASS AuthenticatorAssertionResponse must be primary interface of requestedCredential.response 
+PASS Stringification of requestedCredential.response 
+PASS AuthenticatorAssertionResponse interface: requestedCredential.response must inherit property "authenticatorData" with the proper type 
+PASS AuthenticatorAssertionResponse interface: requestedCredential.response must inherit property "signature" with the proper type 
+PASS AuthenticatorAssertionResponse interface: requestedCredential.response must inherit property "userHandle" with the proper type 
+PASS AuthenticatorResponse interface: requestedCredential.response must inherit property "clientDataJSON" with the proper type 
 
index ad2bcbf..040a145 100644 (file)
@@ -35,7 +35,7 @@ promise_test(async () => {
     `);
     idlArray.add_idls(idlText);
 
-    const options = {
+    const creationOptions = {
         publicKey: {
             rp: {
                 name: "localhost",
@@ -49,9 +49,17 @@ promise_test(async () => {
             pubKeyCredParams: [{ type: "public-key", alg: -7 }],
         }
     };
-    credential = await navigator.credentials.create(options);
+    createdCredential = await navigator.credentials.create(creationOptions);
 
-    idlArray.add_objects({"PublicKeyCredential": ["credential"], "AuthenticatorAttestationResponse": ["credential.response"]});
+
+    const requestOptions = {
+        publicKey: {
+            challenge: Base64URL.parse("MTIzNDU2"),
+        }
+    };
+    requestedCredential = await navigator.credentials.get(requestOptions);
+
+    idlArray.add_objects({"PublicKeyCredential": ["createdCredential"], "AuthenticatorAttestationResponse": ["createdCredential.response"], "AuthenticatorAssertionResponse": ["requestedCredential.response"]});
     idlArray.test();
 }, "Setup for WebAuthN API IDL tests.");
 </script>
index 0f3eeff..b11c793 100644 (file)
@@ -30,7 +30,7 @@
                 assert_equals(bytesToHexString(credential.response.attestationObject), '000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001ff3374b98316b38046727a770b8e95c4580a292b9e2f4bb44a250a5402d6d3783a');
                 try {
                     assert_throws("NotSupportedError", credential.getClientExtensionResults());
-                } catch { }
+                } catch(error) { }
             });
     }, "PublicKeyCredential's [[create]] with minimum options");
 </script>
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https-expected.txt
new file mode 100644 (file)
index 0000000..2e880cf
--- /dev/null
@@ -0,0 +1,5 @@
+
+PASS PublicKeyCredential's [[get]] with timeout 
+PASS PublicKeyCredential's [[get]] with a mismatched RP ID 
+PASS PublicKeyCredential's [[get]] with user cancellations 
+
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure.https.html
new file mode 100644 (file)
index 0000000..3354c3a
--- /dev/null
@@ -0,0 +1,41 @@
+<!DOCTYPE html>
+<title>Web Authentication API: PublicKeyCredential's [[get]] failure cases.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/util.js"></script>
+<script>
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                timeout: 0,
+            }
+        };
+        return promise_rejects(t, "NotAllowedError",
+            navigator.credentials.get(options));
+    }, "PublicKeyCredential's [[get]] with timeout");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                rpId: "example.com",
+                challenge: asciiToUint8Array("123456"),
+            }
+        };
+        return promise_rejects(t, "SecurityError",
+            navigator.credentials.get(options));
+    }, "PublicKeyCredential's [[get]] with a mismatched RP ID");
+
+    // This test is targeting to the dummy authenticator, which always cancel the operation
+    // when allowCredentials is not empty.
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: asciiToUint8Array("123456"),
+                allowCredentials: [{ type: "public-key", id: asciiToUint8Array("123456") }],
+            }
+        };
+        return promise_rejects(t, "NotAllowedError",
+            navigator.credentials.get(options));
+    }, "PublicKeyCredential's [[get]] with user cancellations");
+</script>
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https-expected.txt b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https-expected.txt
new file mode 100644 (file)
index 0000000..78d5370
--- /dev/null
@@ -0,0 +1,3 @@
+
+PASS PublicKeyCredential's [[get]] with minimum options 
+
diff --git a/LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https.html b/LayoutTests/http/wpt/webauthn/public-key-credential-get-success.https.html
new file mode 100644 (file)
index 0000000..8015f39
--- /dev/null
@@ -0,0 +1,31 @@
+<!DOCTYPE html>
+<title>Web Authentication API: PublicKeyCredential's [[get]] success cases.</title>
+<script src="/resources/testharness.js"></script>
+<script src="/resources/testharnessreport.js"></script>
+<script src="./resources/util.js"></script>
+<script>
+    // The following test is specifically tuned for current dummy authenticator.
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                challenge: Base64URL.parse("MTIzNDU2"),
+            }
+        };
+
+        return navigator.credentials.get(options).then(function(credential) {
+            const clientDataJsonHash = '577cc24b64b3dd011c61a8efb240aee7e77acc3c144d6dfbe097c6a208bb6d49';
+
+            assert_equals(credential.id, 'V3zCS2Sz3QEcYajvskCu5-d6zDwUTW374JfGogi7bUk');
+            assert_equals(credential.type, 'public-key');
+            assert_equals(bytesToHexString(credential.rawId), clientDataJsonHash);
+            assert_equals(bytesToASCIIString(credential.response.clientDataJSON), '{"type":"webauthn.get","challenge":"MTIzNDU2","origin":"https://localhost:9443","hashAlgorithm":"SHA-256"}');
+            // This field is completely fake 0x00*43 | 0x0001ff | SHA-256 hash of the clientDataJSON
+            assert_equals(bytesToHexString(credential.response.authenticatorData), clientDataJsonHash);
+            assert_equals(bytesToHexString(credential.response.signature), clientDataJsonHash);
+            assert_equals(bytesToHexString(credential.response.userHandle), clientDataJsonHash);
+            try {
+                assert_throws("NotSupportedError", credential.getClientExtensionResults());
+            } catch(error) { }
+        });
+    }, "PublicKeyCredential's [[get]] with minimum options");
+</script>
index ad2ffb1..bac6f6a 100644 (file)
@@ -1,3 +1,43 @@
+2018-01-24  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthN] Implement PublicKeyCredential’s [[DiscoverFromExternalSource]] with a dummy authenticator
+        https://bugs.webkit.org/show_bug.cgi?id=182032
+        <rdar://problem/36459922>
+
+        Reviewed by Brent Fulgham.
+
+        This patch implements PublicKeyCredential's [[DiscoverFromExternalSource]] from
+        https://www.w3.org/TR/webauthn/#getAssertion as of 5 December 2017. In order to
+        do testing, a dummy authenticator is implemented to exercise a failure and a
+        pass path. A number of dependencies need to be resolved later in order to comply
+        with the spec, which are marked by FIXME in the patch and tracked by proper
+        bugs. Those dependencies will be addressed once the first prototype is finshed.
+
+        Tests: http/tests/webauthn/public-key-credential-get-with-invalid-parameters.https.html
+               http/wpt/credential-management/credentialscontainer-store-basics.https.html
+               http/wpt/webauthn/public-key-credential-get-failure.https.html
+               http/wpt/webauthn/public-key-credential-get-success.https.html
+
+        * Modules/credentialmanagement/CredentialsContainer.cpp:
+        (WebCore::CredentialsContainer::get):
+        (WebCore::CredentialsContainer::isCreate):
+        Fixes some minor issues.
+        * Modules/webauthn/Authenticator.cpp:
+        (WebCore::Authenticator::getAssertion const):
+        * Modules/webauthn/Authenticator.h:
+        (WebCore::Authenticator::AssertionReturnBundle::AssertionReturnBundle):
+        * Modules/webauthn/PublicKeyCredential.cpp:
+        (WebCore::PublicKeyCredential::collectFromCredentialStore):
+        Changed a parameter type.
+        (WebCore::PublicKeyCredential::discoverFromExternalSource):
+        (WebCore::PublicKeyCredential::create):
+        Improved some comments.
+        * Modules/webauthn/PublicKeyCredential.h:
+        * Modules/webauthn/PublicKeyCredentialRequestOptions.h:
+        (): Deleted.
+        * bindings/js/JSAuthenticatorResponseCustom.cpp:
+        (WebCore::toJSNewlyCreated):
+
 2018-01-24  Dean Jackson  <dino@apple.com>
 
         Move WebGL's colorspace code into IOSurface
index 3fad75a..d47c084 100644 (file)
@@ -71,12 +71,10 @@ bool CredentialsContainer::doesHaveSameOriginAsItsAncestors()
     return true;
 }
 
-// FIXME: Since the underlying authenticator model is not clear at this moment, the timer is moved to CredentialsContainer such that
+// FIXME(181946): Since the underlying authenticator model is not clear at this moment, the timer is moved to CredentialsContainer such that
 // timer can stay with main thread and therefore can easily time out activities on the work queue.
-// https://bugs.webkit.org/show_bug.cgi?id=181946.
-// FIXME: The usages of AbortSignal are also moved here for the very same reason. Also the AbortSignal is kind of bogus at this moment
+// FIXME(181945): The usages of AbortSignal are also moved here for the very same reason. Also the AbortSignal is kind of bogus at this moment
 // since it doesn't support observers (or other means) to trigger the actual abort action. Enhancement to AbortSignal is needed.
-// https://bugs.webkit.org/show_bug.cgi?id=181945.
 template<typename OperationType>
 void CredentialsContainer::dispatchTask(OperationType&& operation, Ref<DeferredPromise>&& promise, std::optional<unsigned long> timeOutInMs)
 {
@@ -86,8 +84,7 @@ void CredentialsContainer::dispatchTask(OperationType&& operation, Ref<DeferredP
 
     auto* promiseIndex = promise.ptr();
     auto weakThis = m_weakPtrFactory.createWeakPtr(*this);
-    // FIXME: We should probably trim timeOutInMs to some max allowable number.
-    // https://bugs.webkit.org/show_bug.cgi?id=181947
+    // FIXME(181947): We should probably trim timeOutInMs to some max allowable number.
     if (timeOutInMs) {
         auto pendingPromise = PendingPromise::create(WTFMove(promise), std::make_unique<Timer>([promiseIndex, weakThis] () {
             ASSERT(isMainThread());
@@ -110,11 +107,8 @@ void CredentialsContainer::dispatchTask(OperationType&& operation, Ref<DeferredP
                 if (auto promise = weakThis->m_pendingPromises.take(promiseIndex)) {
                     if (result.hasException())
                         promise.value()->promise->reject(result.releaseException());
-                    else {
-                        // FIXME: Got some crazy compile error when I was trying to pass RHS to the resolve method.
-                        RefPtr<BasicCredential> credential = result.releaseReturnValue();
-                        promise.value()->promise->resolve<IDLNullable<IDLInterface<BasicCredential>>>(credential.get());
-                    }
+                    else
+                        promise.value()->promise->resolve<IDLNullable<IDLInterface<BasicCredential>>>(result.returnValue().get());
                 }
             }
         });
@@ -124,6 +118,8 @@ void CredentialsContainer::dispatchTask(OperationType&& operation, Ref<DeferredP
 
 void CredentialsContainer::get(CredentialRequestOptions&& options, Ref<DeferredPromise>&& promise)
 {
+    // The following implements https://www.w3.org/TR/credential-management-1/#algorithm-request as of 4 August 2017
+    // with enhancement from 14 November 2017 Editor's Draft.
     // FIXME: Optional options are passed with no contents. It should be std::optional.
     if ((!options.signal && !options.publicKey) || !m_document) {
         promise->reject(Exception { NotSupportedError });
@@ -133,19 +129,22 @@ void CredentialsContainer::get(CredentialRequestOptions&& options, Ref<DeferredP
         promise->reject(Exception { AbortError });
         return;
     }
+    // Step 1-2.
     ASSERT(m_document->isSecureContext());
 
-    // The followings is a shortcut to https://www.w3.org/TR/credential-management-1/#algorithm-request,
-    // as we only support PublicKeyCredential which can only be requested from [[discoverFromExternalSource]].
+    // Step 3 is enhanced with doesHaveSameOriginAsItsAncestors.
+    // Step 4-6. Shortcut as we only support PublicKeyCredential which can only
+    // be requested from [[discoverFromExternalSource]].
     if (!options.publicKey) {
         promise->reject(Exception { NotSupportedError });
         return;
     }
 
-    auto operation = [options = WTFMove(options)] (const SecurityOrigin& origin, bool isSameOriginWithItsAncestors) {
+    auto timeout = options.publicKey->timeout;
+    auto operation = [options = WTFMove(options.publicKey.value())] (const SecurityOrigin& origin, bool isSameOriginWithItsAncestors) {
         return PublicKeyCredential::discoverFromExternalSource(origin, options, isSameOriginWithItsAncestors);
     };
-    dispatchTask(WTFMove(operation), WTFMove(promise), options.publicKey->timeout);
+    dispatchTask(WTFMove(operation), WTFMove(promise), timeout);
 }
 
 void CredentialsContainer::store(const BasicCredential&, Ref<DeferredPromise>&& promise)
@@ -181,7 +180,7 @@ void CredentialsContainer::isCreate(CredentialCreationOptions&& options, Ref<Def
         // Shortcut as well.
         return PublicKeyCredential::create(origin, options, isSameOriginWithItsAncestors);
     };
-    dispatchTask(WTFMove(operation), WTFMove(promise), options.publicKey->timeout);
+    dispatchTask(WTFMove(operation), WTFMove(promise), timeout);
 }
 
 void CredentialsContainer::preventSilentAccess(Ref<DeferredPromise>&& promise) const
index 7278783..c195223 100644 (file)
@@ -26,6 +26,8 @@
 #include "config.h"
 #include "Authenticator.h"
 
+#include <AuthenticatorAttestationResponse.h>
+#include <wtf/CurrentTime.h>
 #include <wtf/NeverDestroyed.h>
 
 namespace WebCore {
@@ -55,4 +57,19 @@ ExceptionOr<Vector<uint8_t>> Authenticator::makeCredential(const Vector<uint8_t>
     return WTFMove(attestationObject);
 }
 
+ExceptionOr<Authenticator::AssertionReturnBundle> Authenticator::getAssertion(const String&, const Vector<uint8_t>& hash, const Vector<PublicKeyCredentialDescriptor>& allowCredentialIds) const
+{
+    // The followings is just a dummy implementaion to support initial development.
+    // User cancellation is effecively NotAllowedError.
+    if (!allowCredentialIds.isEmpty())
+        return Exception { NotAllowedError };
+
+    // FIXME: Delay processing for 0.1 seconds to simulate a timeout condition. This code will be removed
+    // when the full test infrastructure is set up.
+    WTF::sleep(0.1);
+
+    // Fill all parts with hash
+    return AssertionReturnBundle(ArrayBuffer::create(hash.data(), hash.size()), ArrayBuffer::create(hash.data(), hash.size()), ArrayBuffer::create(hash.data(), hash.size()), ArrayBuffer::create(hash.data(), hash.size()));
+}
+
 } // namespace WebCore
index f3c5b36..dbcea94 100644 (file)
 
 namespace WebCore {
 
-// FIXME: Consider moving all static methods from PublicKeyCredential to here and making this
+// FIXME(181946): Consider moving all static methods from PublicKeyCredential to here and making this
 // as an authenticator manager that controls all authenticator activities, mostly likely asnyc
 // for attestations.
 class Authenticator {
     WTF_MAKE_NONCOPYABLE(Authenticator);
     friend class NeverDestroyed<Authenticator>;
 public:
+    // FIXME(181946): After moving all static methods from PublicKeyCredential to here, we will probably
+    // return PublicKeyCredential directly and get rid of the following return type.
+    struct AssertionReturnBundle {
+        AssertionReturnBundle(Ref<ArrayBuffer>&& id, Ref<ArrayBuffer>&& data, Ref<ArrayBuffer>&& sig, Ref<ArrayBuffer>&& handle)
+            : credentialID(WTFMove(id))
+            , authenticatorData(WTFMove(data))
+            , signature(WTFMove(sig))
+            , userHandle(WTFMove(handle))
+        {
+        }
+
+        Ref<ArrayBuffer> credentialID;
+        Ref<ArrayBuffer> authenticatorData;
+        Ref<ArrayBuffer> signature;
+        Ref<ArrayBuffer> userHandle;
+    };
+
     static Authenticator& singleton();
 
     // Omit requireResidentKey, requireUserPresence, and requireUserVerification as we always provide resident keys and require user verification.
     ExceptionOr<Vector<uint8_t>> makeCredential(const Vector<uint8_t>& hash, const PublicKeyCredentialCreationOptions::RpEntity&, const PublicKeyCredentialCreationOptions::UserEntity&, const Vector<PublicKeyCredentialCreationOptions::Parameters>&, const Vector<PublicKeyCredentialDescriptor>& excludeCredentialIds) const;
+    ExceptionOr<AssertionReturnBundle> getAssertion(const String& rpId, const Vector<uint8_t>& hash, const Vector<PublicKeyCredentialDescriptor>& allowCredentialIds) const;
 
 #if !COMPILER(MSVC)
 private:
index e683dca..97881d5 100644 (file)
@@ -28,8 +28,9 @@
 
 #include "Authenticator.h"
 #include "AuthenticatorResponse.h"
-#include "CredentialCreationOptions.h"
 #include "JSDOMPromiseDeferred.h"
+#include "PublicKeyCredentialCreationOptions.h"
+#include "PublicKeyCredentialRequestOptions.h"
 #include "SecurityOrigin.h"
 #include <pal/crypto/CryptoDigest.h>
 #include <wtf/CurrentTime.h>
@@ -50,9 +51,7 @@ enum class ClientDataType {
     Get
 };
 
-// FIXME: Add token binding ID and extensions.
-// https://bugs.webkit.org/show_bug.cgi?id=181948
-// https://bugs.webkit.org/show_bug.cgi?id=181949
+// FIXME(181948): Add token binding ID and extensions.
 static Ref<ArrayBuffer> produceClientDataJson(ClientDataType type, const BufferSource& challenge, const SecurityOrigin& origin)
 {
     auto object = JSON::Object::create();
@@ -100,14 +99,48 @@ PublicKeyCredential::PublicKeyCredential(RefPtr<ArrayBuffer>&& id, RefPtr<Authen
 {
 }
 
-Vector<Ref<BasicCredential>> PublicKeyCredential::collectFromCredentialStore(CredentialRequestOptions&&, bool)
+Vector<Ref<BasicCredential>> PublicKeyCredential::collectFromCredentialStore(PublicKeyCredentialRequestOptions&&, bool)
 {
     return { };
 }
 
-ExceptionOr<RefPtr<BasicCredential>> PublicKeyCredential::discoverFromExternalSource(const SecurityOrigin&, const CredentialRequestOptions&, bool)
+ExceptionOr<RefPtr<BasicCredential>> PublicKeyCredential::discoverFromExternalSource(const SecurityOrigin& callerOrigin, const PublicKeyCredentialRequestOptions& options, bool sameOriginWithAncestors)
 {
-    return Exception { NotSupportedError };
+    using namespace PublicKeyCredentialInternal;
+
+    // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
+    // FIXME: Extensions are not supported yet. Skip Step 8-9.
+    // Step 1, 3-4, 13, 16 are handled by the caller, including options sanitizing, timer and abort signal.
+    // Step 2.
+    if (!sameOriginWithAncestors)
+        return Exception { NotAllowedError };
+
+    // Step 5-7.
+    // FIXME(181950): We lack fundamental support from SecurityOrigin to determine if a host is a valid domain or not.
+    // Step 6 is therefore skipped. Also, we lack the support to determine whether a domain is a registrable
+    // domain suffix of another domain. Hence restrict the comparison to equal in Step 7.
+    if (!options.rpId.isEmpty() && !(callerOrigin.host() == options.rpId))
+        return Exception { SecurityError };
+    if (options.rpId.isEmpty())
+        options.rpId = callerOrigin.host();
+
+    // Step 10-12.
+    auto clientDataJson = produceClientDataJson(ClientDataType::Get, options.challenge, callerOrigin);
+    auto clientDataJsonHash = produceClientDataJsonHash(clientDataJson);
+
+    // Step 14-15, 17-19.
+    // Only platform attachments will be supported at this stage. Assuming one authenticator per device.
+    // Also, resident keys, user verifications and direct attestation are enforced at this tage.
+    // For better performance, no filtering is done here regarding to options.excludeCredentials.
+    // What's more, user cancellations effectively means NotAllowedError. Therefore, the below call
+    // will only returns either an exception or a PublicKeyCredential ref.
+    // FIXME(181946): The following operation might need to perform async.
+    auto result = Authenticator::singleton().getAssertion(options.rpId, clientDataJsonHash, options.allowCredentials);
+    if (result.hasException())
+        return result.releaseException();
+
+    auto bundle = result.releaseReturnValue();
+    return ExceptionOr<RefPtr<BasicCredential>>(PublicKeyCredential::create(WTFMove(bundle.credentialID), AuthenticatorAssertionResponse::create(WTFMove(clientDataJson), WTFMove(bundle.authenticatorData), WTFMove(bundle.signature), WTFMove(bundle.userHandle))));
 }
 
 RefPtr<BasicCredential> PublicKeyCredential::store(RefPtr<BasicCredential>&&, bool)
@@ -121,16 +154,15 @@ ExceptionOr<RefPtr<BasicCredential>> PublicKeyCredential::create(const SecurityO
 
     // The following implements https://www.w3.org/TR/webauthn/#createCredential as of 5 December 2017.
     // FIXME: Extensions are not supported yet. Skip Step 11-12.
-    // Step 1, 3, 4, 17 are handled by the caller, including options sanitizing, timer and abort signal.
+    // Step 1, 3-4, 16-17 are handled by the caller, including options sanitizing, timer and abort signal.
     // Step 2.
     if (!sameOriginWithAncestors)
         return Exception { NotAllowedError };
 
     // Step 5-7.
-    // FIXME: We lack fundamental support from SecurityOrigin to determine if a host is a valid domain or not.
+    // FIXME(181950): We lack fundamental support from SecurityOrigin to determine if a host is a valid domain or not.
     // Step 6 is therefore skipped. Also, we lack the support to determine whether a domain is a registrable
     // domain suffix of another domain. Hence restrict the comparison to equal in Step 7.
-    // https://bugs.webkit.org/show_bug.cgi?id=181950
     if (!options.rp.id.isEmpty() && !(callerOrigin.host() == options.rp.id))
         return Exception { SecurityError };
     if (options.rp.id.isEmpty())
@@ -152,16 +184,13 @@ ExceptionOr<RefPtr<BasicCredential>> PublicKeyCredential::create(const SecurityO
     // For better performance, no filtering is done here regarding to options.excludeCredentials.
     // What's more, user cancellations effectively means NotAllowedError. Therefore, the below call
     // will only returns either an exception or a PublicKeyCredential ref.
-    // FIXME: The following operation might need to perform async.
-    // https://bugs.webkit.org/show_bug.cgi?id=181946
+    // FIXME(181946): The following operation might need to perform async.
     auto result = Authenticator::singleton().makeCredential(clientDataJsonHash, options.rp, options.user, options.pubKeyCredParams, options.excludeCredentials);
     if (result.hasException())
         return result.releaseException();
 
     auto attestationObject = result.releaseReturnValue();
-    // FIXME: Got some crazy compile error when I was trying to return RHS directly.
-    RefPtr<BasicCredential> credential = PublicKeyCredential::create(getIdFromAttestationObject(attestationObject), AuthenticatorAttestationResponse::create(WTFMove(clientDataJson), ArrayBuffer::create(attestationObject.data(), attestationObject.size())));
-    return WTFMove(credential);
+    return ExceptionOr<RefPtr<BasicCredential>>(PublicKeyCredential::create(getIdFromAttestationObject(attestationObject), AuthenticatorAttestationResponse::create(WTFMove(clientDataJson), ArrayBuffer::create(attestationObject.data(), attestationObject.size()))));
 }
 
 ArrayBuffer* PublicKeyCredential::rawId() const
index 5b866b9..e655895 100644 (file)
@@ -37,7 +37,7 @@ class DeferredPromise;
 class SecurityOrigin;
 
 struct PublicKeyCredentialCreationOptions;
-struct CredentialRequestOptions;
+struct PublicKeyCredentialRequestOptions;
 
 class PublicKeyCredential final : public BasicCredential {
 public:
@@ -46,8 +46,8 @@ public:
         return adoptRef(*new PublicKeyCredential(WTFMove(id), WTFMove(response)));
     }
 
-    static Vector<Ref<BasicCredential>> collectFromCredentialStore(CredentialRequestOptions&&, bool);
-    static ExceptionOr<RefPtr<BasicCredential>> discoverFromExternalSource(const SecurityOrigin&, const CredentialRequestOptions&, bool sameOriginWithAncestors);
+    static Vector<Ref<BasicCredential>> collectFromCredentialStore(PublicKeyCredentialRequestOptions&&, bool);
+    static ExceptionOr<RefPtr<BasicCredential>> discoverFromExternalSource(const SecurityOrigin&, const PublicKeyCredentialRequestOptions&, bool sameOriginWithAncestors);
     static RefPtr<BasicCredential> store(RefPtr<BasicCredential>&&, bool);
     static ExceptionOr<RefPtr<BasicCredential>> create(const SecurityOrigin&, const PublicKeyCredentialCreationOptions&, bool sameOriginWithAncestors);
 
index e49e1c8..7dab4b6 100644 (file)
@@ -33,8 +33,8 @@ namespace WebCore {
 
 struct PublicKeyCredentialRequestOptions {
     BufferSource challenge;
-    unsigned long timeout { 0 };
-    String rpId;
+    std::optional<unsigned long> timeout;
+    mutable String rpId;
     Vector<PublicKeyCredentialDescriptor> allowCredentials;
 };
 
index afe094e..09acace 100644 (file)
@@ -26,6 +26,7 @@
 #include "config.h"
 #include "JSAuthenticatorResponse.h"
 
+#include "JSAuthenticatorAssertionResponse.h"
 #include "JSAuthenticatorAttestationResponse.h"
 #include "JSDOMBinding.h"
 
@@ -36,6 +37,8 @@ JSValue toJSNewlyCreated(JSC::ExecState*, JSDOMGlobalObject* globalObject, Ref<A
 {
     if (is<AuthenticatorAttestationResponse>(response))
         return createWrapper<AuthenticatorAttestationResponse>(globalObject, WTFMove(response));
+    if (is<AuthenticatorAssertionResponse>(response))
+        return createWrapper<AuthenticatorAssertionResponse>(globalObject, WTFMove(response));
     return createWrapper<AuthenticatorResponse>(globalObject, WTFMove(response));
 }