Title: [259680] trunk
Revision
259680
Author
[email protected]
Date
2020-04-07 16:01:20 -0700 (Tue, 07 Apr 2020)

Log Message

[WebAuthn] Cancel WebAuthn requests when users cancel LocalAuthentication prompts
https://bugs.webkit.org/show_bug.cgi?id=209923
<rdar://problem/61223713>

Reviewed by Brent Fulgham.

Source/WebCore:

Covered by new tests within existing test files.

* testing/MockWebAuthenticationConfiguration.h:
(WebCore::MockWebAuthenticationConfiguration::LocalConfiguration::encode const):
(WebCore::MockWebAuthenticationConfiguration::LocalConfiguration::decode):
* testing/MockWebAuthenticationConfiguration.idl:
Adds a new parameter to reflect user cancellations on LocalAuthentication UI.

Source/WebKit:

This patch intents to streamline WebAuthn local authenticator UX a bit more. Here, we should treat user
cancellation of the LocalAuthentication UI as if it were being done on the UI Client's WebAuthn UI.

* UIProcess/WebAuthentication/Authenticator.h:
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::cancelRequest):
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
* UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
(WebKit::LocalAuthenticator::continueMakeCredentialAfterUserVerification):
(WebKit::LocalAuthenticator::continueGetAssertionAfterUserVerification):
(WebKit::LocalAuthenticator::validateUserVerification const):
* UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
* UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
(WebKit::LocalConnection::verifyUser const):
* UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
(WebKit::MockLocalConnection::MockLocalConnection):
(WebKit::MockLocalConnection::verifyUser const):
(WebKit::MockLocalConnection::filterResponses const):
* WebKit.xcodeproj/project.pbxproj:

Tools:

Modifies existing tests to accommodate changes in MockWebAuthenticationConfiguration.idl.

* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html:
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-duplicate-credential.html:
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-error.html:
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la.html:

LayoutTests:

Adds a new test for the change and modifies existing tests to accommodate changes in MockWebAuthenticationConfiguration.idl.

* http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html:
* http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt:
* http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-create-success-local.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html:
* http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
* http/wpt/webauthn/public-key-credential-get-success-local.https.html:

Modified Paths

Diff

Modified: trunk/LayoutTests/ChangeLog (259679 => 259680)


--- trunk/LayoutTests/ChangeLog	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/ChangeLog	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,3 +1,21 @@
+2020-04-07  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Cancel WebAuthn requests when users cancel LocalAuthentication prompts
+        https://bugs.webkit.org/show_bug.cgi?id=209923
+        <rdar://problem/61223713>
+
+        Reviewed by Brent Fulgham.
+
+        Adds a new test for the change and modifies existing tests to accommodate changes in MockWebAuthenticationConfiguration.idl.
+
+        * http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html:
+        * http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt:
+        * http/wpt/webauthn/public-key-credential-create-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-create-success-local.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html:
+        * http/wpt/webauthn/public-key-credential-get-failure-local.https.html:
+        * http/wpt/webauthn/public-key-credential-get-success-local.https.html:
+
 2020-04-07  Jacob Uphoff  <[email protected]>
 
         [ macOS debug ] REGRESSION (r259463): http/tests/media/clearkey/collect-webkit-media-session.html is failing

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local-silent.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -6,7 +6,7 @@
 <script>
     // Default mock configuration. Tests need to override if they need different configuration.
     if (window.internals)
-        internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+        internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "no", acceptAttestation: false } });
 
     promise_test(t => {
         const options = {
@@ -126,7 +126,7 @@
             }
         };
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "yes", acceptAttestation: false } });
         return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
     }, "PublicKeyCredential's [[create]] without private keys in a mock local authenticator.");
 
@@ -152,7 +152,7 @@
             }
         };
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: true, acceptAttestation: false, privateKeyBase64: privateKeyBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "yes", acceptAttestation: false, privateKeyBase64: privateKeyBase64 } });
         return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.").then(() => {
             if (window.testRunner)
                 testRunner.cleanUpKeychain(testRpId, credentialIDBase64);

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https-expected.txt	2020-04-07 23:01:20 UTC (rev 259680)
@@ -7,4 +7,5 @@
 PASS PublicKeyCredential's [[create]] without attestation in a mock local authenticator. 
 PASS PublicKeyCredential's [[create]] not deleting old credential in a mock local authenticator. 
 PASS PublicKeyCredential's [[create]] with timeout in a mock local authenticator. 
