[WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAu...
authorjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2020 21:42:39 +0000 (21:42 +0000)
committerjiewen_tan@apple.com <jiewen_tan@apple.com@268f45cc-cd09-0410-ab3c-d52691b4dbfc>
Wed, 4 Mar 2020 21:42:39 +0000 (21:42 +0000)
https://bugs.webkit.org/show_bug.cgi?id=208533
<rdar://problem/60010184>

Reviewed by Alex Christensen.

Source/WebCore:

Covered by new tests within existing test files.

* en.lproj/Localizable.strings:
* platform/LocalizedStrings.cpp:
(WebCore::touchIDPromptTitle):
(WebCore::biometricFallbackPromptTitle):
* platform/LocalizedStrings.h:
Adds localized strings to support the customized LocalAuthentication dialog.

Source/WebKit:

This patch implements the above SPI to replace -[_WKWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:].
The original SPI is designed on the premise that Safari is going to highly customize the LocalAuthentication UI, and that is not happening
anymore. Therefore, WebKit takes back the invocation of LocalAuthentication and offer a new SPI to tell clients when WebKit is about to
show LocalAuthentication UI. Clients then have the trigger to pull at their pleasure.

This patch implements all plumbings to replace the SPI. Besides that, this patch also:
1) enhances the LocalConnection::verifyUser with a slightly customized LocalAuthentication dialog;
2) adds the SPI used above into the SPI header;
3) makes _WKWebAuthenticationPanelDelegate.transports as a NSSet instead of a NSArray;
4) lets LocalService::isAvailable return false if Apple attestation is not available.

* Platform/spi/Cocoa/LocalAuthenticationSPI.h:
* UIProcess/API/APIWebAuthenticationPanelClient.h:
(API::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
(API::WebAuthenticationPanelClient::verifyUser const): Deleted.
* UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h:
* UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
(-[_WKWebAuthenticationPanel transports]):
* UIProcess/WebAuthentication/Authenticator.h:
* UIProcess/WebAuthentication/AuthenticatorManager.cpp:
(WebKit::AuthenticatorManager::decidePolicyForLocalAuthenticator):
(WebKit::AuthenticatorManager::verifyUser): Deleted.
* UIProcess/WebAuthentication/AuthenticatorManager.h:
* UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
* UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
(WebKit::LocalAuthenticator::makeCredential):
(WebKit::LocalAuthenticator::continueMakeCredentialAfterDecidePolicy):
(WebKit::LocalAuthenticator::continueMakeCredentialAfterUserVerification):
(WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
(WebKit::LocalAuthenticator::getAssertion):
(WebKit::LocalAuthenticator::continueGetAssertionAfterResponseSelected):
(WebKit::LocalAuthenticator::continueGetAssertionAfterUserVerification):
(WebKit::LocalAuthenticator::continueMakeCredentialAfterUserConsented): Deleted.
(WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented): Deleted.
* UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
* UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
(WebKit::LocalConnection::verifyUser const):
(WebKit::LocalConnection::isUnlocked const): Deleted.
* UIProcess/WebAuthentication/Cocoa/LocalService.mm:
(WebKit::LocalService::isAvailable):
* UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h:
* UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm:
(WebKit::WebAuthenticationPanelClient::WebAuthenticationPanelClient):
(WebKit::localAuthenticatorPolicy):
(WebKit::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
(WebKit::WebAuthenticationPanelClient::verifyUser const): Deleted.
* UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
* UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
(WebKit::MockLocalConnection::verifyUser const):
(WebKit::MockLocalConnection::isUnlocked const): Deleted.
* UIProcess/WebAuthentication/WebAuthenticationFlags.h:

Tools:

* TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
* TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
(-[TestWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:]):
(TestWebKitAPI::TEST):
(-[TestWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:]): Deleted.
* TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html: Removed.

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

29 files changed:
LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html
LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html
Source/WebCore/ChangeLog
Source/WebCore/en.lproj/Localizable.strings
Source/WebCore/platform/LocalizedStrings.cpp
Source/WebCore/platform/LocalizedStrings.h
Source/WebKit/ChangeLog
Source/WebKit/Platform/spi/Cocoa/LocalAuthenticationSPI.h
Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h
Source/WebKit/UIProcess/API/C/WKPage.cpp
Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h
Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm
Source/WebKit/UIProcess/WebAuthentication/Authenticator.h
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp
Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm
Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalService.mm
Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h
Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm
Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.h
Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm
Source/WebKit/UIProcess/WebAuthentication/WebAuthenticationFlags.h
Tools/ChangeLog
Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj
Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm
Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html [deleted file]

index 0f69d0b..1caffd7 100644 (file)
@@ -98,7 +98,7 @@
                     pubKeyCredParams: [{ type: "public-key", alg: -7 }]
                 }
             };
-            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't verify user.");
         }, "PublicKeyCredential's [[create]] without user consent in a mock local authenticator.");
 
         promise_test(t => {
index 3e4f499..f27f711 100644 (file)
@@ -55,7 +55,7 @@
 
             if (window.testRunner)
                 testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
-            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
+            return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't verify user.").then(() => {
                 if (window.testRunner)
                     testRunner.cleanUpKeychain(testRpId, userhandleBase64);
             });
index 940acad..0898cae 100644 (file)
@@ -1,3 +1,20 @@
+2020-03-03  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
+        https://bugs.webkit.org/show_bug.cgi?id=208533
+        <rdar://problem/60010184>
+
+        Reviewed by Alex Christensen.
+
+        Covered by new tests within existing test files.
+
+        * en.lproj/Localizable.strings:
+        * platform/LocalizedStrings.cpp:
+        (WebCore::touchIDPromptTitle):
+        (WebCore::biometricFallbackPromptTitle):
+        * platform/LocalizedStrings.h:
+        Adds localized strings to support the customized LocalAuthentication dialog.
+
 2020-03-04  Antoine Quint  <graouts@apple.com>
 
         Add an SPI to allow UserStyleSheet injection to target a specific WKWebView
