Source/WebCore/ChangeLog

 12020-03-03 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
 4 https://bugs.webkit.org/show_bug.cgi?id=208533
 5 <rdar://problem/60010184>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 Covered by new tests within existing test files.
 10
 11 * en.lproj/Localizable.strings:
 12 * platform/LocalizedStrings.cpp:
 13 (WebCore::touchIDPromptTitle):
 14 (WebCore::biometricFallbackPromptTitle):
 15 * platform/LocalizedStrings.h:
 16 Adds localized strings to support the customized LocalAuthentication dialog.
 17
1182020-03-02 Per Arne Vollan <pvollan@apple.com>
219
320 [Cocoa] Mapping from MIME type to UTI type should be done in the UI process

Source/WebKit/ChangeLog

 12020-03-03 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
 4 https://bugs.webkit.org/show_bug.cgi?id=208533
 5 <rdar://problem/60010184>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This patch implements the above SPI to replace -[_WKWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:].
 10 The original SPI is designed on the premise that Safari is going to highly customize the LocalAuthentication UI, and that is not happening
 11 anymore. Therefore, WebKit takes back the invocation of LocalAuthentication and offer a new SPI to tell clients when WebKit is about to
 12 show LocalAuthentication UI. Clients then have the trigger to pull at their pleasure.
 13
 14 This patch implements all plumbings to replace the SPI. Besides that, this patch also:
 15 1) enhances the LocalConnection::verifyUser with a slightly customized LocalAuthentication dialog;
 16 2) adds the SPI used above into the SPI header;
 17 3) makes _WKWebAuthenticationPanelDelegate.transports as a NSSet instead of a NSArray;
 18 4) lets LocalService::isAvailable return false if Apple attestation is not available.
 19
 20 * Platform/spi/Cocoa/LocalAuthenticationSPI.h:
 21 * UIProcess/API/APIWebAuthenticationPanelClient.h:
 22 (API::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
 23 (API::WebAuthenticationPanelClient::verifyUser const): Deleted.
 24 * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h:
 25 * UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm:
 26 (-[_WKWebAuthenticationPanel transports]):
 27 * UIProcess/WebAuthentication/Authenticator.h:
 28 * UIProcess/WebAuthentication/AuthenticatorManager.cpp:
 29 (WebKit::AuthenticatorManager::decidePolicyForLocalAuthenticator):
 30 (WebKit::AuthenticatorManager::verifyUser): Deleted.
 31 * UIProcess/WebAuthentication/AuthenticatorManager.h:
 32 * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h:
 33 * UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm:
 34 (WebKit::LocalAuthenticator::makeCredential):
 35 (WebKit::LocalAuthenticator::continueMakeCredentialAfterDecidePolicy):
 36 (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserVerification):
 37 (WebKit::LocalAuthenticator::continueMakeCredentialAfterAttested):
 38 (WebKit::LocalAuthenticator::getAssertion):
 39 (WebKit::LocalAuthenticator::continueGetAssertionAfterResponseSelected):
 40 (WebKit::LocalAuthenticator::continueGetAssertionAfterUserVerification):
 41 (WebKit::LocalAuthenticator::continueMakeCredentialAfterUserConsented): Deleted.
 42 (WebKit::LocalAuthenticator::continueGetAssertionAfterUserConsented): Deleted.
 43 * UIProcess/WebAuthentication/Cocoa/LocalConnection.h:
 44 * UIProcess/WebAuthentication/Cocoa/LocalConnection.mm:
 45 (WebKit::LocalConnection::verifyUser const):
 46 (WebKit::LocalConnection::isUnlocked const): Deleted.
 47 * UIProcess/WebAuthentication/Cocoa/LocalService.mm:
 48 (WebKit::LocalService::isAvailable):
 49 * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h:
 50 * UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm:
 51 (WebKit::WebAuthenticationPanelClient::WebAuthenticationPanelClient):
 52 (WebKit::localAuthenticatorPolicy):
 53 (WebKit::WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator const):
 54 (WebKit::WebAuthenticationPanelClient::verifyUser const): Deleted.
 55 * UIProcess/WebAuthentication/Mock/MockLocalConnection.h:
 56 * UIProcess/WebAuthentication/Mock/MockLocalConnection.mm:
 57 (WebKit::MockLocalConnection::verifyUser const):
 58 (WebKit::MockLocalConnection::isUnlocked const): Deleted.
 59 * UIProcess/WebAuthentication/WebAuthenticationFlags.h:
 60
1612020-03-02 Per Arne Vollan <pvollan@apple.com>
262
363 [Cocoa] Mapping from MIME type to UTI type should be done in the UI process

Source/WebCore/en.lproj/Localizable.strings

310310/* Video Enter Full Screen context menu item */
311311"Enter Full Screen" = "Enter Full Screen";
312312
 313/* Use passcode as a fallback to sign into this website */
 314"Enter passcode to sign into this website." = "Enter passcode to sign into this website.";
 315
313316/* menu item */
314317"Enter Picture in Picture" = "Enter Picture in Picture";
315318

877880/* prompt string in authentication panel */
878881"To view this page, you must log in to this area on %@:" = "To view this page, you must log in to this area on %@:";
879882
 883/* Use Touch ID to sign into this website */
 884"Touch ID to sign into this website." = "Touch ID to sign into this website.";
 885
880886/* Transformations context sub-menu item */
881887"Transformations" = "Transformations";
882888

Source/WebCore/platform/LocalizedStrings.cpp

@@String unacceptableTLSCertificate()
12071207}
12081208#endif
12091209
 1210#if ENABLE(WEB_AUTHN)
 1211String touchIDPromptTitle()
 1212{
 1213 return WEB_UI_STRING("Touch ID to sign into this website.", "Use Touch ID to sign into this website");
 1214}
 1215
 1216String biometricFallbackPromptTitle()
 1217{
 1218 return WEB_UI_STRING("Enter passcode to sign into this website.", "Use passcode as a fallback to sign into this website");
 1219}
 1220#endif
 1221
12101222} // namespace WebCore

Source/WebCore/platform/LocalizedStrings.h