+PASS PublicKeyCredential's [[create]] with user cancel in a mock local authenticator. 
 

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -6,7 +6,7 @@
 <script>
     // Default mock configuration. Tests need to override if they need different configuration.
     if (window.internals)
-        internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+        internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false } });
 
     promise_test(t => {
         const options = {
@@ -121,7 +121,7 @@
             }
         };
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false } });
         return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't create private key.");
     }, "PublicKeyCredential's [[create]] without private keys in a mock local authenticator.");
 
@@ -146,7 +146,7 @@
             }
         };
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, privateKeyBase64: privateKeyBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false, privateKeyBase64: privateKeyBase64 } });
         return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't attest: The operation couldn't complete.").then(() => {
             if (window.testRunner)
                 testRunner.cleanUpKeychain(testRpId, credentialIDBase64);
@@ -173,7 +173,7 @@
             }
         };
         if (window.internals) {
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false } });
             testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, testUserEntityBundleBase64);
         }
         return promiseRejects(t, "UnknownError", navigator.credentials.create(options), "Couldn't create private key.").then(() => {
@@ -202,7 +202,29 @@
         };
 
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false } });
         return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Operation timed out.");
     }, "PublicKeyCredential's [[create]] with timeout in a mock local authenticator.");
+
+    promise_test(function(t) {
+        const options = {
+            publicKey: {
+                rp: {
+                    name: "example.com"
+                },
+                user: {
+                    name: "John Appleseed",
+                    id: asciiToUint8Array("123456"),
+                    displayName: "John",
+                },
+                challenge: asciiToUint8Array("123456"),
+                pubKeyCredParams: [{ type: "public-key", alg: -7 }],
+            }
+        };
+
+        if (window.internals)
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "cancel", acceptAttestation: false } });
+        return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "This request has been cancelled by the user.");
+    }, "PublicKeyCredential's [[create]] with user cancel in a mock local authenticator.");
+
 </script>

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-create-success-local.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -52,7 +52,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: false,
                     privateKeyBase64: privateKeyBase64,
                 }
@@ -85,7 +85,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: false,
                     privateKeyBase64: privateKeyBase64,
                 }
@@ -119,7 +119,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: false,
                     privateKeyBase64: privateKeyBase64,
                 }
@@ -163,7 +163,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: false,
                     privateKeyBase64: privateKeyBase64,
                 }
@@ -197,7 +197,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: true,
                     privateKeyBase64: privateKeyBase64,
                     userCertificateBase64: testAttestationCertificateBase64,
@@ -233,7 +233,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: true,
                     privateKeyBase64: privateKeyBase64,
                     userCertificateBase64: testAttestationCertificateBase64,
@@ -269,7 +269,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: false,
                     privateKeyBase64: privateKeyBase64,
                 }