index 6d79c5c..755225e 100644 (file)
 /* Video Enter Full Screen context menu item */
 "Enter Full Screen" = "Enter Full Screen";
 
+/* Use passcode as a fallback to sign into this website */
+"Enter passcode to sign into this website." = "Enter passcode to sign into this website.";
+
 /* menu item */
 "Enter Picture in Picture" = "Enter Picture in Picture";
 
 /* prompt string in authentication panel */
 "To view this page, you must log in to this area on %@:" = "To view this page, you must log in to this area on %@:";
 
+/* Use Touch ID to sign into this website */
+"Touch ID to sign into this website." = "Touch ID to sign into this website.";
+
 /* Transformations context sub-menu item */
 "Transformations" = "Transformations";
 
index a114fd7..9ac0bd2 100644 (file)
@@ -1207,4 +1207,16 @@ String unacceptableTLSCertificate()
 }
 #endif
 
+#if ENABLE(WEB_AUTHN)
+String touchIDPromptTitle()
+{
+    return WEB_UI_STRING("Touch ID to sign into this website.", "Use Touch ID to sign into this website");
+}
+
+String biometricFallbackPromptTitle()
+{
+    return WEB_UI_STRING("Enter passcode to sign into this website.", "Use passcode as a fallback to sign into this website");
+}
+#endif
+
 } // namespace WebCore
