Source/WebKit/ChangeLog

 12021-01-14 Jiewen Tan <jiewen_tan@apple.com>
 2
 3 [WebAuthn] Polish the new WebAuthn UI
 4 https://bugs.webkit.org/show_bug.cgi?id=220617
 5 <rdar://problem/73185470>
 6
 7 Reviewed by NOBODY (OOPS!).
 8
 9 This patch does the following few things:
 10 1. It updates the way how the PIN error for security keys is handled.
 11 2. It uses the credential name to identify a credential that passed to the UI instead of the login choice object
 12 as it turns out that the UI won't return the same object at all.
 13 3. It delays to show the UI if the platform authenticator is involved given the platform authenticator might not contain
 14 the requested credentials. If not, we should either show an error or just requesting the security key ones.
 15
 16 Covered by manual tests.
 17
 18 * Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h:
 19 (NS_ERROR_ENUM):
 20 * UIProcess/WebAuthentication/Cocoa/AuthenticatorPresenterCoordinator.h:
 21 * UIProcess/WebAuthentication/Cocoa/AuthenticatorPresenterCoordinator.mm:
 22 (WebKit::AuthenticatorPresenterCoordinator::AuthenticatorPresenterCoordinator):
 23 (WebKit::AuthenticatorPresenterCoordinator::updatePresenter):
 24 (WebKit::AuthenticatorPresenterCoordinator::selectAssertionResponse):
 25 (WebKit::AuthenticatorPresenterCoordinator::didSelectAssertionResponse):
 26 * UIProcess/WebAuthentication/Cocoa/WKASCAuthorizationPresenterDelegate.mm:
 27 (-[WKASCAuthorizationPresenterDelegate authorizationPresenter:credentialRequestedForLoginChoice:authenticatedContext:completionHandler:]):
 28
1292021-01-12 BJ Burg <bburg@apple.com>
230
331 [Cocoa] Web Inspector: move browser domain activation methods back to WKWebView and UIDelegate

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

@@typedef NS_ERROR_ENUM(ASCAuthorizationErrorDomain, ASCAuthorizationError) {
172172 ASCAuthorizationErrorNoCredentialsFound,
173173 ASCAuthorizationErrorLAError,
174174 ASCAuthorizationErrorLAExcludeCredentialsMatched,
175 };
176 
177 extern NSString * const ASCPINValidationResultKey;
178 
179 typedef NS_ENUM(NSInteger, ASCPINValidationResult) {
180  ASCPINValidationResultPINBlocked,
181  ASCPINValidationResultPINAuthBlocked,
182  ASCPINValidationResultPINInvalid,
 175 ASCAuthorizationErrorPINInvalid,
 176 ASCAuthorizationErrorAuthenticatorTemporarilyLocked,
 177 ASCAuthorizationErrorAuthenticatorPermanentlyLocked,
183178};
184179
185180NS_ASSUME_NONNULL_END

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

@@public:
6565
6666 void setCredentialRequestHandler(CredentialRequestHandler&& handler) { m_credentialRequestHandler = WTFMove(handler); }
6767 void setLAContext(LAContext *);
68  void didSelectAssertionResponse(ASCLoginChoiceProtocol *, LAContext *);
 68 void didSelectAssertionResponse(const String& credentialName, LAContext *);
6969 void setPin(const String&);
7070
7171private:

@@private:
7373 RetainPtr<ASCAuthorizationPresentationContext> m_context;
7474 RetainPtr<ASCAuthorizationPresenter> m_presenter;
7575 RetainPtr<WKASCAuthorizationPresenterDelegate> m_presenterDelegate;
 76 Function<void()> m_delayedPresentation;
 77#if HAVE(ASC_AUTH_UI)
 78 bool m_delayedPresentationNeedsSecurityKey { false };
 79#endif
7680
7781 CredentialRequestHandler m_credentialRequestHandler;
7882

@@private:
8084 RetainPtr<LAContext> m_laContext;
8185
8286 CompletionHandler<void(WebCore::AuthenticatorAssertionResponse*)> m_responseHandler;
83  HashMap<ASCLoginChoiceProtocol *, RefPtr<WebCore::AuthenticatorAssertionResponse>> m_credentials;
 87 HashMap<String, RefPtr<WebCore::AuthenticatorAssertionResponse>> m_credentials;
8488
8589 CompletionHandler<void(const String&)> m_pinHandler;
8690};

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

@@AuthenticatorPresenterCoordinator::AuthenticatorPresenterCoordinator(const Authe
5353 [m_context addLoginChoice:adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initRegistrationChoice]).get()];
5454 break;
5555 case ClientDataType::Get:
56  if (transports.contains(AuthenticatorTransport::Usb) || transports.contains(AuthenticatorTransport::Nfc))
 56 if ((transports.contains(AuthenticatorTransport::Usb) || transports.contains(AuthenticatorTransport::Nfc)) && !transports.contains(AuthenticatorTransport::Internal))