@@ -308,7 +308,7 @@
         if (window.internals)
             internals.setMockWebAuthenticationConfiguration({
                 local: {
-                    acceptAuthentication: true,
+                    userVerification: "yes",
                     acceptAttestation: true,
                     privateKeyBase64: privateKeyBase64,
                     userCertificateBase64: testAttestationCertificateBase64,

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local-silent.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -6,7 +6,7 @@
 <script>
     promise_test(t => {
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "no", acceptAttestation: false } });
 
         const options = {
             publicKey: {
@@ -30,7 +30,7 @@
         const credentialIDBase64 = btoa(String.fromCharCode.apply(0, credentialID));
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "no", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {
@@ -56,7 +56,7 @@
         const credentialIDBase64 = btoa(String.fromCharCode.apply(0, credentialID));
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { acceptAuthentication: false, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ silentFailure: true, local: { userVerification: "no", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -6,7 +6,7 @@
 <script>
     promise_test(t => {
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false } });
 
         const options = {
             publicKey: {
@@ -29,7 +29,7 @@
         const credentialIDBase64 = btoa(String.fromCharCode.apply(0, credentialID));
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {
@@ -54,7 +54,7 @@
         const credentialIDBase64 = btoa(String.fromCharCode.apply(0, credentialID));
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {

Modified: trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html (259679 => 259680)


--- trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/LayoutTests/http/wpt/webauthn/public-key-credential-get-success-local.https.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -41,7 +41,7 @@
         const credentialIDBase64 = base64encode(credentialID);
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {
@@ -62,7 +62,7 @@
         const credentialIDBase64 = base64encode(credentialID);
         // Default mock configuration. Tests need to override if they need different configuration.
         if (window.internals)
-            internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
+            internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false, preferredCredentialIdBase64: credentialIDBase64 } });
 
         const options = {
             publicKey: {

Modified: trunk/Source/WebCore/ChangeLog (259679 => 259680)


--- trunk/Source/WebCore/ChangeLog	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebCore/ChangeLog	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,3 +1,19 @@
+2020-04-07  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Cancel WebAuthn requests when users cancel LocalAuthentication prompts
+        https://bugs.webkit.org/show_bug.cgi?id=209923
+        <rdar://problem/61223713>
+
+        Reviewed by Brent Fulgham.
+
+        Covered by new tests within existing test files.
+
+        * testing/MockWebAuthenticationConfiguration.h:
+        (WebCore::MockWebAuthenticationConfiguration::LocalConfiguration::encode const):
+        (WebCore::MockWebAuthenticationConfiguration::LocalConfiguration::decode):
+        * testing/MockWebAuthenticationConfiguration.idl:
+        Adds a new parameter to reflect user cancellations on LocalAuthentication UI.
+
 2020-04-07  Tadeu Zagallo  <[email protected]>
 
         Not using strict mode within ClassDeclaration statement

Modified: trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h (259679 => 259680)


--- trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.h	2020-04-07 23:01:20 UTC (rev 259680)
@@ -61,8 +61,14 @@
         MaliciousPayload
     };
 
+    enum class UserVerification : uint8_t {
+        No,
+        Yes,
+        Cancel
+    };
+
     struct LocalConfiguration {
-        bool acceptAuthentication { false };
+        UserVerification userVerification { UserVerification::No };
         bool acceptAttestation { false };
         String privateKeyBase64;
         String userCertificateBase64;
@@ -112,7 +118,7 @@
 template<class Encoder>
 void MockWebAuthenticationConfiguration::LocalConfiguration::encode(Encoder& encoder) const
 {
-    encoder << acceptAuthentication << acceptAttestation << privateKeyBase64 << userCertificateBase64 << intermediateCACertificateBase64 << preferredCredentialIdBase64;
+    encoder << userVerification << acceptAttestation << privateKeyBase64 << userCertificateBase64 << intermediateCACertificateBase64 << preferredCredentialIdBase64;
 }
 
 template<class Decoder>
@@ -120,11 +126,11 @@
 {
     MockWebAuthenticationConfiguration::LocalConfiguration result;
 
-    Optional<bool> acceptAuthentication;
-    decoder >> acceptAuthentication;
-    if (!acceptAuthentication)
+    Optional<UserVerification> userVerification;
+    decoder >> userVerification;
+    if (!userVerification)
         return WTF::nullopt;
-    result.acceptAuthentication = *acceptAuthentication;
+    result.userVerification = *userVerification;
 
     Optional<bool> acceptAttestation;
     decoder >> acceptAttestation;
@@ -297,6 +303,15 @@
     >;
 };
 
+template<> struct EnumTraits<WebCore::MockWebAuthenticationConfiguration::UserVerification> {
+    using values = EnumValues<
+        WebCore::MockWebAuthenticationConfiguration::UserVerification,
+        WebCore::MockWebAuthenticationConfiguration::UserVerification::No,
+        WebCore::MockWebAuthenticationConfiguration::UserVerification::Yes,
+        WebCore::MockWebAuthenticationConfiguration::UserVerification::Cancel
+    >;
+};
+
 } // namespace WTF
 
 #endif // ENABLE(WEB_AUTHN)

Modified: trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl (259679 => 259680)


--- trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebCore/testing/MockWebAuthenticationConfiguration.idl	2020-04-07 23:01:20 UTC (rev 259680)
@@ -61,6 +61,14 @@
 
 [
     Conditional=WEB_AUTHN,
+] enum UserVerification {
+    "no",
+    "yes",
+    "cancel"
+};
+
+[
+    Conditional=WEB_AUTHN,
 ] dictionary MockWebAuthenticationConfiguration {
     boolean silentFailure = false;
     MockLocalConfiguration local;
@@ -71,7 +79,7 @@
 [
     Conditional=WEB_AUTHN,
 ] dictionary MockLocalConfiguration {
-    boolean acceptAuthentication = false;
+    UserVerification userVerification = "no";
     boolean acceptAttestation = false;
     DOMString privateKeyBase64;
     DOMString userCertificateBase64;

Modified: trunk/Source/WebKit/ChangeLog (259679 => 259680)


--- trunk/Source/WebKit/ChangeLog	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/ChangeLog	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,3 +1,32 @@
+2020-04-07  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Cancel WebAuthn requests when users cancel LocalAuthentication prompts
+        https://bugs.webkit.org/show_bug.cgi?id=209923
+        <rdar://problem/61223713>
+
+        Reviewed by Brent Fulgham.
+
+        This patch intents to streamline WebAuthn local authenticator UX a bit more. Here, we should treat user
+        cancellation of the LocalAuthentication UI as if it were being done on the UI Client's WebAuthn UI.
+
+        * UIProcess/WebAuthentication/Authenticator.h:
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::cancelRequest):
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
+        (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserVerification):
+        (WebKit::LocalAuthenticator::continueGetAssertionAfterUserVerification):
+        (WebKit::LocalAuthenticator::validateUserVerification const):
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
+        (WebKit::LocalConnection::verifyUser const):
+        * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
+        (WebKit::MockLocalConnection::MockLocalConnection):
+        (WebKit::MockLocalConnection::verifyUser const):
+        (WebKit::MockLocalConnection::filterResponses const):
+        * WebKit.xcodeproj/project.pbxproj:
+
 2020-04-07  Sihui Liu  <[email protected]>
 
         [ macOS ] Update sandbox rules for storage

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Authenticator.h	2020-04-07 23:01:20 UTC (rev 259680)
@@ -57,6 +57,7 @@
         virtual void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) = 0;
         virtual void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, WebAuthenticationSource, CompletionHandler<void(WebCore::AuthenticatorAssertionResponse*)>&&) = 0;
         virtual void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) = 0;
+        virtual void cancelRequest() = 0;
     };
 
     virtual ~Authenticator() = default;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp	2020-04-07 23:01:20 UTC (rev 259680)
@@ -179,9 +179,7 @@
         if (frameID && frameID != pendingFrameID->frameID)
             return;
     }
-    invokePendingCompletionHandler(ExceptionData { NotAllowedError, "Operation timed out."_s });
-    clearState();
-    m_requestTimeOutTimer.stop();
+    cancelRequest();
 }
 
 // The following implements part of Step 20. of https://www.w3.org/TR/webauthn/#createCredential
@@ -192,9 +190,7 @@
     RELEASE_ASSERT(RunLoop::isMain());
     if (!m_pendingCompletionHandler || m_pendingRequestData.panel.get() != &panel)
         return;
-    invokePendingCompletionHandler(ExceptionData { NotAllowedError, "This request has been cancelled by the user."_s });
-    clearState();
-    m_requestTimeOutTimer.stop();
+    cancelRequest();
 }
 
 void AuthenticatorManager::clearStateAsync()
@@ -295,6 +291,13 @@
     });
 }
 