@@namespace WebCore {
339339 WEBCORE_EXPORT String datePickerYearLabelTitle();
340340#endif
341341
 342#if ENABLE(WEB_AUTHN)
 343 WEBCORE_EXPORT String touchIDPromptTitle();
 344 WEBCORE_EXPORT String biometricFallbackPromptTitle();
 345#endif
 346
342347#if USE(GLIB) && defined(GETTEXT_PACKAGE)
343348#define WEB_UI_STRING(string, description) WebCore::localizedString(_(string))
344349#define WEB_UI_STRING_KEY(string, key, description) WebCore::localizedString(C_(key, string))

Source/WebKit/Platform/spi/Cocoa/LocalAuthenticationSPI.h

3434#else
3535
3636typedef NS_ENUM(NSInteger, LAOption) {
37  LAOptionNotInteractive,
 37 LAOptionAuthenticationTitle,
 38 LAOptionPasscodeTitle,
3839};
3940
4041@interface LAContext(Private) <NSSecureCoding>
4142
42 - (NSDictionary *)evaluatePolicy:(LAPolicy)policy options:(NSDictionary *)options error:(NSError **)error;
 43- (void)evaluateAccessControl:(SecAccessControlRef)accessControl operation:(LAAccessControlOperation)operation options:(NSDictionary *)options reply:(void(^)(NSDictionary *result, NSError *error))reply;
4344
4445@end
4546

Source/WebKit/UIProcess/API/APIWebAuthenticationPanelClient.h

2727
2828#if ENABLE(WEB_AUTHN)
2929
 30#include "WebAuthenticationFlags.h"
3031#include <wtf/CompletionHandler.h>
3132#include <wtf/HashSet.h>
3233#include <wtf/RefCounted.h>

@@namespace WebCore {
3940class AuthenticatorAssertionResponse;
4041}
4142
42 namespace WebKit {
43 enum class WebAuthenticationStatus : uint8_t;
44 enum class WebAuthenticationResult : bool;
45 }
46 
4743namespace API {
4844
4945class WebAuthenticationPanelClient {

@@public:
5551 virtual void dismissPanel(WebKit::WebAuthenticationResult) const { }
5652 virtual void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&& completionHandler) const { completionHandler(emptyString()); }
5753 virtual void selectAssertionResponse(Vector<Ref<WebCore::AuthenticatorAssertionResponse>>&& responses, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&& completionHandler) const { ASSERT(!responses.isEmpty()); completionHandler(responses[0]); }
58  virtual void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&& completionHandler) const { completionHandler(nullptr); }
 54 virtual void decidePolicyForLocalAuthenticator(CompletionHandler<void(WebKit::LocalAuthenticatorPolicy)>&& completionHandler) const { completionHandler(WebKit::LocalAuthenticatorPolicy::Disallow); }
5955};
6056
6157} // namespace API

Source/WebKit/UIProcess/API/C/WKPage.cpp

4747#include "APIPolicyClient.h"
4848#include "APISessionState.h"
4949#include "APIUIClient.h"
 50#include "APIWebAuthenticationPanel.h"
 51#include "APIWebAuthenticationPanelClient.h"
5052#include "APIWebsitePolicies.h"
5153#include "APIWindowFeatures.h"
5254#include "AuthenticationChallengeDisposition.h"

@@void WKPageSetPageUIClient(WKPageRef pageRef, const WKPageUIClientBase* wkClient
20792081
20802082#if ENABLE(WEB_AUTHN)
20812083 // The current method is specialized for WebKitTestRunner.
2082  void runWebAuthenticationPanel(WebPageProxy&, API::WebAuthenticationPanel&, WebFrameProxy&, FrameInfoData&&, CompletionHandler<void(WebKit::WebAuthenticationPanelResult)>&& completionHandler) final
 2084 void runWebAuthenticationPanel(WebPageProxy&, API::WebAuthenticationPanel& panel, WebFrameProxy&, FrameInfoData&&, CompletionHandler<void(WebKit::WebAuthenticationPanelResult)>&& completionHandler) final
20832085 {
 2086 class PanelClient : public API::WebAuthenticationPanelClient {
 2087 public:
 2088 void decidePolicyForLocalAuthenticator(CompletionHandler<void(WebKit::LocalAuthenticatorPolicy)>&& completionHandler) const { completionHandler(WebKit::LocalAuthenticatorPolicy::Allow); }
 2089 };
 2090
20842091 if (!m_client.runWebAuthenticationPanel) {
20852092 completionHandler(WebKit::WebAuthenticationPanelResult::Unavailable);
20862093 return;
20872094 }
 2095
 2096 panel.setClient(WTF::makeUniqueRef<PanelClient>());
20882097 completionHandler(WebKit::WebAuthenticationPanelResult::Presented);
20892098 }
20902099#endif

Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.h

@@typedef NS_ENUM(NSInteger, _WKWebAuthenticationType) {
6868 _WKWebAuthenticationTypeGet,
6969} WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
7070
 71typedef NS_ENUM(NSInteger, _WKLocalAuthenticatorPolicy) {
 72 _WKLocalAuthenticatorPolicyAllow,
 73 _WKLocalAuthenticatorPolicyDisallow,
 74} WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 75
7176@protocol _WKWebAuthenticationPanelDelegate <NSObject>
7277
7378@optional

@@typedef NS_ENUM(NSInteger, _WKWebAuthenticationType) {
7681- (void)panel:(_WKWebAuthenticationPanel *)panel dismissWebAuthenticationPanelWithResult:(_WKWebAuthenticationResult)result WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
7782- (void)panel:(_WKWebAuthenticationPanel *)panel requestPINWithRemainingRetries:(NSUInteger)retries completionHandler:(void (^)(NSString *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
7883- (void)panel:(_WKWebAuthenticationPanel *)panel selectAssertionResponse:(NSArray < _WKWebAuthenticationAssertionResponse *> *)responses completionHandler:(void (^)(_WKWebAuthenticationAssertionResponse *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
79 - (void)panel:(_WKWebAuthenticationPanel *)panel verifyUserWithAccessControl:(SecAccessControlRef)accessControl completionHandler:(void (^)(LAContext *))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
 84- (void)panel:(_WKWebAuthenticationPanel *)panel decidePolicyForLocalAuthenticatorWithCompletionHandler:(void (^)(_WKLocalAuthenticatorPolicy policy))completionHandler WK_API_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA));
8085
8186@end
8287

@@WK_CLASS_AVAILABLE(macos(WK_MAC_TBA), ios(WK_IOS_TBA))
8590
8691@property (nullable, nonatomic, weak) id <_WKWebAuthenticationPanelDelegate> delegate;
8792@property (nonatomic, readonly, copy) NSString *relyingPartyID;
88 @property (nonatomic, readonly, copy) NSArray *transports;
 93@property (nonatomic, readonly, copy) NSSet *transports;
8994@property (nonatomic, readonly) _WKWebAuthenticationType type;
9095
9196- (void)cancel;

Source/WebKit/UIProcess/API/Cocoa/_WKWebAuthenticationPanel.mm

3333@implementation _WKWebAuthenticationPanel {
3434#if ENABLE(WEB_AUTHN)
3535 WeakPtr<WebKit::WebAuthenticationPanelClient> _client;
36  RetainPtr<NSMutableArray> _transports;
 36 RetainPtr<NSMutableSet> _transports;
3737#endif
3838}
3939

@@static _WKWebAuthenticationTransport wkWebAuthenticationTransport(WebCore::Authe
8181 }
8282}
8383
84 - (NSArray *)transports
 84- (NSSet *)transports
8585{
8686 if (_transports)
8787 return _transports.get();
8888
8989 auto& transports = _panel->transports();
90  _transports = [[NSMutableArray alloc] initWithCapacity:transports.size()];
 90 _transports = [[NSMutableSet alloc] initWithCapacity:transports.size()];
9191 for (auto& transport : transports)
9292 [_transports addObject:adoptNS([[NSNumber alloc] initWithInt:wkWebAuthenticationTransport(transport)]).get()];
9393 return _transports.get();

Source/WebKit/UIProcess/WebAuthentication/Authenticator.h

@@public:
5656 virtual void authenticatorStatusUpdated(WebAuthenticationStatus) = 0;
5757 virtual void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) = 0;
5858 virtual void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) = 0;
59  virtual void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) = 0;
 59 virtual void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) = 0;