index 3e33164..d718472 100644 (file)
@@ -339,6 +339,11 @@ namespace WebCore {
     WEBCORE_EXPORT String datePickerYearLabelTitle();
 #endif
 
+#if ENABLE(WEB_AUTHN)
+    WEBCORE_EXPORT String touchIDPromptTitle();
+    WEBCORE_EXPORT String biometricFallbackPromptTitle();
+#endif
+
 #if USE(GLIB) && defined(GETTEXT_PACKAGE)
 #define WEB_UI_STRING(string, description) WebCore::localizedString(_(string))
 #define WEB_UI_STRING_KEY(string, key, description) WebCore::localizedString(C_(key, string))
index 2a77c6f..0ab6a66 100644 (file)
@@ -1,3 +1,63 @@
+2020-03-03  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
+        https://bugs.webkit.org/show_bug.cgi?id=208533
+        <rdar://problem/60010184>
+
+        Reviewed by Alex Christensen.
+
+        This patch implements the above SPI to replace -[_WKWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:].
+        The original SPI is designed on the premise that Safari is going to highly customize the LocalAuthentication UI, and that is not happening
+        anymore. Therefore, WebKit takes back the invocation of LocalAuthentication and offer a new SPI to tell clients when WebKit is about to
+        show LocalAuthentication UI. Clients then have the trigger to pull at their pleasure.
+
+        This patch implements all plumbings to replace the SPI. Besides that, this patch also:
+        1) enhances the LocalConnection::verifyUser with a slightly customized LocalAuthentication dialog;
+        2) adds the SPI used above into the SPI header;
+        3) makes _WKWebAuthenticationPanelDelegate.transports as a NSSet instead of a NSArray;
+        4) lets LocalService::isAvailable return false if Apple attestation is not available.
+
+        * Platform/spi/Cocoa/LocalAuthenticationSPI.h:
+        * UIProcess/API/APIWebAuthenticationPanelClient.h:
+        (API::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
+        (API::WebAuthenticationPanelClient::verifyUser const): Deleted.
+        * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h:
+        * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
+        (-[_WKWebAuthenticationPanel transports]):
+        * UIProcess/WebAuthentication/Authenticator.h:
+        * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
+        (WebKit::AuthenticatorManager::decidePolicyForLocalAuthenticator):
+        (WebKit::AuthenticatorManager::verifyUser): Deleted.
+        * UIProcess/WebAuthentication/AuthenticatorManager.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
+        (WebKit::LocalAuthenticator::makeCredential):
+        (WebKit::LocalAuthenticator::continueMakeCredentialAfterDecidePolicy):
+        (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserVerification):
+        (WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
+        (WebKit::LocalAuthenticator::getAssertion):
+        (WebKit::LocalAuthenticator::continueGetAssertionAfterResponseSelected):
+        (WebKit::LocalAuthenticator::continueGetAssertionAfterUserVerification):
+        (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserConsented): Deleted.
+        (WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented): Deleted.
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
+        * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
+        (WebKit::LocalConnection::verifyUser const):
+        (WebKit::LocalConnection::isUnlocked const): Deleted.
+        * UIProcess/WebAuthentication/Cocoa/LocalService.mm:
+        (WebKit::LocalService::isAvailable):
+        * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h:
+        * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm:
+        (WebKit::WebAuthenticationPanelClient::WebAuthenticationPanelClient):
+        (WebKit::localAuthenticatorPolicy):
+        (WebKit::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
+        (WebKit::WebAuthenticationPanelClient::verifyUser const): Deleted.
+        * UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
+        * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
+        (WebKit::MockLocalConnection::verifyUser const):
+        (WebKit::MockLocalConnection::isUnlocked const): Deleted.
+        * UIProcess/WebAuthentication/WebAuthenticationFlags.h:
+
 2020-03-04  Alex Christensen  <achristensen@webkit.org>
 
         callAsyncJavaScript with an invalid parameter and no completionHandler should not crash
index e67f2bf..8e584d8 100644 (file)
 #else
 
 typedef NS_ENUM(NSInteger, LAOption) {
-    LAOptionNotInteractive,
+    LAOptionAuthenticationTitle,
+    LAOptionPasscodeTitle,
 };
 
 @interface LAContext(Private) <NSSecureCoding>
 
-- (NSDictionary *)evaluatePolicy:(LAPolicy)policy options:(NSDictionary *)options error:(NSError **)error;
+- (void)evaluateAccessControl:(SecAccessControlRef)accessControl operation:(LAAccessControlOperation)operation options:(NSDictionary *)options reply:(void(^)(NSDictionary *result, NSError *error))reply;
 
 @end
 
index 8c36853..cd0d6ab 100644 (file)
@@ -27,6 +27,7 @@
 
 #if ENABLE(WEB_AUTHN)
 
+#include "WebAuthenticationFlags.h"
 #include <wtf/CompletionHandler.h>
 #include <wtf/HashSet.h>
 #include <wtf/RefCounted.h>
@@ -39,11 +40,6 @@ namespace WebCore {
 class AuthenticatorAssertionResponse;
 }
 
-namespace WebKit {
-enum class WebAuthenticationStatus : uint8_t;
-enum class WebAuthenticationResult : bool;
-}
-
 namespace API {
 
 class WebAuthenticationPanelClient {
@@ -55,7 +51,7 @@ public:
     virtual void dismissPanel(WebKit::WebAuthenticationResult) const { }
     virtual void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&& completionHandler) const { completionHandler(emptyString()); }
     virtual void selectAssertionResponse(Vector<Ref<WebCore::AuthenticatorAssertionResponse>>&& responses, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&& completionHandler) const { ASSERT(!responses.isEmpty()); completionHandler(responses[0]); }
-    virtual void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&& completionHandler) const { completionHandler(nullptr); }
+    virtual void decidePolicyForLocalAuthenticator(CompletionHandler<void(WebKit::LocalAuthenticatorPolicy)>&& completionHandler) const { completionHandler(WebKit::LocalAuthenticatorPolicy::Disallow); }
 };
 
 } // namespace API
index 881264e..00f4905 100644 (file)
@@ -47,6 +47,8 @@
 #include "APIPolicyClient.h"
 #include "APISessionState.h"
 #include "APIUIClient.h"
+#include "APIWebAuthenticationPanel.h"
+#include "APIWebAuthenticationPanelClient.h"
 #include "APIWebsitePolicies.h"
 #include "APIWindowFeatures.h"
 #include "AuthenticationChallengeDisposition.h"
@@ -2079,12 +2081,19 @@ void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient
 
 #if ENABLE(WEB_AUTHN)
         // The current method is specialized for WebKitTestRunner.
-        void runWebAuthenticationPanel(WebPageProxy&, API::WebAuthenticationPanel&, WebFrameProxy&, FrameInfoData&&, CompletionHandler<void(WebKit::WebAuthenticationPanelResult)>&& completionHandler) final
+        void runWebAuthenticationPanel(WebPageProxy&, API::WebAuthenticationPanel& panel, WebFrameProxy&, FrameInfoData&&, CompletionHandler<void(WebKit::WebAuthenticationPanelResult)>&& completionHandler) final
         {
+            class PanelClient : public API::WebAuthenticationPanelClient {
+            public:
+                void decidePolicyForLocalAuthenticator(CompletionHandler<void(WebKit::LocalAuthenticatorPolicy)>&& completionHandler) const { completionHandler(WebKit::LocalAuthenticatorPolicy::Allow); }
+            };
+
             if (!m_client.runWebAuthenticationPanel) {
                 completionHandler(WebKit::WebAuthenticationPanelResult::Unavailable);
                 return;
             }
+
+            panel.setClient(WTF::makeUniqueRef<PanelClient>());
             completionHandler(WebKit::WebAuthenticationPanelResult::Presented);
         }
 #endif
index 9741dd6..883dff4 100644 (file)
@@ -68,6 +68,11 @@ typedef NS_ENUM(NSInteger, _WKWebAuthenticationType) {
     _WKWebAuthenticationTypeGet,
 } WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
+typedef NS_ENUM(NSInteger, _WKLocalAuthenticatorPolicy) {
+    _WKLocalAuthenticatorPolicyAllow,
+    _WKLocalAuthenticatorPolicyDisallow,
+} WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+
 @protocol _WKWebAuthenticationPanelDelegate <NSObject>
 
 @optional
@@ -76,7 +81,7 @@ typedef NS_ENUM(NSInteger, _WKWebAuthenticationType) {
 - (void)panel:(_WKWebAuthenticationPanel *)panel dismissWebAuthenticationPanelWithResult:(_WKWebAuthenticationResult)result WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 - (void)panel:(_WKWebAuthenticationPanel *)panel requestPINWithRemainingRetries:(NSUInteger)retries completionHandler:(void (^)(NSString *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 - (void)panel:(_WKWebAuthenticationPanel *)panel selectAssertionResponse:(NSArray < _WKWebAuthenticationAssertionResponse *> *)responses completionHandler:(void (^)(_WKWebAuthenticationAssertionResponse *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
-- (void)panel:(_WKWebAuthenticationPanel *)panel verifyUserWithAccessControl:(SecAccessControlRef)accessControl completionHandler:(void (^)(LAContext *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
+- (void)panel:(_WKWebAuthenticationPanel *)panel decidePolicyForLocalAuthenticatorWithCompletionHandler:(void (^)(_WKLocalAuthenticatorPolicy policy))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 
 @end
 
@@ -85,7 +90,7 @@ WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA))
 
 @property (nullable, nonatomic, weak) id <_WKWebAuthenticationPanelDelegate> delegate;
 @property (nonatomic, readonly, copy) NSString *relyingPartyID;
-@property (nonatomic, readonly, copy) NSArray *transports;
+@property (nonatomic, readonly, copy) NSSet *transports;
 @property (nonatomic, readonly) _WKWebAuthenticationType type;
 
 - (void)cancel;
index 1973b0f..25862bd 100644 (file)
@@ -33,7 +33,7 @@
 @implementation _WKWebAuthenticationPanel {
 #if ENABLE(WEB_AUTHN)
     WeakPtr<WebKit::WebAuthenticationPanelClient> _client;
-    RetainPtr<NSMutableArray> _transports;
+    RetainPtr<NSMutableSet> _transports;
 #endif
 }
 
@@ -81,13 +81,13 @@ static _WKWebAuthenticationTransport wkWebAuthenticationTransport(WebCore::Authe
     }
 }
 
-- (NSArray *)transports
+- (NSSet *)transports
 {
     if (_transports)
         return _transports.get();
 
     auto& transports = _panel->transports();
-    _transports = [[NSMutableArray alloc] initWithCapacity:transports.size()];
+    _transports = [[NSMutableSet alloc] initWithCapacity:transports.size()];
     for (auto& transport : transports)
         [_transports addObject:adoptNS([[NSNumber alloc] initWithInt:wkWebAuthenticationTransport(transport)]).get()];
     return _transports.get();
index 91d99ed..5671aec 100644 (file)
@@ -56,7 +56,7 @@ public:
         virtual void authenticatorStatusUpdated(WebAuthenticationStatus) = 0;
         virtual void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) = 0;
         virtual void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) = 0;
-        virtual void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) = 0;
+        virtual void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) = 0;
     };
 
     virtual ~Authenticator() = default;
index fcc27ad..c391801 100644 (file)
@@ -295,11 +295,10 @@ void AuthenticatorManager::selectAssertionResponse(const HashSet<Ref<Authenticat
     });
 }
 