5757 [m_context addLoginChoice:adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initAssertionPlaceholderChoice]).get()];
5858 break;
5959 default:

@@AuthenticatorPresenterCoordinator::AuthenticatorPresenterCoordinator(const Authe
6565 [m_presenter setDelegate:m_presenterDelegate.get()];
6666
6767 auto completionHandler = makeBlockPtr([manager = m_manager] (id<ASCCredentialProtocol> credential, NSError *error) mutable {
68  ASSERT(!RunLoop::isMain());
6968 if (credential || !error)
7069 return;
7170

@@AuthenticatorPresenterCoordinator::AuthenticatorPresenterCoordinator(const Authe
7675 manager->cancel();
7776 });
7877 });
 78
 79 if (type == ClientDataType::Get && transports.contains(AuthenticatorTransport::Internal)) {
 80 m_delayedPresentationNeedsSecurityKey = (transports.contains(AuthenticatorTransport::Usb) || transports.contains(AuthenticatorTransport::Nfc));
 81 m_delayedPresentation = [completionHandler = WTFMove(completionHandler), this] {
 82 [m_presenter presentAuthorizationWithContext:m_context.get() completionHandler:completionHandler.get()];
 83 };
 84 return;
 85 }
 86
7987 [m_presenter presentAuthorizationWithContext:m_context.get() completionHandler:completionHandler.get()];
8088#endif // HAVE(ASC_AUTH_UI)
8189}

@@void AuthenticatorPresenterCoordinator::updatePresenter(WebAuthenticationStatus
95103#if HAVE(ASC_AUTH_UI)
96104 switch (status) {
97105 case WebAuthenticationStatus::PinBlocked: {
98  auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorPINRequired userInfo:@{ ASCPINValidationResultKey: @(ASCPINValidationResultPINBlocked) }]);
 106 auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorAuthenticatorPermanentlyLocked userInfo:nil]);
99107 m_credentialRequestHandler(nil, error.get());
100108 break;
101109 }
102110 case WebAuthenticationStatus::PinAuthBlocked: {
103  auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorPINRequired userInfo:@{ ASCPINValidationResultKey: @(ASCPINValidationResultPINAuthBlocked) }]);
 111 auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorAuthenticatorTemporarilyLocked userInfo:nil]);
104112 m_credentialRequestHandler(nil, error.get());
105113 break;
106114 }
107115 case WebAuthenticationStatus::PinInvalid: {
108  auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorPINRequired userInfo:@{ ASCPINValidationResultKey: @(ASCPINValidationResultPINInvalid) }]);
 116 auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorPINInvalid userInfo:nil]);
109117 m_credentialRequestHandler(nil, error.get());
110118 break;
111119 }

@@void AuthenticatorPresenterCoordinator::updatePresenter(WebAuthenticationStatus
114122 [m_presenter updateInterfaceForUserVisibleError:error.get()];
115123 break;
116124 }
117  case WebAuthenticationStatus::NoCredentialsFound:
118  case WebAuthenticationStatus::LANoCredential: {
 125 case WebAuthenticationStatus::LANoCredential:
 126 if (m_delayedPresentationNeedsSecurityKey)
 127 [m_context addLoginChoice:adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initAssertionPlaceholderChoice]).get()];
 128 m_delayedPresentation();
 129 break;
 130 case WebAuthenticationStatus::NoCredentialsFound: {
119131 auto error = adoptNS([[NSError alloc] initWithDomain:ASCAuthorizationErrorDomain code:ASCAuthorizationErrorNoCredentialsFound userInfo:nil]);
120132 [m_presenter updateInterfaceForUserVisibleError:error.get()];
121133 break;

@@void AuthenticatorPresenterCoordinator::selectAssertionResponse(Vector<Ref<Authe
163175 auto loginChoice = adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initWithName:response->name() displayName:response->displayName() userHandle:userHandle.get()]);
164176 [loginChoices addObject:loginChoice.get()];
165177
166  m_credentials.add((ASCLoginChoiceProtocol *)loginChoice.get(), WTFMove(response));
 178 m_credentials.add(response->name(), WTFMove(response));
167179 }
168180
169181 [m_presenter updateInterfaceWithLoginChoices:loginChoices.get()];