6060 };
6161
6262 virtual ~Authenticator() = default;

Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.cpp

@@void AuthenticatorManager::selectAssertionResponse(const HashSet<Ref<Authenticat
295295 });
296296}
297297
298 void AuthenticatorManager::verifyUser(SecAccessControlRef accessControlRef, CompletionHandler<void(LAContext *)>&& completionHandler)
 298void AuthenticatorManager::decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&& completionHandler)
299299{
300  RetainPtr<SecAccessControlRef> accessControl = accessControlRef;
301  dispatchPanelClientCall([accessControl = WTFMove(accessControl), completionHandler = WTFMove(completionHandler)] (const API::WebAuthenticationPanel& panel) mutable {
302  panel.client().verifyUser(accessControl.get(), WTFMove(completionHandler));
 300 dispatchPanelClientCall([completionHandler = WTFMove(completionHandler)] (const API::WebAuthenticationPanel& panel) mutable {
 301 panel.client().decidePolicyForLocalAuthenticator(WTFMove(completionHandler));
303302 });
304303}
305304

Source/WebKit/UIProcess/WebAuthentication/AuthenticatorManager.h

@@private:
8383 void authenticatorStatusUpdated(WebAuthenticationStatus) final;
8484 void requestPin(uint64_t retries, CompletionHandler<void(const WTF::String&)>&&) final;
8585 void selectAssertionResponse(const HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) final;
86  void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) final;
 86 void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) final;
8787
8888 // Overriden by MockAuthenticatorManager.
8989 virtual UniqueRef<AuthenticatorTransportService> createService(WebCore::AuthenticatorTransport, AuthenticatorTransportService::Observer&) const;

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.h