-void AuthenticatorManager::verifyUser(SecAccessControlRef accessControlRef, CompletionHandler<void(LAContext *)>&& completionHandler)
+void AuthenticatorManager::decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&& completionHandler)
 {
-    RetainPtr<SecAccessControlRef> accessControl = accessControlRef;
-    dispatchPanelClientCall([accessControl = WTFMove(accessControl), completionHandler = WTFMove(completionHandler)] (const API::WebAuthenticationPanel& panel) mutable {
-        panel.client().verifyUser(accessControl.get(), WTFMove(completionHandler));
+    dispatchPanelClientCall([completionHandler = WTFMove(completionHandler)] (const API::WebAuthenticationPanel& panel) mutable {
+        panel.client().decidePolicyForLocalAuthenticator(WTFMove(completionHandler));
     });
 }
 
index e8f2d2b..bd0b96d 100644 (file)
@@ -83,7 +83,7 @@ private:
     void authenticatorStatusUpdated(WebAuthenticationStatus) final;
     void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) final;
     void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) final;
-    void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) final;
+    void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) final;
 
     // Overriden by MockAuthenticatorManager.
     virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;
index 9a47570..69ed34e 100644 (file)
@@ -38,14 +38,15 @@ namespace WebKit {
 class LocalAuthenticator final : public Authenticator {
 public:
     // Here is the FSM.
-    // MakeCredential: Init => RequestReceived => UserConsented => Attested => End
-    // GetAssertion: Init => RequestReceived => ResponseSelected => UserConsented => End
+    // MakeCredential: Init => RequestReceived => PolicyDecided => UserVerified => Attested => End
+    // GetAssertion: Init => RequestReceived => ResponseSelected => UserVerified => End
     enum class State {
         Init,
         RequestReceived,
-        UserConsented,
+        UserVerified,
         Attested,
-        ResponseSelected
+        ResponseSelected,
+        PolicyDecided,
     };
 
     static Ref<LocalAuthenticator> create(UniqueRef<LocalConnection>&& connection)
@@ -57,12 +58,13 @@ private:
     explicit LocalAuthenticator(UniqueRef<LocalConnection>&&);
 
     void makeCredential() final;
-    void continueMakeCredentialAfterUserConsented(SecAccessControlRef, LAContext *);
+    void continueMakeCredentialAfterDecidePolicy(LocalAuthenticatorPolicy);
+    void continueMakeCredentialAfterUserVerification(SecAccessControlRef, LocalConnection::UserVerification, LAContext *);
     void continueMakeCredentialAfterAttested(SecKeyRef, Vector<uint8_t>&& credentialId, Vector<uint8_t>&& authData, NSArray *certificates, NSError *);
 
     void getAssertion() final;
     void continueGetAssertionAfterResponseSelected(Ref<WebCore::AuthenticatorAssertionResponse>&&);
-    void continueGetAssertionAfterUserConsented(LAContext *, Ref<WebCore::AuthenticatorAssertionResponse>&&);
+    void continueGetAssertionAfterUserVerification(Ref<WebCore::AuthenticatorAssertionResponse>&&, LocalConnection::UserVerification, LAContext *);
 
     void receiveException(WebCore::ExceptionData&&, WebAuthenticationStatus = WebAuthenticationStatus::LAError) const;
 
index ffc60ea..f1d9445 100644 (file)
@@ -160,6 +160,28 @@ void LocalAuthenticator::makeCredential()
 
     // Step 6.
     // Get user consent.
+    if (auto* observer = this->observer()) {
+        auto callback = [weakThis = makeWeakPtr(*this)] (LocalAuthenticatorPolicy policy) {
+            ASSERT(RunLoop::isMain());
+            if (!weakThis)
+                return;
+
+            weakThis->continueMakeCredentialAfterDecidePolicy(policy);
+        };
+        observer->decidePolicyForLocalAuthenticator(WTFMove(callback));
+    }
+}
+
+void LocalAuthenticator::continueMakeCredentialAfterDecidePolicy(LocalAuthenticatorPolicy policy)
+{
+    ASSERT(m_state == State::RequestReceived);
+    m_state = State::PolicyDecided;
+
+    if (policy == LocalAuthenticatorPolicy::Disallow) {
+        receiveRespond(ExceptionData { UnknownError, "Disallow local authenticator."_s });
+        return;
+    }
+
     RetainPtr<SecAccessControlRef> accessControl;
     {
         CFErrorRef errorRef = nullptr;
@@ -171,29 +193,27 @@ void LocalAuthenticator::makeCredential()
         }
     }
 
-    if (auto* observer = this->observer()) {
-        SecAccessControlRef accessControlRef = accessControl.get();
-        auto callback = [accessControl = WTFMove(accessControl), weakThis = makeWeakPtr(*this)] (LAContext *context) {
-            ASSERT(RunLoop::isMain());
-            if (!weakThis)
-                return;
+    SecAccessControlRef accessControlRef = accessControl.get();
+    auto callback = [accessControl = WTFMove(accessControl), weakThis = makeWeakPtr(*this)] (LocalConnection::UserVerification verification, LAContext *context) {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
 
-            weakThis->continueMakeCredentialAfterUserConsented(accessControl.get(), context);
-        };
-        observer->verifyUser(accessControlRef, WTFMove(callback));
-    }
+        weakThis->continueMakeCredentialAfterUserVerification(accessControl.get(), verification, context);
+    };
+    m_connection->verifyUser(accessControlRef, WTFMove(callback));
 }
 