+void AuthenticatorManager::cancelRequest()
+{
+    invokePendingCompletionHandler(ExceptionData { NotAllowedError, "This request has been cancelled by the user."_s });
+    clearState();
+    m_requestTimeOutTimer.stop();
+}
+
 UniqueRef<AuthenticatorTransportService> AuthenticatorManager::createService(AuthenticatorTransport transport, AuthenticatorTransportService::Observer& observer) const
 {
     return AuthenticatorTransportService::create(transport, observer);

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h	2020-04-07 23:01:20 UTC (rev 259680)
@@ -84,6 +84,7 @@
     void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) final;
     void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, WebAuthenticationSource, CompletionHandler<void(WebCore::AuthenticatorAssertionResponse*)>&&) final;
     void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) final;
+    void cancelRequest() final;
 
     // Overriden by MockAuthenticatorManager.
     virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h	2020-04-07 23:01:20 UTC (rev 259680)
@@ -68,6 +68,7 @@
 
     void receiveException(WebCore::ExceptionData&&, WebAuthenticationStatus = WebAuthenticationStatus::LAError) const;
     void deleteDuplicateCredential() const;
+    bool validateUserVerification(LocalConnection::UserVerification) const;
 
     State m_state { State::Init };
     UniqueRef<LocalConnection> m_connection;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm	2020-04-07 23:01:20 UTC (rev 259680)
@@ -274,10 +274,8 @@
     m_state = State::UserVerified;
     auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
 
-    if (verification == LocalConnection::UserVerification::No) {
-        receiveException({ NotAllowedError, "Couldn't verify user."_s });
+    if (!validateUserVerification(verification))
         return;
-    }
 
     // Here is the keychain schema.
     // kSecAttrLabel: RP ID