@@void AuthenticatorPresenterCoordinator::selectAssertionResponse(Vector<Ref<Authe
171183 }
172184
173185 if (source == WebAuthenticationSource::Local) {
174  auto loginChoices = adoptNS([[NSMutableArray alloc] init]);
175 
176186 for (auto& response : responses) {
177187 RetainPtr<NSData> userHandle;
178188 if (response->userHandle())
179189 userHandle = adoptNS([[NSData alloc] initWithBytes:response->userHandle()->data() length:response->userHandle()->byteLength()]);
180190
181191 auto loginChoice = adoptNS([allocASCPlatformPublicKeyCredentialLoginChoiceInstance() initWithName:response->name() displayName:response->displayName() userHandle:userHandle.get()]);
182  [loginChoices addObject:loginChoice.get()];
 192 [m_context addLoginChoice:loginChoice.get()];
183193
184  m_credentials.add((ASCLoginChoiceProtocol *)loginChoice.get(), WTFMove(response));
 194 m_credentials.add(response->name(), WTFMove(response));
185195 }
186196
187  [loginChoices addObjectsFromArray:[m_context loginChoices]]; // Adds the security key option if exists.
188  [m_presenter updateInterfaceWithLoginChoices:loginChoices.get()];
 197 if (m_delayedPresentationNeedsSecurityKey)
 198 [m_context addLoginChoice:adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initAssertionPlaceholderChoice]).get()];
 199 m_delayedPresentation();
189200 return;
190201 }
191202#endif // HAVE(ASC_AUTH_UI)

@@void AuthenticatorPresenterCoordinator::setLAContext(LAContext *context)
225236 m_laContext = context;
226237}
227238
228 void AuthenticatorPresenterCoordinator::didSelectAssertionResponse(ASCLoginChoiceProtocol *loginChoice, LAContext *context)
 239void AuthenticatorPresenterCoordinator::didSelectAssertionResponse(const String& credentialName, LAContext *context)
229240{
230  auto response = m_credentials.take(loginChoice);
 241 auto response = m_credentials.take(credentialName);
231242 if (!response)
232243 return;
233244

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

@@- (void)authorizationPresenter:(ASCAuthorizationPresenter *)presenter credential
5555 }];
5656
5757 if ([loginChoice isKindOfClass:WebKit::getASCPlatformPublicKeyCredentialLoginChoiceClass()]) {
58  if ([(ASCPlatformPublicKeyCredentialLoginChoice *)loginChoice isRegistrationRequest]) {
 58 auto *platformLoginChoice = (ASCPlatformPublicKeyCredentialLoginChoice *)loginChoice;
 59
 60 if ([platformLoginChoice isRegistrationRequest]) {
5961 [self dispatchCoordinatorCallback:[context = retainPtr(context)] (WebKit::AuthenticatorPresenterCoordinator& coordinator) mutable {
6062 coordinator.setLAContext(context.get());
6163 }];

@@- (void)authorizationPresenter:(ASCAuthorizationPresenter *)presenter credential
6365 return;
6466 }
6567
66  if (![(ASCPlatformPublicKeyCredentialLoginChoice *)loginChoice isRegistrationRequest]) {
67  [self dispatchCoordinatorCallback:[loginChoice, context = retainPtr(context)] (WebKit::AuthenticatorPresenterCoordinator& coordinator) mutable {
68  coordinator.didSelectAssertionResponse((ASCLoginChoiceProtocol *)loginChoice, context.get());
69  }];
 68 String loginChoiceName = platformLoginChoice.name;
 69 [self dispatchCoordinatorCallback:[loginChoiceName = WTFMove(loginChoiceName), context = retainPtr(context)] (WebKit::AuthenticatorPresenterCoordinator& coordinator) mutable {
 70 coordinator.didSelectAssertionResponse(loginChoiceName, context.get());
 71 }];
7072
71  return;
72  }
 73 return;
7374 }
7475
7576 if ([loginChoice isKindOfClass:WebKit::getASCSecurityKeyPublicKeyCredentialLoginChoiceClass()]) {
76  if ([(ASCSecurityKeyPublicKeyCredentialLoginChoice *)loginChoice loginChoiceKind] == ASCSecurityKeyPublicKeyCredentialLoginChoiceKindAssertion) {
77  [self dispatchCoordinatorCallback:[loginChoice] (WebKit::AuthenticatorPresenterCoordinator& coordinator) mutable {
78  coordinator.didSelectAssertionResponse((ASCLoginChoiceProtocol *)loginChoice, nil);
 77 auto *securityKeyLoginChoice = (ASCSecurityKeyPublicKeyCredentialLoginChoice *)loginChoice;
 78
 79 if ([securityKeyLoginChoice loginChoiceKind] == ASCSecurityKeyPublicKeyCredentialLoginChoiceKindAssertion) {
 80 String loginChoiceName = securityKeyLoginChoice.name;
 81 [self dispatchCoordinatorCallback:[loginChoiceName = WTFMove(loginChoiceName)] (WebKit::AuthenticatorPresenterCoordinator& coordinator) mutable {
 82 coordinator.didSelectAssertionResponse(loginChoiceName, nil);
7983 }];
8084
8185 return;