-void LocalAuthenticator::continueMakeCredentialAfterUserConsented(SecAccessControlRef accessControlRef, LAContext *context)
+void LocalAuthenticator::continueMakeCredentialAfterUserVerification(SecAccessControlRef accessControlRef, LocalConnection::UserVerification verification, LAContext *context)
 {
     using namespace LocalAuthenticatorInternal;
 
-    ASSERT(m_state == State::RequestReceived);
-    m_state = State::UserConsented;
+    ASSERT(m_state == State::PolicyDecided);
+    m_state = State::UserVerified;
     auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
 
-    if (!m_connection->isUnlocked(context)) {
-        receiveRespond(ExceptionData { NotAllowedError, "Couldn't get user consent."_s });
+    if (verification == LocalConnection::UserVerification::No) {
+        receiveException({ NotAllowedError, "Couldn't verify user."_s });
         return;
     }
 
@@ -309,7 +329,7 @@ void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
 {
     using namespace LocalAuthenticatorInternal;
 
-    ASSERT(m_state == State::UserConsented);
+    ASSERT(m_state == State::UserVerified);
     m_state = State::Attested;
     auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
 
@@ -392,7 +412,7 @@ void LocalAuthenticator::getAssertion()
         return;
     }
 
-    // Step 6.
+    // Step 6-7. User consent is implicitly acquired by selecting responses.
     for (NSDictionary *attribute : intersectedCredentialsAttributes) {
         auto addResult = m_assertionResponses.add(AuthenticatorAssertionResponse::create(
             toArrayBuffer(attribute[(id)kSecAttrApplicationLabel]),
@@ -422,28 +442,28 @@ void LocalAuthenticator::continueGetAssertionAfterResponseSelected(Ref<WebCore::
     ASSERT(m_state == State::RequestReceived);
     m_state = State::ResponseSelected;
 
-    // Step 7. Get user consent.
-    if (auto* observer = this->observer()) {
-        auto accessControlRef = response->accessControl();
-        auto callback = [weakThis = makeWeakPtr(*this), response = WTFMove(response)] (LAContext *context) mutable {
-            ASSERT(RunLoop::isMain());
-            if (!weakThis)
-                return;
+    auto accessControlRef = response->accessControl();
+    auto callback = [
+        weakThis = makeWeakPtr(*this),
+        response = WTFMove(response)
+    ] (LocalConnection::UserVerification verification, LAContext *context) mutable {
+        ASSERT(RunLoop::isMain());
+        if (!weakThis)
+            return;
 
-            weakThis->continueGetAssertionAfterUserConsented(context, WTFMove(response));
-        };
-        observer->verifyUser(accessControlRef, WTFMove(callback));
-    }
+        weakThis->continueGetAssertionAfterUserVerification(WTFMove(response), verification, context);
+    };
+    m_connection->verifyUser(accessControlRef, WTFMove(callback));
 }
 
-void LocalAuthenticator::continueGetAssertionAfterUserConsented(LAContext *context, Ref<WebCore::AuthenticatorAssertionResponse>&& response)
+void LocalAuthenticator::continueGetAssertionAfterUserVerification(Ref<WebCore::AuthenticatorAssertionResponse>&& response, LocalConnection::UserVerification verification, LAContext *context)
 {
     using namespace LocalAuthenticatorInternal;
     ASSERT(m_state == State::ResponseSelected);
-    m_state = State::UserConsented;
+    m_state = State::UserVerified;
 
-    if (!m_connection->isUnlocked(context)) {
-        receiveRespond(ExceptionData { NotAllowedError, "Couldn't get user consent."_s });
+    if (verification == LocalConnection::UserVerification::No) {
+        receiveException({ NotAllowedError, "Couldn't verify user."_s });
         return;
     }
 
@@ -456,23 +476,20 @@ void LocalAuthenticator::continueGetAssertionAfterUserConsented(LAContext *conte
     // Step 11.
     RetainPtr<CFDataRef> signature;
     {
-        auto query = adoptNS([[NSMutableDictionary alloc] init]);
-        [query addEntriesFromDictionary:@{
+        NSDictionary *query = @{
             (id)kSecClass: (id)kSecClassKey,
             (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
             (id)kSecAttrApplicationLabel: toNSData(response->rawId()).get(),
+            (id)kSecUseAuthenticationContext: context,
             (id)kSecReturnRef: @YES,
 #if HAVE(DATA_PROTECTION_KEYCHAIN)
             (id)kSecUseDataProtectionKeychain: @YES
 #else
             (id)kSecAttrNoLegacy: @YES
 #endif
-        }];
-        // context is nullptr in mock testing.
-        if (context)
-            [query setObject:context forKey:(id)kSecUseAuthenticationContext];
+        };
         CFTypeRef privateKeyRef = nullptr;
-        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query.get(), &privateKeyRef);
+        OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &privateKeyRef);
         if (status) {
             receiveException({ UnknownError, makeString("Couldn't get the private key reference: ", status) });
             return;
index 8a7662c..bc62f3d 100644 (file)
@@ -36,9 +36,7 @@
 OBJC_CLASS LAContext;
 
 namespace WebCore {
-
 class AuthenticatorAssertionResponse;
-
 }
 
 namespace WebKit {
@@ -52,13 +50,19 @@ class LocalConnection {
     WTF_MAKE_FAST_ALLOCATED;
     WTF_MAKE_NONCOPYABLE(LocalConnection);
 public:
+    enum class UserVerification : bool {
+        No,
+        Yes
+    };
+
     using AttestationCallback = CompletionHandler<void(NSArray *, NSError *)>;
+    using UserVerificationCallback = CompletionHandler<void(UserVerification, LAContext *)>;
 
     LocalConnection() = default;
     virtual ~LocalConnection() = default;
 
     // Overrided by MockLocalConnection.
-    virtual bool isUnlocked(LAContext *) const;
+    virtual void verifyUser(SecAccessControlRef, UserVerificationCallback&&) const;
     virtual RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const;
     virtual void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const;
     virtual void filterResponses(HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&) const { };
index 53e0123..4b7721a 100644 (file)
@@ -28,6 +28,7 @@
 
 #if ENABLE(WEB_AUTHN)
 
+#import <WebCore/LocalizedStrings.h>
 #import <wtf/BlockPtr.h>
 #import <wtf/RunLoop.h>
 
 
 namespace WebKit {
 
-bool LocalConnection::isUnlocked(LAContext *context) const
+void LocalConnection::verifyUser(SecAccessControlRef accessControl, UserVerificationCallback&& completionHandler) const
 {
-    NSError *error = nil;
-    auto result = [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics options:@{ @(LAOptionNotInteractive): @YES } error:&error];
-    if (!result)
-        LOG_ERROR("Couldn't get user consent: %@", error);
-    return !!result;
+    auto context = adoptNS([allocLAContextInstance() init]);
+
+    auto options = adoptNS([[NSMutableDictionary alloc] init]);
+    if ([context biometryType] == LABiometryTypeTouchID)
+        [options setObject:WebCore::touchIDPromptTitle() forKey:@(LAOptionAuthenticationTitle)];
+    [options setObject:WebCore::biometricFallbackPromptTitle() forKey:@(LAOptionPasscodeTitle)];
+
+    auto reply = makeBlockPtr([context, completionHandler = WTFMove(completionHandler)] (NSDictionary *, NSError *error) mutable {
+        ASSERT(!RunLoop::isMain());
+
+        UserVerification verification = UserVerification::Yes;
+        if (error) {
+            LOG_ERROR("Couldn't authenticate with biometrics: %@", error);
+            verification = UserVerification::No;
+        }
+        RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), verification, context = WTFMove(context)] () mutable {
+            completionHandler(verification, context.get());
+        });
+    });
+
+    [context evaluateAccessControl:accessControl operation:LAAccessControlOperationUseKeySign options:options.get() reply:reply.get()];
 }
 
 RetainPtr<SecKeyRef> LocalConnection::createCredentialPrivateKey(LAContext *context, SecAccessControlRef accessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const
index 6bc0825..ad7be13 100644 (file)
@@ -55,6 +55,8 @@ bool LocalService::isAvailable()
 
 #if defined(LOCALSERVICE_ADDITIONS)
 LOCALSERVICE_ADDITIONS
+#else
+    return false;
 #endif
 
     return true;
index df3d95a..d2edff6 100644 (file)
@@ -50,7 +50,7 @@ private:
     void dismissPanel(WebAuthenticationResult) const final;
     void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&&) const final;
     void selectAssertionResponse(Vector<Ref<WebCore::AuthenticatorAssertionResponse>>&&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) const final;
-    void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) const final;
+    void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) const final;
 
     _WKWebAuthenticationPanel *m_panel;
     WeakObjCPtr<id <_WKWebAuthenticationPanelDelegate> > m_delegate;
@@ -60,7 +60,7 @@ private:
         bool panelDismissWebAuthenticationPanelWithResult : 1;
         bool panelRequestPinWithRemainingRetriesCompletionHandler : 1;
         bool panelSelectAssertionResponseCompletionHandler : 1;
-        bool panelVerifyUserWithAccessControlCompletionHandler : 1;
+        bool panelDecidePolicyForLocalAuthenticatorCompletionHandler : 1;
     } m_delegateMethods;
 };
 
index 0e973fb..dbdf5ae 100644 (file)
@@ -32,7 +32,6 @@
 #import "APIWebAuthenticationAssertionResponse.h"
 #import "CompletionHandlerCallChecker.h"
 #import "WKNSArray.h"
-#import "WebAuthenticationFlags.h"
 #import "_WKWebAuthenticationAssertionResponseInternal.h"
 #import "_WKWebAuthenticationPanel.h"
 #import <wtf/BlockPtr.h>
@@ -50,7 +49,7 @@ WebAuthenticationPanelClient::WebAuthenticationPanelClient(_WKWebAuthenticationP
     m_delegateMethods.panelDismissWebAuthenticationPanelWithResult = [delegate respondsToSelector:@selector(panel:dismissWebAuthenticationPanelWithResult:)];
     m_delegateMethods.panelRequestPinWithRemainingRetriesCompletionHandler = [delegate respondsToSelector:@selector(panel:requestPINWithRemainingRetries:completionHandler:)];
     m_delegateMethods.panelSelectAssertionResponseCompletionHandler = [delegate respondsToSelector:@selector(panel:selectAssertionResponse:completionHandler:)];
-    m_delegateMethods.panelVerifyUserWithAccessControlCompletionHandler = [delegate respondsToSelector:@selector(panel:verifyUserWithAccessControl:completionHandler:)];
+    m_delegateMethods.panelDecidePolicyForLocalAuthenticatorCompletionHandler = [delegate respondsToSelector:@selector(panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:)];
 }
 
 RetainPtr<id <_WKWebAuthenticationPanelDelegate> > WebAuthenticationPanelClient::delegate()
@@ -167,25 +166,37 @@ void WebAuthenticationPanelClient::selectAssertionResponse(Vector<Ref<WebCore::A
     }).get()];
 }
 
-void WebAuthenticationPanelClient::verifyUser(SecAccessControlRef accessControl, CompletionHandler<void(LAContext *)>&& completionHandler) const
+static LocalAuthenticatorPolicy localAuthenticatorPolicy(_WKLocalAuthenticatorPolicy result)
 {
-    if (!m_delegateMethods.panelVerifyUserWithAccessControlCompletionHandler) {
-        completionHandler(adoptNS([allocLAContextInstance() init]).get());
+    switch (result) {
+    case _WKLocalAuthenticatorPolicyAllow:
+        return LocalAuthenticatorPolicy::Allow;
+    case _WKLocalAuthenticatorPolicyDisallow:
+        return LocalAuthenticatorPolicy::Disallow;
+    }
+    ASSERT_NOT_REACHED();
+    return LocalAuthenticatorPolicy::Allow;
+}
+
+void WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&& completionHandler) const
+{
+    if (!m_delegateMethods.panelDecidePolicyForLocalAuthenticatorCompletionHandler) {
+        completionHandler(LocalAuthenticatorPolicy::Disallow);
         return;
     }
 
     auto delegate = m_delegate.get();
     if (!delegate) {
-        completionHandler(adoptNS([allocLAContextInstance() init]).get());
+        completionHandler(LocalAuthenticatorPolicy::Disallow);
         return;
     }
 
-    auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(panel:verifyUserWithAccessControl:completionHandler:));
-    [delegate panel:m_panel verifyUserWithAccessControl:accessControl completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](LAContext *context) mutable {
+    auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:));
+    [delegate panel:m_panel decidePolicyForLocalAuthenticatorWithCompletionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](_WKLocalAuthenticatorPolicy policy) mutable {
         if (checker->completionHandlerHasBeenCalled())
             return;
         checker->didCallCompletionHandler();
-        completionHandler(context);
+        completionHandler(localAuthenticatorPolicy(policy));
     }).get()];
 }
 