@@ -500,10 +498,8 @@
     ASSERT(m_state == State::ResponseSelected);
     m_state = State::UserVerified;
 
-    if (verification == LocalConnection::UserVerification::No) {
-        receiveException({ NotAllowedError, "Couldn't verify user."_s });
+    if (!validateUserVerification(verification))
         return;
-    }
 
     // Step 10.
     auto authData = buildAuthData(WTF::get<PublicKeyCredentialRequestOptions>(requestData().options).rpId, getAssertionFlags, counter, { });
@@ -588,6 +584,22 @@
     });
 }
 
+bool LocalAuthenticator::validateUserVerification(LocalConnection::UserVerification verification) const
+{
+    if (verification == LocalConnection::UserVerification::Cancel) {
+        if (auto* observer = this->observer())
+            observer->cancelRequest();
+        return false;
+    }
+
+    if (verification == LocalConnection::UserVerification::No) {
+        receiveException({ NotAllowedError, "Couldn't verify user."_s });
+        return false;
+    }
+
+    return true;
+}
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h	2020-04-07 23:01:20 UTC (rev 259680)
@@ -51,9 +51,10 @@
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(LocalConnection);
 public:
-    enum class UserVerification : bool {
+    enum class UserVerification : uint8_t {
         No,
-        Yes
+        Yes,
+        Cancel
     };
 
     using AttestationCallback = CompletionHandler<void(NSArray *, NSError *)>;

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm	2020-04-07 23:01:20 UTC (rev 259680)
@@ -83,6 +83,8 @@
         if (error) {
             LOG_ERROR("Couldn't authenticate with biometrics: %@", error);
             verification = UserVerification::No;
+            if (error.code == LAErrorUserCancel)
+                verification = UserVerification::Cancel;
         }
         RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), verification, context = WTFMove(context)] () mutable {
             completionHandler(verification, context.get());

Modified: trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm (259679 => 259680)


--- trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm	2020-04-07 23:01:20 UTC (rev 259680)
@@ -38,22 +38,31 @@
 #import "LocalAuthenticationSoftLink.h"
 
 namespace WebKit {
+using namespace WebCore;
 
-MockLocalConnection::MockLocalConnection(const WebCore::MockWebAuthenticationConfiguration& configuration)
+MockLocalConnection::MockLocalConnection(const MockWebAuthenticationConfiguration& configuration)
     : m_configuration(configuration)
 {
 }
 
-void MockLocalConnection::verifyUser(const String&, WebCore::ClientDataType, SecAccessControlRef, UserVerificationCallback&& callback) const
+void MockLocalConnection::verifyUser(const String&, ClientDataType, SecAccessControlRef, UserVerificationCallback&& callback) const
 {
     // Mock async operations.
     RunLoop::main().dispatch([configuration = m_configuration, callback = WTFMove(callback)]() mutable {
         ASSERT(configuration.local);
-        if (!configuration.local->acceptAuthentication) {
-            callback(UserVerification::No, nil);
-            return;
+
+        UserVerification userVerification = UserVerification::No;
+        switch (configuration.local->userVerification) {
+        case MockWebAuthenticationConfiguration::UserVerification::No:
+            break;
+        case MockWebAuthenticationConfiguration::UserVerification::Yes:
+            userVerification = UserVerification::Yes;
+            break;
+        case MockWebAuthenticationConfiguration::UserVerification::Cancel:
+            userVerification = UserVerification::Cancel;
         }
-        callback(UserVerification::Yes, adoptNS([allocLAContextInstance() init]).get());
+
+        callback(userVerification, adoptNS([allocLAContextInstance() init]).get());
     });
 }
 
@@ -113,7 +122,7 @@
     });
 }
 
