/* * Copyright (C) 2021 Apple Inc. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS'' * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF * THE POSSIBILITY OF SUCH DAMAGE. */ #if HAVE(UNIFIED_ASC_AUTH_UI) #import "config.h" #import "WebAuthenticatorCoordinatorProxy.h" #import "LocalService.h" #import "WKError.h" #import "WebAuthenticationRequestData.h" #import "WebPageProxy.h" #import #import #import #import #import #import #import #import "AuthenticationServicesCoreSoftLink.h" namespace WebKit { using namespace WebCore; static inline Ref toArrayBuffer(NSData *data) { return ArrayBuffer::create(reinterpret_cast(data.bytes), data.length); } static inline RetainPtr toNSData(const Vector& data) { return adoptNS([[NSData alloc] initWithBytes:data.data() length:data.size()]); } static inline RetainPtr toNSString(UserVerificationRequirement userVerificationRequirement) { switch (userVerificationRequirement) { case UserVerificationRequirement::Required: return @"required"; case UserVerificationRequirement::Discouraged: return @"discouraged"; case UserVerificationRequirement::Preferred: return @"preferred"; } return @"preferred"; } static inline RetainPtr toNSString(AttestationConveyancePreference attestationConveyancePreference) { switch (attestationConveyancePreference) { case AttestationConveyancePreference::Direct: return @"direct"; case AttestationConveyancePreference::Indirect: return @"indirect"; case AttestationConveyancePreference::None: return @"none"; } return @"none"; } static inline ExceptionCode toExceptionCode(NSInteger nsErrorCode) { ExceptionCode exceptionCode = (ExceptionCode)nsErrorCode; switch (exceptionCode) { case IndexSizeError: FALLTHROUGH; case HierarchyRequestError: FALLTHROUGH; case WrongDocumentError: FALLTHROUGH; case InvalidCharacterError: FALLTHROUGH; case NoModificationAllowedError: FALLTHROUGH; case NotFoundError: FALLTHROUGH; case NotSupportedError: FALLTHROUGH; case InUseAttributeError: FALLTHROUGH; case InvalidStateError: FALLTHROUGH; case SyntaxError: FALLTHROUGH; case InvalidModificationError: FALLTHROUGH; case NamespaceError: FALLTHROUGH; case InvalidAccessError: FALLTHROUGH; case TypeMismatchError: FALLTHROUGH; case SecurityError: FALLTHROUGH; case NetworkError: FALLTHROUGH; case AbortError: FALLTHROUGH; case URLMismatchError: FALLTHROUGH; case QuotaExceededError: FALLTHROUGH; case TimeoutError: FALLTHROUGH; case InvalidNodeTypeError: FALLTHROUGH; case DataCloneError: FALLTHROUGH; case EncodingError: FALLTHROUGH; case NotReadableError: FALLTHROUGH; case UnknownError: FALLTHROUGH; case ConstraintError: FALLTHROUGH; case DataError: FALLTHROUGH; case TransactionInactiveError: FALLTHROUGH; case ReadonlyError: FALLTHROUGH; case VersionError: FALLTHROUGH; case OperationError: FALLTHROUGH; case NotAllowedError: FALLTHROUGH; case RangeError: FALLTHROUGH; case TypeError: FALLTHROUGH; case JSSyntaxError: FALLTHROUGH; case StackOverflowError: FALLTHROUGH; case OutOfMemoryError: FALLTHROUGH; case ExistingExceptionError: return exceptionCode; } return NotAllowedError; } static inline RetainPtr toASCDescriptor(PublicKeyCredentialDescriptor descriptor) { RetainPtr> transports; size_t transportCount = descriptor.transports.size(); if (transportCount) { transports = adoptNS([[NSMutableArray alloc] initWithCapacity:transportCount]); for (AuthenticatorTransport transport : descriptor.transports) { NSString *transportString = nil; switch (transport) { case AuthenticatorTransport::Usb: transportString = @"usb"; break; case AuthenticatorTransport::Nfc: transportString = @"nfc"; break; case AuthenticatorTransport::Ble: transportString = @"ble"; break; case AuthenticatorTransport::Internal: transportString = @"internal"; break; case AuthenticatorTransport::Cable: transportString = @"cable"; break; case AuthenticatorTransport::Hybrid: transportString = @"hybrid"; break; } if (transportString) [transports addObject:transportString]; } } return adoptNS([allocASCPublicKeyCredentialDescriptorInstance() initWithCredentialID:WebCore::toNSData(descriptor.id).get() transports:transports.get()]); } static inline RetainPtr toASCExtensions(const AuthenticationExtensionsClientInputs& extensions) { if ([allocASCWebAuthenticationExtensionsClientInputsInstance() respondsToSelector:@selector(initWithAppID:isGoogleLegacyAppIDSupport:)]) return adoptNS([allocASCWebAuthenticationExtensionsClientInputsInstance() initWithAppID:extensions.appid isGoogleLegacyAppIDSupport:extensions.googleLegacyAppidSupport]); return nil; } static inline void setGlobalFrameIDForContext(RetainPtr requestContext, std::optional globalFrameID) { if (globalFrameID && [requestContext respondsToSelector:@selector(setGlobalFrameID:)]) { auto ascGlobalFrameID = adoptNS([allocASCGlobalFrameIdentifierInstance() init]); ascGlobalFrameID.get().webFrameID = [NSNumber numberWithUnsignedLong:globalFrameID->frameID.toUInt64()]; ascGlobalFrameID.get().webPageID = [NSNumber numberWithUnsignedLong:globalFrameID->pageID.toUInt64()]; requestContext.get().globalFrameID = ascGlobalFrameID.get(); } } static inline ASPublicKeyCredentialResidentKeyPreference toASCResidentKeyPreference(std::optional requirement, bool requireResidentKey) { if (!requirement) return requireResidentKey ? ASPublicKeyCredentialResidentKeyPreferenceRequired : ASPublicKeyCredentialResidentKeyPreferenceNotPresent; switch (*requirement) { case ResidentKeyRequirement::Discouraged: return ASPublicKeyCredentialResidentKeyPreferenceDiscouraged; case ResidentKeyRequirement::Preferred: return ASPublicKeyCredentialResidentKeyPreferencePreferred; case ResidentKeyRequirement::Required: return ASPublicKeyCredentialResidentKeyPreferenceRequired; } ASSERT_NOT_REACHED(); return ASPublicKeyCredentialResidentKeyPreferenceNotPresent; } static RetainPtr configureRegistrationRequestContext(const PublicKeyCredentialCreationOptions& options, const Vector& hash, std::optional globalFrameID) { ASCCredentialRequestTypes requestTypes = ASCCredentialRequestTypePlatformPublicKeyRegistration | ASCCredentialRequestTypeSecurityKeyPublicKeyRegistration; RetainPtr userVerification; bool shouldRequireResidentKey = false; std::optional residentKeyRequirement; std::optional authenticatorSelection = options.authenticatorSelection; if (authenticatorSelection) { std::optional attachment = authenticatorSelection->authenticatorAttachment; if (attachment == AuthenticatorAttachment::Platform) requestTypes = ASCCredentialRequestTypePlatformPublicKeyRegistration; else if (attachment == AuthenticatorAttachment::CrossPlatform) requestTypes = ASCCredentialRequestTypeSecurityKeyPublicKeyRegistration; userVerification = toNSString(authenticatorSelection->userVerification); shouldRequireResidentKey = authenticatorSelection->requireResidentKey; residentKeyRequirement = authenticatorSelection->residentKey; } if (!LocalService::isAvailable()) requestTypes &= ~ASCCredentialRequestTypePlatformPublicKeyRegistration; auto requestContext = adoptNS([allocASCCredentialRequestContextInstance() initWithRequestTypes:requestTypes]); [requestContext setRelyingPartyIdentifier:options.rp.id]; setGlobalFrameIDForContext(requestContext, globalFrameID); auto credentialCreationOptions = adoptNS([allocASCPublicKeyCredentialCreationOptionsInstance() init]); if ([credentialCreationOptions respondsToSelector:@selector(setClientDataHash:)]) [credentialCreationOptions setClientDataHash:toNSData(hash).get()]; else [credentialCreationOptions setChallenge:WebCore::toNSData(options.challenge).get()]; [credentialCreationOptions setRelyingPartyIdentifier:options.rp.id]; [credentialCreationOptions setUserName:options.user.name]; [credentialCreationOptions setUserIdentifier:WebCore::toNSData(options.user.id).get()]; [credentialCreationOptions setUserDisplayName:options.user.displayName]; [credentialCreationOptions setUserVerificationPreference:userVerification.get()]; if ([credentialCreationOptions respondsToSelector:@selector(setResidentKeyPreference:)]) [credentialCreationOptions setResidentKeyPreference:toASCResidentKeyPreference(residentKeyRequirement, shouldRequireResidentKey)]; else [credentialCreationOptions setShouldRequireResidentKey:shouldRequireResidentKey]; [credentialCreationOptions setAttestationPreference:toNSString(options.attestation).get()]; RetainPtr> supportedAlgorithmIdentifiers = adoptNS([[NSMutableArray alloc] initWithCapacity:options.pubKeyCredParams.size()]); for (PublicKeyCredentialCreationOptions::Parameters algorithmParameter : options.pubKeyCredParams) [supportedAlgorithmIdentifiers addObject:@(algorithmParameter.alg)]; [credentialCreationOptions setSupportedAlgorithmIdentifiers:supportedAlgorithmIdentifiers.get()]; size_t excludedCredentialCount = options.excludeCredentials.size(); if (excludedCredentialCount) { RetainPtr> excludedCredentials = adoptNS([[NSMutableArray alloc] initWithCapacity:excludedCredentialCount]); for (PublicKeyCredentialDescriptor descriptor : options.excludeCredentials) [excludedCredentials addObject:toASCDescriptor(descriptor).get()]; [credentialCreationOptions setExcludedCredentials:excludedCredentials.get()]; } if (options.extensions) { if ([credentialCreationOptions respondsToSelector:@selector(setExtensionsCBOR:)]) [credentialCreationOptions setExtensionsCBOR:toNSData(options.extensions->toCBOR()).get()]; else [credentialCreationOptions setExtensions:toASCExtensions(*options.extensions).get()]; } if (options.timeout && [credentialCreationOptions respondsToSelector:@selector(setTimeout:)]) credentialCreationOptions.get().timeout = [NSNumber numberWithUnsignedInt:*options.timeout]; if (requestTypes & ASCCredentialRequestTypePlatformPublicKeyRegistration) [requestContext setPlatformKeyCredentialCreationOptions:credentialCreationOptions.get()]; if (requestTypes & ASCCredentialRequestTypeSecurityKeyPublicKeyRegistration) [requestContext setSecurityKeyCredentialCreationOptions:credentialCreationOptions.get()]; return requestContext; } static inline RetainPtr configureAssertionOptions(const PublicKeyCredentialRequestOptions& options, const Vector& hash, ASCPublicKeyCredentialKind kind, const std::optional& parentOrigin, RetainPtr> allowedCredentials, RetainPtr userVerification) { auto assertionOptions = adoptNS(allocASCPublicKeyCredentialAssertionOptionsInstance()); if ([assertionOptions respondsToSelector:@selector(initWithKind:relyingPartyIdentifier:clientDataHash:userVerificationPreference:allowedCredentials:)]) { auto nsHash = toNSData(hash); [assertionOptions initWithKind:kind relyingPartyIdentifier:options.rpId clientDataHash:nsHash.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()]; } else { auto challenge = WebCore::toNSData(options.challenge); [assertionOptions initWithKind:kind relyingPartyIdentifier:options.rpId challenge:challenge.get() userVerificationPreference:userVerification.get() allowedCredentials:allowedCredentials.get()]; } if (options.extensions) { if ([assertionOptions respondsToSelector:@selector(setExtensionsCBOR:)]) [assertionOptions setExtensionsCBOR:toNSData(options.extensions->toCBOR()).get()]; else [assertionOptions setExtensions:toASCExtensions(*options.extensions).get()]; } if (parentOrigin && [assertionOptions respondsToSelector:@selector(setDestinationSiteForCrossSiteAssertion:)]) assertionOptions.get().destinationSiteForCrossSiteAssertion = parentOrigin->toString(); else if (parentOrigin && ![assertionOptions respondsToSelector:@selector(setDestinationSiteForCrossSiteAssertion:)]) return nil; if (options.timeout && [assertionOptions respondsToSelector:@selector(setTimeout:)]) assertionOptions.get().timeout = [NSNumber numberWithUnsignedInt:*options.timeout]; return assertionOptions; } static RetainPtr configurationAssertionRequestContext(const PublicKeyCredentialRequestOptions& options, const Vector& hash, std::optional mediation, std::optional globalFrameID, std::optional& parentOrigin) { ASCCredentialRequestTypes requestTypes = ASCCredentialRequestTypePlatformPublicKeyAssertion | ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion; RetainPtr userVerification; std::optional attachment = options.authenticatorAttachment; if (attachment == AuthenticatorAttachment::Platform) requestTypes = ASCCredentialRequestTypePlatformPublicKeyAssertion; else if (attachment == AuthenticatorAttachment::CrossPlatform) requestTypes = ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion; userVerification = toNSString(options.userVerification); size_t allowedCredentialCount = options.allowCredentials.size(); RetainPtr> allowedCredentials; if (allowedCredentialCount) { allowedCredentials = adoptNS([[NSMutableArray alloc] initWithCapacity:allowedCredentialCount]); for (PublicKeyCredentialDescriptor descriptor : options.allowCredentials) [allowedCredentials addObject:toASCDescriptor(descriptor).get()]; } auto requestContext = adoptNS([allocASCCredentialRequestContextInstance() initWithRequestTypes:requestTypes]); [requestContext setRelyingPartyIdentifier:options.rpId]; if (mediation == MediationRequirement::Conditional) { if (![requestContext respondsToSelector:@selector(setRequestStyle:)]) return nil; requestContext.get().requestStyle = ASCredentialRequestStyleAutoFill; } setGlobalFrameIDForContext(requestContext, globalFrameID); if (requestTypes & ASCCredentialRequestTypePlatformPublicKeyAssertion) { auto assertionOptions = configureAssertionOptions(options, hash, ASCPublicKeyCredentialKindPlatform, parentOrigin, allowedCredentials, userVerification); if (!assertionOptions) return nil; [requestContext setPlatformKeyCredentialAssertionOptions:assertionOptions.get()]; } if (requestTypes & ASCCredentialRequestTypeSecurityKeyPublicKeyAssertion) { auto assertionOptions = configureAssertionOptions(options, hash, ASCPublicKeyCredentialKindSecurityKey, parentOrigin, allowedCredentials, userVerification); if (!assertionOptions) return nil; [requestContext setSecurityKeyCredentialAssertionOptions:assertionOptions.get()]; } return requestContext; } static Vector toAuthenticatorTransports(NSArray *ascTransports) { Vector transports; transports.reserveInitialCapacity(ascTransports.count); for (NSNumber *ascTransport : ascTransports) { if (WTF::isValidEnum(ascTransport.intValue)) transports.uncheckedAppend(static_cast(ascTransport.intValue)); } return transports; } static std::optional toExtensionOutputs(NSData *extensionOutputsCBOR) { if (!extensionOutputsCBOR) return std::nullopt; return AuthenticationExtensionsClientOutputs::fromCBOR(vectorFromNSData(extensionOutputsCBOR)); } RetainPtr WebAuthenticatorCoordinatorProxy::contextForRequest(WebAuthenticationRequestData&& requestData) { RetainPtr result; WTF::switchOn(requestData.options, [&](const PublicKeyCredentialCreationOptions& options) { result = configureRegistrationRequestContext(options, requestData.hash, requestData.globalFrameID); }, [&](const PublicKeyCredentialRequestOptions& options) { result = configurationAssertionRequestContext(options, requestData.hash, requestData.mediation, requestData.globalFrameID, requestData.parentOrigin); }); return result; } static inline void continueAfterRequest(RetainPtr> credential, RetainPtr error, RequestCompletionHandler&& handler) { AuthenticatorResponseData response = { }; AuthenticatorAttachment attachment; ExceptionData exceptionData = { }; if ([credential isKindOfClass:getASCPlatformPublicKeyCredentialRegistrationClass()]) { attachment = AuthenticatorAttachment::Platform; response.isAuthenticatorAttestationResponse = true; ASCPlatformPublicKeyCredentialRegistration *registrationCredential = credential.get(); response.rawId = toArrayBuffer(registrationCredential.credentialID); response.attestationObject = toArrayBuffer(registrationCredential.attestationObject); if ([registrationCredential respondsToSelector:@selector(transports)]) response.transports = toAuthenticatorTransports(registrationCredential.transports); if ([registrationCredential respondsToSelector:@selector(extensionOutputsCBOR)]) response.extensionOutputs = toExtensionOutputs(registrationCredential.extensionOutputsCBOR); } else if ([credential isKindOfClass:getASCSecurityKeyPublicKeyCredentialRegistrationClass()]) { attachment = AuthenticatorAttachment::CrossPlatform; response.isAuthenticatorAttestationResponse = true; ASCSecurityKeyPublicKeyCredentialRegistration *registrationCredential = credential.get(); response.rawId = toArrayBuffer(registrationCredential.credentialID); response.attestationObject = toArrayBuffer(registrationCredential.attestationObject); if ([registrationCredential respondsToSelector:@selector(transports)]) response.transports = toAuthenticatorTransports(registrationCredential.transports); if ([registrationCredential respondsToSelector:@selector(extensionOutputsCBOR)]) response.extensionOutputs = toExtensionOutputs(registrationCredential.extensionOutputsCBOR); } else if ([credential isKindOfClass:getASCPlatformPublicKeyCredentialAssertionClass()]) { attachment = AuthenticatorAttachment::Platform; response.isAuthenticatorAttestationResponse = false; ASCPlatformPublicKeyCredentialAssertion *assertionCredential = credential.get(); response.rawId = toArrayBuffer(assertionCredential.credentialID); response.authenticatorData = toArrayBuffer(assertionCredential.authenticatorData); response.signature = toArrayBuffer(assertionCredential.signature); response.userHandle = toArrayBuffer(assertionCredential.userHandle); if ([assertionCredential respondsToSelector:@selector(extensionOutputsCBOR)]) response.extensionOutputs = toExtensionOutputs(assertionCredential.extensionOutputsCBOR); } else if ([credential isKindOfClass:getASCSecurityKeyPublicKeyCredentialAssertionClass()]) { attachment = AuthenticatorAttachment::CrossPlatform; response.isAuthenticatorAttestationResponse = false; ASCSecurityKeyPublicKeyCredentialAssertion *assertionCredential = credential.get(); response.rawId = toArrayBuffer(assertionCredential.credentialID); response.authenticatorData = toArrayBuffer(assertionCredential.authenticatorData); response.signature = toArrayBuffer(assertionCredential.signature); response.userHandle = toArrayBuffer(assertionCredential.userHandle); if ([assertionCredential respondsToSelector:@selector(extensionOutputsCBOR)]) response.extensionOutputs = toExtensionOutputs(assertionCredential.extensionOutputsCBOR); } else { attachment = (AuthenticatorAttachment) 0; ExceptionCode exceptionCode; NSString *errorMessage = nil; if ([error.get().domain isEqualToString:WKErrorDomain]) { exceptionCode = toExceptionCode(error.get().code); errorMessage = error.get().userInfo[NSLocalizedDescriptionKey]; } else { exceptionCode = NotAllowedError; if ([error.get().domain isEqualToString:ASCAuthorizationErrorDomain] && error.get().code == ASCAuthorizationErrorUserCanceled) errorMessage = @"This request has been cancelled by the user."; else errorMessage = @"Operation failed."; } exceptionData = { exceptionCode, errorMessage }; } handler(response, attachment, exceptionData); } void WebAuthenticatorCoordinatorProxy::performRequest(RetainPtr requestContext, RequestCompletionHandler&& handler) { if (requestContext.get().requestTypes == ASCCredentialRequestTypeNone) { handler({ }, (AuthenticatorAttachment)0, ExceptionData { NotAllowedError, "This request has been cancelled by the user."_s }); return; } m_proxy = adoptNS([allocASCAgentProxyInstance() init]); #if PLATFORM(MAC) || PLATFORM(MACCATALYST) if ([requestContext respondsToSelector:@selector(requestStyle)] && requestContext.get().requestStyle == ASCredentialRequestStyleAutoFill) { [m_proxy performAutoFillAuthorizationRequestsForContext:requestContext.get() withCompletionHandler:makeBlockPtr([weakThis = WeakPtr { *this }, handler = WTFMove(handler)](id credential, NSError *error) mutable { ensureOnMainRunLoop([weakThis, handler = WTFMove(handler), credential = retainPtr(credential), error = retainPtr(error)] () mutable { if (!weakThis) { handler({ }, (AuthenticatorAttachment)0, ExceptionData { NotAllowedError, "Operation failed."_s }); return; } continueAfterRequest(credential, error, WTFMove(handler)); if (weakThis->m_proxy) weakThis->m_proxy.clear(); }); }).get()]; return; } #endif // PLATFORM(MAC) || PLATFORM(MACCATALYST) #if PLATFORM(IOS) [m_proxy performAuthorizationRequestsForContext:requestContext.get() withCompletionHandler:makeBlockPtr([weakThis = WeakPtr { *this }, handler = WTFMove(handler)](id credential, NSError *error) mutable { callOnMainRunLoop([weakThis, handler = WTFMove(handler), credential = retainPtr(credential), error = retainPtr(error)] () mutable { #elif PLATFORM(MAC) RetainPtr window = m_webPageProxy.platformWindow(); [m_proxy performAuthorizationRequestsForContext:requestContext.get() withClearanceHandler:makeBlockPtr([weakThis = WeakPtr { *this }, handler = WTFMove(handler), window = WTFMove(window)](NSXPCListenerEndpoint *daemonEndpoint, NSError *error) mutable { callOnMainRunLoop([weakThis, handler = WTFMove(handler), window = WTFMove(window), daemonEndpoint = retainPtr(daemonEndpoint), error = retainPtr(error)] () mutable { if (!weakThis || !daemonEndpoint) { LOG_ERROR("Could not connect to authorization daemon: %@\n", error.get()); handler({ }, (AuthenticatorAttachment)0, ExceptionData { NotAllowedError, "Operation failed."_s }); if (weakThis && weakThis->m_proxy) weakThis->m_proxy.clear(); return; } weakThis->m_presenter = adoptNS([allocASCAuthorizationRemotePresenterInstance() init]); [weakThis->m_presenter presentWithWindow:window.get() daemonEndpoint:daemonEndpoint.get() completionHandler:makeBlockPtr([weakThis, handler = WTFMove(handler)](id credentialNotRetain, NSError *errorNotRetain) mutable { auto credential = retainPtr(credentialNotRetain); auto error = retainPtr(errorNotRetain); #endif continueAfterRequest(credential, error, WTFMove(handler)); if (weakThis && weakThis->m_proxy) weakThis->m_proxy.clear(); #if PLATFORM(MAC) }).get()]; #endif }); }).get()]; } void WebAuthenticatorCoordinatorProxy::isConditionalMediationAvailable(QueryCompletionHandler&& handler) { handler([getASCWebKitSPISupportClass() shouldUseAlternateCredentialStore]); } void WebAuthenticatorCoordinatorProxy::isUserVerifyingPlatformAuthenticatorAvailable(QueryCompletionHandler&& handler) { if ([getASCWebKitSPISupportClass() shouldUseAlternateCredentialStore]) { handler(true); return; } handler(LocalService::isAvailable()); } void WebAuthenticatorCoordinatorProxy::cancel() { if (m_proxy) { [m_proxy cancelCurrentRequest]; m_proxy.clear(); } } } // namespace WebKit #endif // HAVE(UNIFIED_ASC_AUTH_UI)