index d0dd9b1..dee773c 100644 (file)
@@ -37,7 +37,7 @@ public:
     explicit MockLocalConnection(const WebCore::MockWebAuthenticationConfiguration&);
 
 private:
-    bool isUnlocked(LAContext *) const final;
+    void verifyUser(SecAccessControlRef, UserVerificationCallback&&) const final;
     RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const final;
     void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const final;
     void filterResponses(HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&) const final;
index 8b8d824..b1c9874 100644 (file)
@@ -44,9 +44,17 @@ MockLocalConnection::MockLocalConnection(const WebCore::MockWebAuthenticationCon
 {
 }
 
-bool MockLocalConnection::isUnlocked(LAContext *context) const
+void MockLocalConnection::verifyUser(SecAccessControlRef, UserVerificationCallback&& callback) const
 {
-    return m_configuration.local->acceptAuthentication;
+    // 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;
+        }
+        callback(UserVerification::Yes, adoptNS([allocLAContextInstance() init]).get());
+    });
 }
 
 RetainPtr<SecKeyRef> MockLocalConnection::createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const
index 5092d93..658eebc 100644 (file)
@@ -51,6 +51,11 @@ enum class WebAuthenticationStatus : uint8_t {
     LANoCredential,
 };
 
+enum class LocalAuthenticatorPolicy : bool {
+    Allow,
+    Disallow
+};
+
 } // namespace WebKit
 
 #endif // ENABLE(WEB_AUTHN)