@@namespace WebKit {
3838class LocalAuthenticator final : public Authenticator {
3939public:
4040 // Here is the FSM.
41  // MakeCredential: Init => RequestReceived => UserConsented => Attested => End
42  // GetAssertion: Init => RequestReceived => ResponseSelected => UserConsented => End
 41 // MakeCredential: Init => RequestReceived => PolicyDecided => UserVerified => Attested => End
 42 // GetAssertion: Init => RequestReceived => ResponseSelected => UserVerified => End
4343 enum class State {
4444 Init,
4545 RequestReceived,
46  UserConsented,
 46 UserVerified,
4747 Attested,
48  ResponseSelected
 48 ResponseSelected,
 49 PolicyDecided,
4950 };
5051
5152 static Ref<LocalAuthenticator> create(UniqueRef<LocalConnection>&& connection)

@@private:
5758 explicit LocalAuthenticator(UniqueRef<LocalConnection>&&);
5859
5960 void makeCredential() final;
60  void continueMakeCredentialAfterUserConsented(SecAccessControlRef, LAContext *);
 61 void continueMakeCredentialAfterDecidePolicy(LocalAuthenticatorPolicy);
 62 void continueMakeCredentialAfterUserVerification(SecAccessControlRef, LocalConnection::UserVerification, LAContext *);
6163 void continueMakeCredentialAfterAttested(SecKeyRef, Vector<uint8_t>&& credentialId, Vector<uint8_t>&& authData, NSArray *certificates, NSError *);
6264
6365 void getAssertion() final;
6466 void continueGetAssertionAfterResponseSelected(Ref<WebCore::AuthenticatorAssertionResponse>&&);
65  void continueGetAssertionAfterUserConsented(LAContext *, Ref<WebCore::AuthenticatorAssertionResponse>&&);
 67 void continueGetAssertionAfterUserVerification(Ref<WebCore::AuthenticatorAssertionResponse>&&, LocalConnection::UserVerification, LAContext *);
6668
6769 void receiveException(WebCore::ExceptionData&&, WebAuthenticationStatus = WebAuthenticationStatus::LAError) const;
6870

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalAuthenticator.mm

@@void LocalAuthenticator::makeCredential()
160160
161161 // Step 6.
162162 // Get user consent.
 163 if (auto* observer = this->observer()) {
 164 auto callback = [weakThis = makeWeakPtr(*this)] (LocalAuthenticatorPolicy policy) {
 165 ASSERT(RunLoop::isMain());
 166 if (!weakThis)
 167 return;
 168
 169 weakThis->continueMakeCredentialAfterDecidePolicy(policy);
 170 };
 171 observer->decidePolicyForLocalAuthenticator(WTFMove(callback));
 172 }
 173}
 174
 175void LocalAuthenticator::continueMakeCredentialAfterDecidePolicy(LocalAuthenticatorPolicy policy)
 176{
 177 ASSERT(m_state == State::RequestReceived);
 178 m_state = State::PolicyDecided;
 179
 180 if (policy == LocalAuthenticatorPolicy::Disallow) {
 181 receiveRespond(ExceptionData { UnknownError, "Disallow local authenticator."_s });
 182 return;
 183 }
 184
163185 RetainPtr<SecAccessControlRef> accessControl;
164186 {
165187 CFErrorRef errorRef = nullptr;

@@void LocalAuthenticator::makeCredential()
171193 }
172194 }
173195
174  if (auto* observer = this->observer()) {
175  SecAccessControlRef accessControlRef = accessControl.get();
176  auto callback = [accessControl = WTFMove(accessControl), weakThis = makeWeakPtr(*this)] (LAContext *context) {
177  ASSERT(RunLoop::isMain());
178  if (!weakThis)
179  return;
 196 SecAccessControlRef accessControlRef = accessControl.get();
 197 auto callback = [accessControl = WTFMove(accessControl), weakThis = makeWeakPtr(*this)] (LocalConnection::UserVerification verification, LAContext *context) {
 198 ASSERT(RunLoop::isMain());
 199 if (!weakThis)
 200 return;
180201
181  weakThis->continueMakeCredentialAfterUserConsented(accessControl.get(), context);
182  };
183  observer->verifyUser(accessControlRef, WTFMove(callback));
184  }
 202 weakThis->continueMakeCredentialAfterUserVerification(accessControl.get(), verification, context);
 203 };
 204 m_connection->verifyUser(accessControlRef, WTFMove(callback));
185205}
186206
187 void LocalAuthenticator::continueMakeCredentialAfterUserConsented(SecAccessControlRef accessControlRef, LAContext *context)
 207void LocalAuthenticator::continueMakeCredentialAfterUserVerification(SecAccessControlRef accessControlRef, LocalConnection::UserVerification verification, LAContext *context)