-void MockLocalConnection::filterResponses(HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>& responses) const
+void MockLocalConnection::filterResponses(HashSet<Ref<AuthenticatorAssertionResponse>>& responses) const
 {
     const auto& preferredCredentialIdBase64 = m_configuration.local->preferredCredentialIdBase64;
     if (preferredCredentialIdBase64.isEmpty())

Modified: trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj (259679 => 259680)


--- trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Source/WebKit/WebKit.xcodeproj/project.pbxproj	2020-04-07 23:01:20 UTC (rev 259680)
@@ -3902,6 +3902,7 @@
 		57B8E3D52355864A00D5C5D1 /* FidoAuthenticator.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = FidoAuthenticator.h; sourceTree = "<group>"; };
 		57B8E3D62355864A00D5C5D1 /* FidoAuthenticator.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = FidoAuthenticator.cpp; sourceTree = "<group>"; };
 		57BBEA6C22BC0BFE00273995 /* SOAuthorizationLoadPolicy.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = SOAuthorizationLoadPolicy.h; sourceTree = "<group>"; };
+		57C3E8AC242490D5001E2209 /* WebAuthenticationRequestData.cpp */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.cpp.cpp; path = WebAuthenticationRequestData.cpp; sourceTree = "<group>"; };
 		57C6244A234679A400383FE7 /* WebAuthenticationFlags.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = WebAuthenticationFlags.h; sourceTree = "<group>"; };
 		57DCED6A2142EAE20016B847 /* WebAuthenticatorCoordinatorMessages.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; name = WebAuthenticatorCoordinatorMessages.h; path = DerivedSources/WebKit2/WebAuthenticatorCoordinatorMessages.h; sourceTree = BUILT_PRODUCTS_DIR; };
 		57DCED6B2142EAE20016B847 /* WebAuthenticatorCoordinatorMessageReceiver.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; name = WebAuthenticatorCoordinatorMessageReceiver.cpp; path = DerivedSources/WebKit2/WebAuthenticatorCoordinatorMessageReceiver.cpp; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -8065,6 +8066,7 @@
 				57DCED9921489F4D0016B847 /* AuthenticatorTransportService.cpp */,
 				57DCED8A214853130016B847 /* AuthenticatorTransportService.h */,
 				57C6244A234679A400383FE7 /* WebAuthenticationFlags.h */,
+				57C3E8AC242490D5001E2209 /* WebAuthenticationRequestData.cpp */,
 				57DCEDA62149F9DA0016B847 /* WebAuthenticationRequestData.h */,
 				57608296202BD8BA00116678 /* WebAuthenticatorCoordinatorProxy.cpp */,
 				57608295202BD8BA00116678 /* WebAuthenticatorCoordinatorProxy.h */,

Modified: trunk/Tools/ChangeLog (259679 => 259680)


--- trunk/Tools/ChangeLog	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Tools/ChangeLog	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,3 +1,18 @@
+2020-04-07  Jiewen Tan  <[email protected]>
+
+        [WebAuthn] Cancel WebAuthn requests when users cancel LocalAuthentication prompts
+        https://bugs.webkit.org/show_bug.cgi?id=209923
+        <rdar://problem/61223713>
+
+        Reviewed by Brent Fulgham.
+
+        Modifies existing tests to accommodate changes in MockWebAuthenticationConfiguration.idl.
+
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-duplicate-credential.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-error.html:
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la.html:
+
 2020-04-07  Ryan Haddad  <[email protected]>
 
         Unreviewed, reverting r259655.

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html (259679 => 259680)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,7 +1,7 @@
 <input type="text" id="input">
 <script>
     if (window.internals) {
-        internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
+        internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "yes", acceptAttestation: false } });
         internals.withUserGesture(() => { input.focus(); });
     }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-duplicate-credential.html (259679 => 259680)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-duplicate-credential.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-duplicate-credential.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -18,7 +18,7 @@
     }
 
     if (window.internals) {
-        internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+        internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false } });
         internals.withUserGesture(() => { input.focus(); });
     }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-error.html (259679 => 259680)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-error.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la-error.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -1,7 +1,7 @@
 <input type="text" id="input">
 <script>
     if (window.internals) {
-        internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: false, acceptAttestation: false } });
+        internals.setMockWebAuthenticationConfiguration({ local: { userVerification: "no", acceptAttestation: false } });
         internals.withUserGesture(() => { input.focus(); });
     }
 

Modified: trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la.html (259679 => 259680)


--- trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la.html	2020-04-07 22:50:50 UTC (rev 259679)
+++ trunk/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-make-credential-la.html	2020-04-07 23:01:20 UTC (rev 259680)
@@ -32,7 +32,7 @@
     if (window.internals) {
         internals.setMockWebAuthenticationConfiguration({
             local: {
-                acceptAuthentication: true,
+                userVerification: "yes",
                 acceptAttestation: true,
                 privateKeyBase64: testES256PrivateKeyBase64,
                 userCertificateBase64: testAttestationCertificateBase64,
_______________________________________________
webkit-changes mailing list
[email protected]
https://lists.webkit.org/mailman/listinfo/webkit-changes

Reply via email to