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
155167 if (source == WebAuthenticationSource::External) {
156168 auto loginChoices = adoptNS([[NSMutableArray alloc] init]);
157169
 170 m_credentials.clear();
158171 for (auto& response : responses) {
159172 RetainPtr<NSData> userHandle;
160173 if (response->userHandle())

@@void AuthenticatorPresenterCoordinator::selectAssertionResponse(Vector<Ref<Authe
163176 auto loginChoice = adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initWithName:response->name() displayName:response->displayName() userHandle:userHandle.get()]);
164177 [loginChoices addObject:loginChoice.get()];
165178
166  m_credentials.add((ASCLoginChoiceProtocol *)loginChoice.get(), WTFMove(response));
 179 m_credentials.add(response->name(), WTFMove(response));
167180 }
168181
169182 [m_presenter updateInterfaceWithLoginChoices:loginChoices.get()];

@@void AuthenticatorPresenterCoordinator::selectAssertionResponse(Vector<Ref<Authe
171184 }
172185
173186 if (source == WebAuthenticationSource::Local) {
174  auto loginChoices = adoptNS([[NSMutableArray alloc] init]);
175 
 187 m_credentials.clear();
176188 for (auto& response : responses) {
177189 RetainPtr<NSData> userHandle;
178190 if (response->userHandle())
179191 userHandle = adoptNS([[NSData alloc] initWithBytes:response->userHandle()->data() length:response->userHandle()->byteLength()]);
180192
181193 auto loginChoice = adoptNS([allocASCPlatformPublicKeyCredentialLoginChoiceInstance() initWithName:response->name() displayName:response->displayName() userHandle:userHandle.get()]);
182  [loginChoices addObject:loginChoice.get()];
 194 [m_context addLoginChoice:loginChoice.get()];
183195
184  m_credentials.add((ASCLoginChoiceProtocol *)loginChoice.get(), WTFMove(response));
 196 m_credentials.add(response->name(), WTFMove(response));
185197 }
186198
187  [loginChoices addObjectsFromArray:[m_context loginChoices]]; // Adds the security key option if exists.
188  [m_presenter updateInterfaceWithLoginChoices:loginChoices.get()];
 199 if (m_delayedPresentationNeedsSecurityKey)
 200 [m_context addLoginChoice:adoptNS([allocASCSecurityKeyPublicKeyCredentialLoginChoiceInstance() initAssertionPlaceholderChoice]).get()];
 201 m_delayedPresentation();
189202 return;
190203 }
191204#endif // HAVE(ASC_AUTH_UI)

@@void AuthenticatorPresenterCoordinator::setLAContext(LAContext *context)
225238 m_laContext = context;
226239}
227240
228 void AuthenticatorPresenterCoordinator::didSelectAssertionResponse(ASCLoginChoiceProtocol *loginChoice, LAContext *context)
 241void AuthenticatorPresenterCoordinator::didSelectAssertionResponse(const String& credentialName, LAContext *context)
229242{
230  auto response = m_credentials.take(loginChoice);
 243 auto response = m_credentials.take(credentialName);
231244 if (!response)
232245 return;
233246

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;