188208{
189209 using namespace LocalAuthenticatorInternal;
190210
191  ASSERT(m_state == State::RequestReceived);
192  m_state = State::UserConsented;
 211 ASSERT(m_state == State::PolicyDecided);
 212 m_state = State::UserVerified;
193213 auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
194214
195  if (!m_connection->isUnlocked(context)) {
196  receiveRespond(ExceptionData { NotAllowedError, "Couldn't get user consent."_s });
 215 if (verification == LocalConnection::UserVerification::No) {
 216 receiveException({ NotAllowedError, "Couldn't verify user."_s });
197217 return;
198218 }
199219

@@void LocalAuthenticator::continueMakeCredentialAfterAttested(SecKeyRef privateKe
309329{
310330 using namespace LocalAuthenticatorInternal;
311331
312  ASSERT(m_state == State::UserConsented);
 332 ASSERT(m_state == State::UserVerified);
313333 m_state = State::Attested;
314334 auto& creationOptions = WTF::get<PublicKeyCredentialCreationOptions>(requestData().options);
315335

@@void LocalAuthenticator::getAssertion()
392412 return;
393413 }
394414
395  // Step 6.
 415 // Step 6-7. User consent is implicitly acquired by selecting responses.
396416 for (NSDictionary *attribute : intersectedCredentialsAttributes) {
397417 auto addResult = m_assertionResponses.add(AuthenticatorAssertionResponse::create(
398418 toArrayBuffer(attribute[(id)kSecAttrApplicationLabel]),

@@void LocalAuthenticator::continueGetAssertionAfterResponseSelected(Ref<WebCore::
422442 ASSERT(m_state == State::RequestReceived);
423443 m_state = State::ResponseSelected;
424444
425  // Step 7. Get user consent.
426  if (auto* observer = this->observer()) {
427  auto accessControlRef = response->accessControl();
428  auto callback = [weakThis = makeWeakPtr(*this), response = WTFMove(response)] (LAContext *context) mutable {
429  ASSERT(RunLoop::isMain());
430  if (!weakThis)
431  return;
 445 auto accessControlRef = response->accessControl();
 446 auto callback = [
 447 weakThis = makeWeakPtr(*this),
 448 response = WTFMove(response)
 449 ] (LocalConnection::UserVerification verification, LAContext *context) mutable {
 450 ASSERT(RunLoop::isMain());
 451 if (!weakThis)
 452 return;
432453
433  weakThis->continueGetAssertionAfterUserConsented(context, WTFMove(response));
434  };
435  observer->verifyUser(accessControlRef, WTFMove(callback));
436  }
 454 weakThis->continueGetAssertionAfterUserVerification(WTFMove(response), verification, context);
 455 };
 456 m_connection->verifyUser(accessControlRef, WTFMove(callback));
437457}
438458
439 void LocalAuthenticator::continueGetAssertionAfterUserConsented(LAContext *context, Ref<WebCore::AuthenticatorAssertionResponse>&& response)
 459void LocalAuthenticator::continueGetAssertionAfterUserVerification(Ref<WebCore::AuthenticatorAssertionResponse>&& response, LocalConnection::UserVerification verification, LAContext *context)
440460{
441461 using namespace LocalAuthenticatorInternal;
442462 ASSERT(m_state == State::ResponseSelected);
443  m_state = State::UserConsented;
 463 m_state = State::UserVerified;
444464
445  if (!m_connection->isUnlocked(context)) {
446  receiveRespond(ExceptionData { NotAllowedError, "Couldn't get user consent."_s });
 465 if (verification == LocalConnection::UserVerification::No) {
 466 receiveException({ NotAllowedError, "Couldn't verify user."_s });
447467 return;
448468 }
449469

@@void LocalAuthenticator::continueGetAssertionAfterUserConsented(LAContext *conte
456476 // Step 11.
457477 RetainPtr<CFDataRef> signature;
458478 {
459  auto query = adoptNS([[NSMutableDictionary alloc] init]);
460  [query addEntriesFromDictionary:@{
 479 NSDictionary *query = @{
461480 (id)kSecClass: (id)kSecClassKey,
462481 (id)kSecAttrKeyClass: (id)kSecAttrKeyClassPrivate,
463482 (id)kSecAttrApplicationLabel: toNSData(response->rawId()).get(),
 483 (id)kSecUseAuthenticationContext: context,
464484 (id)kSecReturnRef: @YES,
465485#if HAVE(DATA_PROTECTION_KEYCHAIN)
466486 (id)kSecUseDataProtectionKeychain: @YES
467487#else
468488 (id)kSecAttrNoLegacy: @YES
469489#endif
470  }];
471  // context is nullptr in mock testing.
472  if (context)
473  [query setObject:context forKey:(id)kSecUseAuthenticationContext];
 490 };
474491 CFTypeRef privateKeyRef = nullptr;
475  OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query.get(), &privateKeyRef);
 492 OSStatus status = SecItemCopyMatching((__bridge CFDictionaryRef)query, &privateKeyRef);
476493 if (status) {
477494 receiveException({ UnknownError, makeString("Couldn't get the private key reference: ", status) });
478495 return;

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.h

3636OBJC_CLASS LAContext;
3737
3838namespace WebCore {
39 
4039class AuthenticatorAssertionResponse;
41 
4240}
4341
4442namespace WebKit {

@@class LocalConnection {
5250 WTF_MAKE_FAST_ALLOCATED;
5351 WTF_MAKE_NONCOPYABLE(LocalConnection);
5452public:
 53 enum class UserVerification : bool {
 54 No,
 55 Yes
 56 };
 57
5558 using AttestationCallback = CompletionHandler<void(NSArray *, NSError *)>;
 59 using UserVerificationCallback = CompletionHandler<void(UserVerification, LAContext *)>;
5660
5761 LocalConnection() = default;
5862 virtual ~LocalConnection() = default;
5963
6064 // Overrided by MockLocalConnection.
61  virtual bool isUnlocked(LAContext *) const;
 65 virtual void verifyUser(SecAccessControlRef, UserVerificationCallback&&) const;
6266 virtual RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const;
6367 virtual void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const;
6468 virtual void filterResponses(HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&) const { };

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalConnection.mm

2828
2929#if ENABLE(WEB_AUTHN)
3030
 31#import <WebCore/LocalizedStrings.h>
3132#import <wtf/BlockPtr.h>
3233#import <wtf/RunLoop.h>
3334

3940
4041namespace WebKit {
4142
42 bool LocalConnection::isUnlocked(LAContext *context) const
 43void LocalConnection::verifyUser(SecAccessControlRef accessControl, UserVerificationCallback&& completionHandler) const
4344{
44  NSError *error = nil;
45  auto result = [context evaluatePolicy:LAPolicyDeviceOwnerAuthenticationWithBiometrics options:@{ @(LAOptionNotInteractive): @YES } error:&error];
46  if (!result)
47  LOG_ERROR("Couldn't get user consent: %@", error);
48  return !!result;
 45 auto context = adoptNS([allocLAContextInstance() init]);
 46
 47 auto options = adoptNS([[NSMutableDictionary alloc] init]);
 48 if ([context biometryType] == LABiometryTypeTouchID)
 49 [options setObject:WebCore::touchIDPromptTitle() forKey:@(LAOptionAuthenticationTitle)];
 50 [options setObject:WebCore::biometricFallbackPromptTitle() forKey:@(LAOptionPasscodeTitle)];
 51
 52 auto reply = makeBlockPtr([context, completionHandler = WTFMove(completionHandler)] (NSDictionary *, NSError *error) mutable {
 53 ASSERT(!RunLoop::isMain());
 54
 55 UserVerification verification = UserVerification::Yes;
 56 if (error) {
 57 LOG_ERROR("Couldn't authenticate with biometrics: %@", error);
 58 verification = UserVerification::No;
 59 }
 60 RunLoop::main().dispatch([completionHandler = WTFMove(completionHandler), verification, context = WTFMove(context)] () mutable {
 61 completionHandler(verification, context.get());
 62 });
 63 });
 64
 65 [context evaluateAccessControl:accessControl operation:LAAccessControlOperationUseKeySign options:options.get() reply:reply.get()];
4966}
5067
5168RetainPtr<SecKeyRef> LocalConnection::createCredentialPrivateKey(LAContext *context, SecAccessControlRef accessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const

Source/WebKit/UIProcess/WebAuthentication/Cocoa/LocalService.mm

@@bool LocalService::isAvailable()
5555
5656#if defined(LOCALSERVICE_ADDITIONS)
5757LOCALSERVICE_ADDITIONS
 58#else
 59 return false;
5860#endif
5961
6062 return true;

Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.h

@@private:
5050 void dismissPanel(WebAuthenticationResult) const final;
5151 void requestPin(uint64_t, CompletionHandler<void(const WTF::String&)>&&) const final;
5252 void selectAssertionResponse(Vector<Ref<WebCore::AuthenticatorAssertionResponse>>&&, CompletionHandler<void(const WebCore::AuthenticatorAssertionResponse&)>&&) const final;
53  void verifyUser(SecAccessControlRef, CompletionHandler<void(LAContext *)>&&) const final;
 53 void decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&&) const final;
5454
5555 _WKWebAuthenticationPanel *m_panel;
5656 WeakObjCPtr<id <_WKWebAuthenticationPanelDelegate> > m_delegate;

@@private:
6060 bool panelDismissWebAuthenticationPanelWithResult : 1;
6161 bool panelRequestPinWithRemainingRetriesCompletionHandler : 1;
6262 bool panelSelectAssertionResponseCompletionHandler : 1;
63  bool panelVerifyUserWithAccessControlCompletionHandler : 1;
 63 bool panelDecidePolicyForLocalAuthenticatorCompletionHandler : 1;
6464 } m_delegateMethods;
6565};
6666

Source/WebKit/UIProcess/WebAuthentication/Cocoa/WebAuthenticationPanelClient.mm

3232#import "APIWebAuthenticationAssertionResponse.h"
3333#import "CompletionHandlerCallChecker.h"
3434#import "WKNSArray.h"
35 #import "WebAuthenticationFlags.h"
3635#import "_WKWebAuthenticationAssertionResponseInternal.h"
3736#import "_WKWebAuthenticationPanel.h"
3837#import <wtf/BlockPtr.h>

@@WebAuthenticationPanelClient::WebAuthenticationPanelClient(_WKWebAuthenticationP
5049 m_delegateMethods.panelDismissWebAuthenticationPanelWithResult = [delegate respondsToSelector:@selector(panel:dismissWebAuthenticationPanelWithResult:)];
5150 m_delegateMethods.panelRequestPinWithRemainingRetriesCompletionHandler = [delegate respondsToSelector:@selector(panel:requestPINWithRemainingRetries:completionHandler:)];
5251 m_delegateMethods.panelSelectAssertionResponseCompletionHandler = [delegate respondsToSelector:@selector(panel:selectAssertionResponse:completionHandler:)];
53  m_delegateMethods.panelVerifyUserWithAccessControlCompletionHandler = [delegate respondsToSelector:@selector(panel:verifyUserWithAccessControl:completionHandler:)];
 52 m_delegateMethods.panelDecidePolicyForLocalAuthenticatorCompletionHandler = [delegate respondsToSelector:@selector(panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:)];
5453}
5554
5655RetainPtr<id <_WKWebAuthenticationPanelDelegate> > WebAuthenticationPanelClient::delegate()

@@void WebAuthenticationPanelClient::selectAssertionResponse(Vector<Ref<WebCore::A
167166 }).get()];
168167}
169168
170 void WebAuthenticationPanelClient::verifyUser(SecAccessControlRef accessControl, CompletionHandler<void(LAContext *)>&& completionHandler) const
 169static LocalAuthenticatorPolicy localAuthenticatorPolicy(_WKLocalAuthenticatorPolicy result)
171170{
172  if (!m_delegateMethods.panelVerifyUserWithAccessControlCompletionHandler) {
173  completionHandler(adoptNS([allocLAContextInstance() init]).get());
 171 switch (result) {
 172 case _WKLocalAuthenticatorPolicyAllow:
 173 return LocalAuthenticatorPolicy::Allow;
 174 case _WKLocalAuthenticatorPolicyDisallow:
 175 return LocalAuthenticatorPolicy::Disallow;
 176 }
 177 ASSERT_NOT_REACHED();
 178 return LocalAuthenticatorPolicy::Allow;
 179}
 180
 181void WebAuthenticationPanelClient::decidePolicyForLocalAuthenticator(CompletionHandler<void(LocalAuthenticatorPolicy)>&& completionHandler) const
 182{
 183 if (!m_delegateMethods.panelDecidePolicyForLocalAuthenticatorCompletionHandler) {
 184 completionHandler(LocalAuthenticatorPolicy::Disallow);
174185 return;
175186 }
176187
177188 auto delegate = m_delegate.get();
178189 if (!delegate) {
179  completionHandler(adoptNS([allocLAContextInstance() init]).get());
 190 completionHandler(LocalAuthenticatorPolicy::Disallow);
180191 return;
181192 }
182193
183  auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(panel:verifyUserWithAccessControl:completionHandler:));
184  [delegate panel:m_panel verifyUserWithAccessControl:accessControl completionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](LAContext *context) mutable {
 194 auto checker = CompletionHandlerCallChecker::create(delegate.get(), @selector(panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:));
 195 [delegate panel:m_panel decidePolicyForLocalAuthenticatorWithCompletionHandler:makeBlockPtr([completionHandler = WTFMove(completionHandler), checker = WTFMove(checker)](_WKLocalAuthenticatorPolicy policy) mutable {
185196 if (checker->completionHandlerHasBeenCalled())
186197 return;
187198 checker->didCallCompletionHandler();
188  completionHandler(context);
 199 completionHandler(localAuthenticatorPolicy(policy));
189200 }).get()];
190201}
191202

Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.h

@@public:
3737 explicit MockLocalConnection(const WebCore::MockWebAuthenticationConfiguration&);
3838
3939private:
40  bool isUnlocked(LAContext *) const final;
 40 void verifyUser(SecAccessControlRef, UserVerificationCallback&&) const final;
4141 RetainPtr<SecKeyRef> createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const final;
4242 void getAttestation(SecKeyRef, NSData *authData, NSData *hash, AttestationCallback&&) const final;
4343 void filterResponses(HashSet<Ref<WebCore::AuthenticatorAssertionResponse>>&) const final;

Source/WebKit/UIProcess/WebAuthentication/Mock/MockLocalConnection.mm

@@MockLocalConnection::MockLocalConnection(const WebCore::MockWebAuthenticationCon
4444{
4545}
4646
47 bool MockLocalConnection::isUnlocked(LAContext *context) const
 47void MockLocalConnection::verifyUser(SecAccessControlRef, UserVerificationCallback&& callback) const
4848{
49  return m_configuration.local->acceptAuthentication;
 49 // Mock async operations.
 50 RunLoop::main().dispatch([configuration = m_configuration, callback = WTFMove(callback)]() mutable {
 51 ASSERT(configuration.local);
 52 if (!configuration.local->acceptAuthentication) {
 53 callback(UserVerification::No, nil);
 54 return;
 55 }
 56 callback(UserVerification::Yes, adoptNS([allocLAContextInstance() init]).get());
 57 });
5058}
5159
5260RetainPtr<SecKeyRef> MockLocalConnection::createCredentialPrivateKey(LAContext *, SecAccessControlRef, const String& secAttrLabel, NSData *secAttrApplicationTag) const

Source/WebKit/UIProcess/WebAuthentication/WebAuthenticationFlags.h

@@enum class WebAuthenticationStatus : uint8_t {
5151 LANoCredential,
5252};
5353
 54enum class LocalAuthenticatorPolicy : bool {
 55 Allow,
 56 Disallow
 57};
 58
5459} // namespace WebKit
5560
5661#endif // ENABLE(WEB_AUTHN)

Tools/ChangeLog

 12020-03-03 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Implement -[_WKWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:] SPI
 4 https://bugs.webkit.org/show_bug.cgi?id=208533
 5 <rdar://problem/60010184>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 * TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj:
 10 * TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm:
 11 (-[TestWebAuthenticationPanelDelegate panel:decidePolicyForLocalAuthenticatorWithCompletionHandler:]):
 12 (TestWebKitAPI::TEST):
 13 (-[TestWebAuthenticationPanelDelegate panel:verifyUserWithAccessControl:completionHandler:]): Deleted.
 14 * TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html: Removed.
 15
1162020-03-02 Per Arne Vollan <pvollan@apple.com>
217
318 [Cocoa] Mapping from MIME type to UTI type should be done in the UI process

Tools/TestWebKitAPI/TestWebKitAPI.xcodeproj/project.pbxproj

350350 573255A722139BC700396AE8 /* load-web-archive-2.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 573255A322139B9000396AE8 /* load-web-archive-2.html */; };
351351 574217882400AC25002B303D /* web-authentication-make-credential-la-error.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 574217872400ABFD002B303D /* web-authentication-make-credential-la-error.html */; };
352352 5742178A2400AED8002B303D /* web-authentication-make-credential-la-duplicate-credential.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 574217892400AED0002B303D /* web-authentication-make-credential-la-duplicate-credential.html */; };
353  5742178C2400CD47002B303D /* web-authentication-get-assertion-la.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */; };
354353 5742178E2400D2DF002B303D /* web-authentication-make-credential-la.html in Copy Resources */ = {isa = PBXBuildFile; fileRef = 5742178D2400D26C002B303D /* web-authentication-make-credential-la.html */; };
355354 574F55D2204D47F0002948C6 /* Security.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 574F55D0204D471C002948C6 /* Security.framework */; };
356355 5758597F23A2527A00C74572 /* CtapPinTest.cpp in Sources */ = {isa = PBXBuildFile; fileRef = 5758597E23A2527A00C74572 /* CtapPinTest.cpp */; };

15241523 578DA44E23ECD28B00246010 /* web-authentication-get-assertion-hid-pin-invalid-error-retry.html in Copy Resources */,
15251524 570D26FC23C3F87000D5CF67 /* web-authentication-get-assertion-hid-pin.html in Copy Resources */,
15261525 57663DEC234F1F9300E85E09 /* web-authentication-get-assertion-hid.html in Copy Resources */,
1527  5742178C2400CD47002B303D /* web-authentication-get-assertion-la.html in Copy Resources */,
15281526 579833922368FA37008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html in Copy Resources */,
15291527 57663DEA234EA66D00E85E09 /* web-authentication-get-assertion-nfc.html in Copy Resources */,
15301528 577454D22359BB01008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html in Copy Resources */,

19741972 5735F0251F3A4EA6000EE801 /* TestWebKitAPI-iOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "TestWebKitAPI-iOS.entitlements"; sourceTree = "<group>"; };
19751973 574217872400ABFD002B303D /* web-authentication-make-credential-la-error.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-la-error.html"; sourceTree = "<group>"; };
19761974 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>"; };
1977  5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-get-assertion-la.html"; sourceTree = "<group>"; };
19781975 5742178D2400D26C002B303D /* web-authentication-make-credential-la.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential-la.html"; sourceTree = "<group>"; };
19791976 5742178F2400D54D002B303D /* web-authentication-make-credential.html */ = {isa = PBXFileReference; lastKnownFileType = text.html; path = "web-authentication-make-credential.html"; sourceTree = "<group>"; };
19801977 574F55D0204D471C002948C6 /* Security.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Security.framework; path = System/Library/Frameworks/Security.framework; sourceTree = SDKROOT; };

36033600 578DA44D23ECD26100246010 /* web-authentication-get-assertion-hid-pin-invalid-error-retry.html */,
36043601 570D26FB23C3F86500D5CF67 /* web-authentication-get-assertion-hid-pin.html */,
36053602 57663DEB234F1F8000E85E09 /* web-authentication-get-assertion-hid.html */,
3606  5742178B2400CD2D002B303D /* web-authentication-get-assertion-la.html */,
36073603 5798337B235EB65C008E5547 /* web-authentication-get-assertion-nfc-multiple-tags.html */,
36083604 57663DE9234EA60B00E85E09 /* web-authentication-get-assertion-nfc.html */,
36093605 577454D12359BAD5008E1ED7 /* web-authentication-get-assertion-u2f-no-credentials.html */,

Tools/TestWebKitAPI/Tests/WebKitCocoa/_WKWebAuthenticationPanel.mm

@@static bool webAuthenticationPanelUpdateLAError = false;
5555static bool webAuthenticationPanelUpdateLAExcludeCredentialsMatched = false;
5656static bool webAuthenticationPanelUpdateLANoCredential = false;
5757static bool webAuthenticationPanelCancelImmediately = false;
58 static bool webAuthenticationPanelVerifyUser = false;
 58static _WKLocalAuthenticatorPolicy localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyDisallow;
5959static String webAuthenticationPanelPin;
6060static BOOL webAuthenticationPanelNullUserHandle = NO;
6161static String testES256PrivateKeyBase64 =

@@- (void)panel:(_WKWebAuthenticationPanel *)panel selectAssertionResponse:(NSArra
151151 completionHandler(responses[index]);
152152}
153153
154 - (void)panel:(_WKWebAuthenticationPanel *)panel verifyUserWithAccessControl:(SecAccessControlRef)accessControl completionHandler:(void (^)(LAContext *))completionHandler
 154- (void)panel:(_WKWebAuthenticationPanel *)panel decidePolicyForLocalAuthenticatorWithCompletionHandler:(void (^)(_WKLocalAuthenticatorPolicy policy))completionHandler
155155{
156  webAuthenticationPanelVerifyUser = true;
157  auto context = adoptNS([[LAContext alloc] init]);
158  completionHandler(context.get());
 156 completionHandler(localAuthenticatorPolicy);
159157}
160158
161159@end

@@static void reset()
300298 webAuthenticationPanelCancelImmediately = false;
301299 webAuthenticationPanelPin = emptyString();
302300 webAuthenticationPanelNullUserHandle = NO;
303  webAuthenticationPanelVerifyUser = false;
 301 localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyDisallow;
304302}
305303
306304static void checkPanel(_WKWebAuthenticationPanel *panel, NSString *relyingPartyID, NSArray *transports, _WKWebAuthenticationType type)
307305{
308306 EXPECT_WK_STREQ(panel.relyingPartyID, relyingPartyID);
309307
310  // Brute force given the maximum size of the array is 4.
311  auto *theTransports = panel.transports;
312  EXPECT_EQ(theTransports.count, transports.count);
 308 EXPECT_EQ(panel.transports.count, transports.count);
313309 size_t count = 0;
314310 for (NSNumber *transport : transports) {
315  for (NSNumber *theTransport : theTransports) {
316  if (transport == theTransport) {
317  count++;
318  break;
319  }
320  }
 311 if ([panel.transports containsObject:transport])
 312 count++;
321313 }
322314 EXPECT_EQ(count, transports.count);
323315

@@TEST(WebAuthenticationPanel, LAMakeCredentialNullDelegate)
12591251 [webView setUIDelegate:delegate.get()];
12601252
12611253 [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
1262  [webView waitForMessage:@"Succeeded!"];
1263  cleanUpKeychain("");
 1254 [webView waitForMessage:@"Disallow local authenticator."];
12641255}
12651256
1266 TEST(WebAuthenticationPanel, LAMakeCredential)
 1257TEST(WebAuthenticationPanel, LAMakeCredentialDisallowLocalAuthenticator)
12671258{
12681259 reset();
12691260 RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];

@@TEST(WebAuthenticationPanel, LAMakeCredential)
12771268 [webView setUIDelegate:delegate.get()];
12781269
12791270 [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
1280  Util::run(&webAuthenticationPanelVerifyUser);
1281  checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeCreate);
1282  [webView waitForMessage:@"Succeeded!"];
 1271 [webView waitForMessage:@"Disallow local authenticator."];
12831272}
12841273
1285 TEST(WebAuthenticationPanel, LAGetAssertion)
 1274TEST(WebAuthenticationPanel, LAMakeCredentialAllowLocalAuthenticator)
12861275{
12871276 reset();
1288  RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-get-assertion-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
 1277 RetainPtr<NSURL> testURL = [[NSBundle mainBundle] URLForResource:@"web-authentication-make-credential-la" withExtension:@"html" subdirectory:@"TestWebKitAPI.resources"];
12891278
12901279 auto *configuration = [WKWebViewConfiguration _test_configurationWithTestPlugInClassName:@"WebProcessPlugInWithInternals" configureJSCForTesting:YES];
12911280 [[configuration preferences] _setEnabled:YES forExperimentalFeature:webAuthenticationExperimentalFeature()];

@@TEST(WebAuthenticationPanel, LAGetAssertion)
12951284 auto delegate = adoptNS([[TestWebAuthenticationPanelUIDelegate alloc] init]);
12961285 [webView setUIDelegate:delegate.get()];
12971286
1298  ASSERT_TRUE(addKeyToKeychain(testES256PrivateKeyBase64, "", testUserhandleBase64));
 1287 localAuthenticatorPolicy = _WKLocalAuthenticatorPolicyAllow;
12991288 [webView loadRequest:[NSURLRequest requestWithURL:testURL.get()]];
1300  Util::run(&webAuthenticationPanelVerifyUser);
1301  checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeGet);
13021289 [webView waitForMessage:@"Succeeded!"];
 1290 checkPanel([delegate panel], @"", @[adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportUSB]).get(), adoptNS([[NSNumber alloc] initWithInt:_WKWebAuthenticationTransportInternal]).get()], _WKWebAuthenticationTypeCreate);
13031291 cleanUpKeychain("");
13041292}
13051293