index e88943e..7004250 100644 (file)
@@ -1,3 +1,18 @@
+2020-03-03  Jiewen Tan  <jiewen_tan@apple.com>
+
+        [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
+        https://bugs.webkit.org/show_bug.cgi?id=208533
+        <rdar://problem/60010184>
+
+        Reviewed by Alex Christensen.
+
+        * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
+        * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
+        (-[TestWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:]):
+        (TestWebKitAPI::TEST):
+        (-[TestWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:]): Deleted.
+        * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html: Removed.
+
 2020-03-04  Alex Christensen  <achristensen@webkit.org>
 
         callAsyncJavaScript with an invalid parameter and no completionHandler should not crash
index 1e63a34..2eabd4c 100644 (file)
                573255A722139BC700396AE8 /* load-web-archive-2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 573255A322139B9000396AE8 /* load-web-archive-2.html */; };
                574217882400AC25002B303D /* web-authentication-make-credential-la-error.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 574217872400ABFD002B303D /* web-authentication-make-credential-la-error.html */; };
                5742178A2400AED8002B303D /* web-authentication-make-credential-la-duplicate-credential.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 574217892400AED0002B303D /* web-authentication-make-credential-la-duplicate-credential.html */; };
-               5742178C2400CD47002B303D /* web-authentication-get-assertion-la.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */; };
                5742178E2400D2DF002B303D /* web-authentication-make-credential-la.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5742178D2400D26C002B303D /* web-authentication-make-credential-la.html */; };
                574F55D2204D47F0002948C6 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 574F55D0204D471C002948C6 /* Security.framework */; };
                5758597F23A2527A00C74572 /* CtapPinTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5758597E23A2527A00C74572 /* CtapPinTest.cpp */; };
                                578DA44E23ECD28B00246010 /* web-authentication-get-assertion-hid-pin-invalid-error-retry.html in Copy Resources */,
                                570D26FC23C3F87000D5CF67 /* web-authentication-get-assertion-hid-pin.html in Copy Resources */,
                                57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */,