Tools/TestWebKitAPI/Tests/WebKitCocoa/web-authentication-get-assertion-la.html

1 <input type="text" id="input">
2 <script>
3  if (window.internals) {
4  internals.setMockWebAuthenticationConfiguration({ local: { acceptAuthentication: true, acceptAttestation: false } });
5  internals.withUserGesture(() => { input.focus(); });
6  }
7 
8  const options = {
9  publicKey: {
10  challenge: new Uint8Array(16),
11  timeout: 100
12  }
13  };
14 
15  navigator.credentials.get(options).then(credential => {
16  // console.log("Succeeded!");
17  window.webkit.messageHandlers.testHandler.postMessage("Succeeded!");
18  }, error => {
19  // console.log(error.message);
20  window.webkit.messageHandlers.testHandler.postMessage(error.message);
21  });
22 </script>

LayoutTests/http/wpt/webauthn/public-key-credential-create-failure-local.https.html

9898 pubKeyCredParams: [{ type: "public-key", alg: -7 }]
9999 }
100100 };
101  return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't get user consent.");
 101 return promiseRejects(t, "NotAllowedError", navigator.credentials.create(options), "Couldn't verify user.");
102102 }, "PublicKeyCredential's [[create]] without user consent in a mock local authenticator.");
103103
104104 promise_test(t => {

LayoutTests/http/wpt/webauthn/public-key-credential-get-failure-local.https.html

5555
5656 if (window.testRunner)
5757 testRunner.addTestKeyToKeychain(privateKeyBase64, testRpId, userhandleBase64);
58  return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't get user consent.").then(() => {
 58 return promiseRejects(t, "NotAllowedError", navigator.credentials.get(options), "Couldn't verify user.").then(() => {
5959 if (window.testRunner)
6060 testRunner.cleanUpKeychain(testRpId, userhandleBase64);
6161 });