-                               5742178C2400CD47002B303D /* web-authentication-get-assertion-la.html in Copy Resources */,
                                579833922368FA37008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html in Copy Resources */,
                                57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
                                577454D22359BB01008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html in Copy Resources */,
                5735F0251F3A4EA6000EE801 /* TestWebKitAPI-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestWebKitAPI-iOS.entitlements"; sourceTree = "<group>"; };
                574217872400ABFD002B303D /* web-authentication-make-credential-la-error.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-la-error.html"; sourceTree = "<group>"; };
                574217892400AED0002B303D /* web-authentication-make-credential-la-duplicate-credential.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-la-duplicate-credential.html"; sourceTree = "<group>"; };
-               5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-get-assertion-la.html"; sourceTree = "<group>"; };
                5742178D2400D26C002B303D /* web-authentication-make-credential-la.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-la.html"; sourceTree = "<group>"; };
                5742178F2400D54D002B303D /* web-authentication-make-credential.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential.html"; sourceTree = "<group>"; };
                574F55D0204D471C002948C6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };
                                578DA44D23ECD26100246010 /* web-authentication-get-assertion-hid-pin-invalid-error-retry.html */,
                                570D26FB23C3F86500D5CF67 /* web-authentication-get-assertion-hid-pin.html */,
                                57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */,
-                               5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */,
                                5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */,
                                57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */,
                                577454D12359BAD5008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html */,
index f2fb452..c067420 100644 (file)
@@ -55,7 +55,7 @@ static bool webAuthenticationPanelUpdateLAError = false;
 static bool webAuthenticationPanelUpdateLAExcludeCredentialsMatched = false;
 static bool webAuthenticationPanelUpdateLANoCredential = false;
 static bool webAuthenticationPanelCancelImmediately = false;
-static bool webAuthenticationPanelVerifyUser = false;
+static _WKLocalAuthenticatorPolicy localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyDisallow;
 static String webAuthenticationPanelPin;
 static BOOL webAuthenticationPanelNullUserHandle = NO;
 static String testES256PrivateKeyBase64 =
@@ -151,11 +151,9 @@ static String testUserhandleBase64 = "AAECAwQFBgcICQ==";
     completionHandler(responses[index]);
 }
 
-- (void)panel:(_WKWebAuthenticationPanel *)panel verifyUserWithAccessControl:(SecAccessControlRef)accessControl completionHandler:(void (^)(LAContext *))completionHandler
+- (void)panel:(_WKWebAuthenticationPanel *)panel decidePolicyForLocalAuthenticatorWithCompletionHandler:(void (^)(_WKLocalAuthenticatorPolicy policy))completionHandler
 {
-    webAuthenticationPanelVerifyUser = true;
-    auto context = adoptNS([[LAContext alloc] init]);
-    completionHandler(context.get());
+    completionHandler(localAuthenticatorPolicy);
 }
 
 @end
@@ -300,24 +298,18 @@ static void reset()
     webAuthenticationPanelCancelImmediately = false;
     webAuthenticationPanelPin = emptyString();
     webAuthenticationPanelNullUserHandle = NO;
-    webAuthenticationPanelVerifyUser = false;
+    localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyDisallow;
 }
 
 static void checkPanel(_WKWebAuthenticationPanel *panel, NSString *relyingPartyID, NSArray *transports, _WKWebAuthenticationType type)
 {
     EXPECT_WK_STREQ(panel.relyingPartyID, relyingPartyID);
 
-    // Brute force given the maximum size of the array is 4.
-    auto *theTransports = panel.transports;
-    EXPECT_EQ(theTransports.count, transports.count);
+    EXPECT_EQ(panel.transports.count, transports.count);
     size_t count = 0;
     for (NSNumber *transport : transports) {
-        for (NSNumber *theTransport : theTransports) {
-            if (transport == theTransport) {
-                count++;
-                break;
-            }
-        }
+        if ([panel.transports containsObject:transport])
+            count++;
     }
     EXPECT_EQ(count, transports.count);
 
@@ -1259,11 +1251,10 @@ TEST(WebAuthenticationPanel, LAMakeCredentialNullDelegate)
     [webView setUIDelegate:delegate.get()];
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
-    [webView waitForMessage:@"Succeeded!"];
-    cleanUpKeychain("");
+    [webView waitForMessage:@"Disallow local authenticator."];
 }
 
-TEST(WebAuthenticationPanel, LAMakeCredential)
+TEST(WebAuthenticationPanel, LAMakeCredentialDisallowLocalAuthenticator)
 {
     reset();
     RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
@@ -1277,15 +1268,13 @@ TEST(WebAuthenticationPanel, LAMakeCredential)
     [webView setUIDelegate:delegate.get()];
 
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
-    Util::run(&webAuthenticationPanelVerifyUser);
-    checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeCreate);
-    [webView waitForMessage:@"Succeeded!"];
+    [webView waitForMessage:@"Disallow local authenticator."];
 }
 
-TEST(WebAuthenticationPanel, LAGetAssertion)
+TEST(WebAuthenticationPanel, LAMakeCredentialAllowLocalAuthenticator)
 {
     reset();
-    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
+    RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
 
     auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
     [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];
@@ -1295,11 +1284,10 @@ TEST(WebAuthenticationPanel, LAGetAssertion)
     auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
     [webView setUIDelegate:delegate.get()];
 
-    ASSERT_TRUE(addKeyToKeychain(testES256PrivateKeyBase64, "", testUserhandleBase64));
+    localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyAllow;
     [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
-    Util::run(&webAuthenticationPanelVerifyUser);
-    checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeGet);
     [webView waitForMessage:@"Succeeded!"];
+    checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeCreate);
     cleanUpKeychain("");
 }
 
diff --git a/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html b/Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html
deleted file mode 100644 (file)
index efa3c5b..0000000
+++ /dev/null
@@ -1,22 +0,0 @@
-<input type="text" id="input">
-<script>
-    if (window.internals) {
-        internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
-        internals.withUserGesture(() => { input.focus(); });
-    }
-
-    const options = {
-        publicKey: {
-            challenge: new Uint8Array(16),
-            timeout: 100
-        }
-    };
-
-    navigator.credentials.get(options).then(credential => {
-        // console.log("Succeeded!");
-        window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
-    }, error => {
-        // console.log(error.message);
-        window.webkit.messageHandlers.testHandler.postMessage(error.message);
-    });
-</script>