MacOSWebAuthnService.mm (65887B)
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */ 3 /* This Source Code Form is subject to the terms of the Mozilla Public 4 * License, v. 2.0. If a copy of the MPL was not distributed with this file, 5 * You can obtain one at http://mozilla.org/MPL/2.0/. */ 6 7 #import <AuthenticationServices/AuthenticationServices.h> 8 9 #include "MacOSWebAuthnService.h" 10 11 #include "CFTypeRefPtr.h" 12 #include "WebAuthnAutoFillEntry.h" 13 #include "WebAuthnEnumStrings.h" 14 #include "WebAuthnResult.h" 15 #include "WebAuthnTransportIdentifiers.h" 16 #include "mozilla/Maybe.h" 17 #include "mozilla/StaticPrefs_security.h" 18 #include "mozilla/dom/CanonicalBrowsingContext.h" 19 #include "nsCocoaUtils.h" 20 #include "nsIWebAuthnPromise.h" 21 #include "nsThreadUtils.h" 22 23 // The documentation for the platform APIs used here can be found at: 24 // https://developer.apple.com/documentation/authenticationservices/public-private_key_authentication/supporting_passkeys 25 26 namespace { 27 static mozilla::LazyLogModule gMacOSWebAuthnServiceLog("macoswebauthnservice"); 28 } // namespace 29 30 namespace mozilla::dom { 31 class API_AVAILABLE(macos(13.3)) MacOSWebAuthnService; 32 } // namespace mozilla::dom 33 34 // The following ASC* declarations are from the private framework 35 // AuthenticationServicesCore. The full definitions can be found in WebKit's 36 // source at Source/WebKit/Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h. 37 // Overriding ASAuthorizationController's _requestContextWithRequests is 38 // currently the only way to provide the right information to the macOS 39 // WebAuthn API (namely, the clientDataHash for requests made to physical 40 // tokens). 41 42 NS_ASSUME_NONNULL_BEGIN 43 44 @class ASCPublicKeyCredentialDescriptor; 45 @interface ASCPublicKeyCredentialDescriptor : NSObject <NSSecureCoding> 46 - (instancetype)initWithCredentialID:(NSData*)credentialID 47 transports: 48 (nullable NSArray<NSString*>*)allowedTransports; 49 @end 50 51 @protocol ASCPublicKeyCredentialCreationOptions 52 @property(nonatomic, copy) NSData* clientDataHash; 53 @property(nonatomic, nullable, copy) NSData* challenge; 54 @property(nonatomic, copy) 55 NSArray<ASCPublicKeyCredentialDescriptor*>* excludedCredentials; 56 @end 57 58 @protocol ASCPublicKeyCredentialAssertionOptions <NSCopying> 59 @property(nonatomic, copy) NSData* clientDataHash; 60 @end 61 62 @protocol ASCCredentialRequestContext 63 @property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialCreationOptions> 64 platformKeyCredentialCreationOptions; 65 @property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialCreationOptions> 66 securityKeyCredentialCreationOptions; 67 @property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialAssertionOptions> 68 platformKeyCredentialAssertionOptions; 69 @property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialAssertionOptions> 70 securityKeyCredentialAssertionOptions; 71 @end 72 73 @interface ASAuthorizationController (Secrets) 74 - (id<ASCCredentialRequestContext>) 75 _requestContextWithRequests:(NSArray<ASAuthorizationRequest*>*)requests 76 error:(NSError**)outError; 77 @end 78 79 NSArray<NSString*>* TransportsByteToTransportsArray(const uint8_t aTransports) 80 API_AVAILABLE(macos(13.3)) { 81 NSMutableArray<NSString*>* transportsNS = [[NSMutableArray alloc] init]; 82 if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) == 83 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) { 84 [transportsNS 85 addObject: 86 ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportUSB]; 87 } 88 if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) == 89 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) { 90 [transportsNS 91 addObject: 92 ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportNFC]; 93 } 94 if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) == 95 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) { 96 [transportsNS 97 addObject: 98 ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportBluetooth]; 99 } 100 // TODO (bug 1859367): the platform doesn't have a definition for "internal" 101 // transport. When it does, this code should be updated to handle it. 102 return transportsNS; 103 } 104 105 NSArray* CredentialListsToCredentialDescriptorArray( 106 const nsTArray<nsTArray<uint8_t>>& aCredentialList, 107 const nsTArray<uint8_t>& aCredentialListTransports, 108 const Class credentialDescriptorClass) API_AVAILABLE(macos(13.3)) { 109 MOZ_ASSERT(aCredentialList.Length() == aCredentialListTransports.Length()); 110 NSMutableArray* credentials = [[NSMutableArray alloc] init]; 111 for (size_t i = 0; i < aCredentialList.Length(); i++) { 112 const nsTArray<uint8_t>& credentialId = aCredentialList[i]; 113 const uint8_t& credentialTransports = aCredentialListTransports[i]; 114 NSData* credentialIdNS = [NSData dataWithBytes:credentialId.Elements() 115 length:credentialId.Length()]; 116 NSArray<NSString*>* credentialTransportsNS = 117 TransportsByteToTransportsArray(credentialTransports); 118 NSObject* credential = [[credentialDescriptorClass alloc] 119 initWithCredentialID:credentialIdNS 120 transports:credentialTransportsNS]; 121 [credentials addObject:credential]; 122 } 123 return credentials; 124 } 125 126 // MacOSAuthorizationController is an ASAuthorizationController that overrides 127 // _requestContextWithRequests so that the implementation can set some options 128 // that aren't directly settable using the public API. 129 API_AVAILABLE(macos(13.3)) 130 @interface MacOSAuthorizationController : ASAuthorizationController 131 @end 132 133 @implementation MacOSAuthorizationController { 134 nsTArray<uint8_t> mClientDataHash; 135 nsTArray<nsTArray<uint8_t>> mCredentialList; 136 nsTArray<uint8_t> mCredentialListTransports; 137 } 138 139 - (void)setRegistrationOptions: 140 (id<ASCPublicKeyCredentialCreationOptions>)registrationOptions { 141 registrationOptions.clientDataHash = 142 [NSData dataWithBytes:mClientDataHash.Elements() 143 length:mClientDataHash.Length()]; 144 // Unset challenge so that the implementation uses clientDataHash (the API 145 // returns an error otherwise). 146 registrationOptions.challenge = nil; 147 const Class publicKeyCredentialDescriptorClass = 148 NSClassFromString(@"ASCPublicKeyCredentialDescriptor"); 149 NSArray<ASCPublicKeyCredentialDescriptor*>* excludedCredentials = 150 CredentialListsToCredentialDescriptorArray( 151 mCredentialList, mCredentialListTransports, 152 publicKeyCredentialDescriptorClass); 153 if ([excludedCredentials count] > 0) { 154 registrationOptions.excludedCredentials = excludedCredentials; 155 } 156 } 157 158 - (void)stashClientDataHash:(nsTArray<uint8_t>&&)clientDataHash 159 andCredentialList:(nsTArray<nsTArray<uint8_t>>&&)credentialList 160 andCredentialListTransports:(nsTArray<uint8_t>&&)credentialListTransports { 161 mClientDataHash = std::move(clientDataHash); 162 mCredentialList = std::move(credentialList); 163 mCredentialListTransports = std::move(credentialListTransports); 164 } 165 166 - (id<ASCCredentialRequestContext>) 167 _requestContextWithRequests:(NSArray<ASAuthorizationRequest*>*)requests 168 error:(NSError**)outError { 169 id<ASCCredentialRequestContext> context = 170 [super _requestContextWithRequests:requests error:outError]; 171 172 if (context.platformKeyCredentialCreationOptions) { 173 [self setRegistrationOptions:context.platformKeyCredentialCreationOptions]; 174 } 175 if (context.securityKeyCredentialCreationOptions) { 176 [self setRegistrationOptions:context.securityKeyCredentialCreationOptions]; 177 } 178 179 if (context.platformKeyCredentialAssertionOptions) { 180 id<ASCPublicKeyCredentialAssertionOptions> assertionOptions = 181 context.platformKeyCredentialAssertionOptions; 182 assertionOptions.clientDataHash = 183 [NSData dataWithBytes:mClientDataHash.Elements() 184 length:mClientDataHash.Length()]; 185 context.platformKeyCredentialAssertionOptions = 186 [assertionOptions copyWithZone:nil]; 187 } 188 if (context.securityKeyCredentialAssertionOptions) { 189 id<ASCPublicKeyCredentialAssertionOptions> assertionOptions = 190 context.securityKeyCredentialAssertionOptions; 191 assertionOptions.clientDataHash = 192 [NSData dataWithBytes:mClientDataHash.Elements() 193 length:mClientDataHash.Length()]; 194 context.securityKeyCredentialAssertionOptions = 195 [assertionOptions copyWithZone:nil]; 196 } 197 198 return context; 199 } 200 @end 201 202 // MacOSAuthenticatorRequestDelegate is an ASAuthorizationControllerDelegate, 203 // which can be set as an ASAuthorizationController's delegate to be called 204 // back when a request for a platform authenticator attestation or assertion 205 // response completes either with an attestation or assertion 206 // (didCompleteWithAuthorization) or with an error (didCompleteWithError). 207 API_AVAILABLE(macos(13.3)) 208 @interface MacOSAuthenticatorRequestDelegate 209 : NSObject <ASAuthorizationControllerDelegate> 210 - (void)setCallback:(mozilla::dom::MacOSWebAuthnService*)callback; 211 @end 212 213 // MacOSAuthenticatorPresentationContextProvider is an 214 // ASAuthorizationControllerPresentationContextProviding, which can be set as 215 // an ASAuthorizationController's presentationContextProvider, and provides a 216 // presentation anchor for the ASAuthorizationController. Basically, this 217 // provides the API a handle to the window that made the request. 218 API_AVAILABLE(macos(13.3)) 219 @interface MacOSAuthenticatorPresentationContextProvider 220 : NSObject <ASAuthorizationControllerPresentationContextProviding> 221 @property(nonatomic, strong) NSWindow* window; 222 @end 223 224 namespace mozilla::dom { 225 226 #pragma clang diagnostic push 227 #pragma clang diagnostic ignored "-Wnullability-completeness" 228 class API_AVAILABLE(macos(13.3)) MacOSWebAuthnService final 229 : public nsIWebAuthnService { 230 public: 231 MacOSWebAuthnService() 232 : mTransactionState(Nothing(), 233 "MacOSWebAuthnService::mTransactionState") {} 234 235 NS_DECL_THREADSAFE_ISUPPORTS 236 NS_DECL_NSIWEBAUTHNSERVICE 237 238 void FinishMakeCredential(const nsTArray<uint8_t>& aRawAttestationObject, 239 const nsTArray<uint8_t>& aCredentialId, 240 const nsTArray<nsString>& aTransports, 241 const Maybe<nsString>& aAuthenticatorAttachment, 242 const Maybe<bool>& aLargeBlobSupported, 243 const Maybe<bool>& aPrfSupported, 244 const Maybe<nsTArray<uint8_t>>& aPrfFirst, 245 const Maybe<nsTArray<uint8_t>>& aPrfSecond); 246 247 void FinishGetAssertion(const nsTArray<uint8_t>& aCredentialId, 248 const nsTArray<uint8_t>& aSignature, 249 const nsTArray<uint8_t>& aAuthenticatorData, 250 const nsTArray<uint8_t>& aUserHandle, 251 const Maybe<nsString>& aAuthenticatorAttachment, 252 const Maybe<bool>& aUsedAppId, 253 const Maybe<nsTArray<uint8_t>>& aLargeBlobValue, 254 const Maybe<bool>& aLargeBlobWritten, 255 const Maybe<nsTArray<uint8_t>>& aPrfFirst, 256 const Maybe<nsTArray<uint8_t>>& aPrfSecond); 257 void ReleasePlatformResources(); 258 void AbortTransaction(nsresult aError); 259 260 private: 261 ~MacOSWebAuthnService() = default; 262 263 void PerformRequests(NSArray<ASAuthorizationRequest*>* aRequests, 264 nsTArray<uint8_t>&& aClientDataHash, 265 nsTArray<nsTArray<uint8_t>>&& aCredentialList, 266 nsTArray<uint8_t>&& aCredentialListTransports, 267 uint64_t aBrowsingContextId); 268 269 struct TransactionState { 270 uint64_t transactionId; 271 uint64_t browsingContextId; 272 Maybe<RefPtr<nsIWebAuthnSignArgs>> pendingSignArgs; 273 Maybe<RefPtr<nsIWebAuthnSignPromise>> pendingSignPromise; 274 Maybe<nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>> autoFillEntries; 275 }; 276 277 using TransactionStateMutex = DataMutex<Maybe<TransactionState>>; 278 void DoGetAssertion(Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId, 279 const TransactionStateMutex::AutoLock& aGuard); 280 281 TransactionStateMutex mTransactionState; 282 283 // Main thread only: 284 ASAuthorizationWebBrowserPublicKeyCredentialManager* mCredentialManager = nil; 285 nsCOMPtr<nsIWebAuthnRegisterPromise> mRegisterPromise; 286 nsCOMPtr<nsIWebAuthnSignPromise> mSignPromise; 287 MacOSAuthorizationController* mAuthorizationController = nil; 288 MacOSAuthenticatorRequestDelegate* mRequestDelegate = nil; 289 MacOSAuthenticatorPresentationContextProvider* mPresentationContextProvider = 290 nil; 291 }; 292 #pragma clang diagnostic pop 293 294 } // namespace mozilla::dom 295 296 nsTArray<uint8_t> NSDataToArray(NSData* data) { 297 nsTArray<uint8_t> array(reinterpret_cast<const uint8_t*>(data.bytes), 298 data.length); 299 return array; 300 } 301 302 API_AVAILABLE(macos(15.0)) 303 NSDictionary<NSData*, ASAuthorizationPublicKeyCredentialPRFAssertionInputValues*>* _Nullable ConstructPrfEvalByCredentialEntries( 304 const RefPtr<nsIWebAuthnSignArgs>& aArgs) { 305 nsTArray<nsTArray<uint8_t>> prfEvalByCredIds; 306 nsTArray<nsTArray<uint8_t>> prfEvalByCredFirsts; 307 nsTArray<bool> prfEvalByCredSecondMaybes; 308 nsTArray<nsTArray<uint8_t>> prfEvalByCredSeconds; 309 if (NS_FAILED(aArgs->GetPrfEvalByCredentialCredentialId(prfEvalByCredIds)) || 310 NS_FAILED(aArgs->GetPrfEvalByCredentialEvalFirst(prfEvalByCredFirsts)) || 311 NS_FAILED(aArgs->GetPrfEvalByCredentialEvalSecondMaybe( 312 prfEvalByCredSecondMaybes)) || 313 NS_FAILED( 314 aArgs->GetPrfEvalByCredentialEvalSecond(prfEvalByCredSeconds)) || 315 prfEvalByCredIds.Length() != prfEvalByCredFirsts.Length() || 316 prfEvalByCredIds.Length() != prfEvalByCredSecondMaybes.Length() || 317 prfEvalByCredIds.Length() != prfEvalByCredSeconds.Length()) { 318 return nil; 319 } 320 321 uint32_t count = prfEvalByCredIds.Length(); 322 NSData* keys[count]; 323 ASAuthorizationPublicKeyCredentialPRFAssertionInputValues* objects[count]; 324 for (size_t i = 0; i < count; i++) { 325 NSData* saltInput1 = [NSData dataWithBytes:prfEvalByCredFirsts[i].Elements() 326 length:prfEvalByCredFirsts[i].Length()]; 327 NSData* saltInput2 = nil; 328 if (prfEvalByCredSecondMaybes[i]) { 329 saltInput2 = [NSData dataWithBytes:prfEvalByCredSeconds[i].Elements() 330 length:prfEvalByCredSeconds[i].Length()]; 331 } 332 keys[i] = [NSData dataWithBytes:prfEvalByCredIds[i].Elements() 333 length:prfEvalByCredIds[i].Length()]; 334 objects[i] = 335 [[ASAuthorizationPublicKeyCredentialPRFAssertionInputValues alloc] 336 initWithSaltInput1:saltInput1 337 saltInput2:saltInput2]; 338 } 339 340 return [NSDictionary dictionaryWithObjects:objects forKeys:keys count:count]; 341 } 342 343 @implementation MacOSAuthenticatorRequestDelegate { 344 RefPtr<mozilla::dom::MacOSWebAuthnService> mCallback; 345 } 346 347 - (void)setCallback:(mozilla::dom::MacOSWebAuthnService*)callback { 348 mCallback = callback; 349 } 350 351 - (void)authorizationController:(ASAuthorizationController*)controller 352 didCompleteWithAuthorization:(ASAuthorization*)authorization { 353 if ([authorization.credential 354 conformsToProtocol: 355 @protocol(ASAuthorizationPublicKeyCredentialRegistration)]) { 356 MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug, 357 ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: " 358 "got registration")); 359 id<ASAuthorizationPublicKeyCredentialRegistration> credential = 360 (id<ASAuthorizationPublicKeyCredentialRegistration>) 361 authorization.credential; 362 nsTArray<uint8_t> rawAttestationObject( 363 NSDataToArray(credential.rawAttestationObject)); 364 nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID)); 365 nsTArray<nsString> transports; 366 mozilla::Maybe<nsString> authenticatorAttachment; 367 mozilla::Maybe<bool> largeBlobSupported; 368 mozilla::Maybe<bool> prfSupported; 369 mozilla::Maybe<nsTArray<uint8_t>> prfFirst; 370 mozilla::Maybe<nsTArray<uint8_t>> prfSecond; 371 if ([credential isKindOfClass: 372 [ASAuthorizationPlatformPublicKeyCredentialRegistration 373 class]]) { 374 transports.AppendElement(u"hybrid"_ns); 375 transports.AppendElement(u"internal"_ns); 376 ASAuthorizationPlatformPublicKeyCredentialRegistration* 377 platformCredential = 378 (ASAuthorizationPlatformPublicKeyCredentialRegistration*) 379 credential; 380 if (__builtin_available(macos 13.5, *)) { 381 switch (platformCredential.attachment) { 382 case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform: 383 authenticatorAttachment.emplace(u"cross-platform"_ns); 384 break; 385 case ASAuthorizationPublicKeyCredentialAttachmentPlatform: 386 authenticatorAttachment.emplace(u"platform"_ns); 387 break; 388 default: 389 break; 390 } 391 } 392 if (__builtin_available(macos 14.0, *)) { 393 if (platformCredential.largeBlob) { 394 largeBlobSupported.emplace(platformCredential.largeBlob.isSupported); 395 } 396 } 397 if (__builtin_available(macos 15.0, *)) { 398 if (platformCredential.prf) { 399 prfSupported.emplace(platformCredential.prf.isSupported); 400 if (platformCredential.prf.first) { 401 prfFirst.emplace(NSDataToArray(platformCredential.prf.first)); 402 } 403 if (platformCredential.prf.second) { 404 prfSecond.emplace(NSDataToArray(platformCredential.prf.second)); 405 } 406 } 407 } 408 } else { 409 // The platform didn't tell us what transport was used, but we know it 410 // wasn't the internal transport. The transport response is not signed by 411 // the authenticator. It represents the "transports that the authenticator 412 // is believed to support, or an empty sequence if the information is 413 // unavailable". We believe macOS supports usb, so we return usb. 414 transports.AppendElement(u"usb"_ns); 415 authenticatorAttachment.emplace(u"cross-platform"_ns); 416 } 417 mCallback->FinishMakeCredential( 418 rawAttestationObject, credentialId, transports, authenticatorAttachment, 419 largeBlobSupported, prfSupported, prfFirst, prfSecond); 420 } else if ([authorization.credential 421 conformsToProtocol: 422 @protocol(ASAuthorizationPublicKeyCredentialAssertion)]) { 423 MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug, 424 ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: " 425 "got assertion")); 426 id<ASAuthorizationPublicKeyCredentialAssertion> credential = 427 (id<ASAuthorizationPublicKeyCredentialAssertion>) 428 authorization.credential; 429 nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID)); 430 nsTArray<uint8_t> signature(NSDataToArray(credential.signature)); 431 nsTArray<uint8_t> rawAuthenticatorData( 432 NSDataToArray(credential.rawAuthenticatorData)); 433 nsTArray<uint8_t> userHandle(NSDataToArray(credential.userID)); 434 mozilla::Maybe<nsString> authenticatorAttachment; 435 mozilla::Maybe<bool> usedAppId; 436 mozilla::Maybe<nsTArray<uint8_t>> largeBlobValue; 437 mozilla::Maybe<bool> largeBlobWritten; 438 mozilla::Maybe<nsTArray<uint8_t>> prfFirst; 439 mozilla::Maybe<nsTArray<uint8_t>> prfSecond; 440 if ([credential 441 isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion 442 class]]) { 443 ASAuthorizationPlatformPublicKeyCredentialAssertion* platformCredential = 444 (ASAuthorizationPlatformPublicKeyCredentialAssertion*)credential; 445 if (__builtin_available(macos 13.5, *)) { 446 switch (platformCredential.attachment) { 447 case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform: 448 authenticatorAttachment.emplace(u"cross-platform"_ns); 449 break; 450 case ASAuthorizationPublicKeyCredentialAttachmentPlatform: 451 authenticatorAttachment.emplace(u"platform"_ns); 452 break; 453 default: 454 break; 455 } 456 } 457 if (__builtin_available(macos 14.0, *)) { 458 if (platformCredential.largeBlob) { 459 if (platformCredential.largeBlob.readData) { 460 largeBlobValue.emplace( 461 NSDataToArray(platformCredential.largeBlob.readData)); 462 } else { 463 largeBlobWritten.emplace(platformCredential.largeBlob.didWrite); 464 } 465 } 466 } 467 if (__builtin_available(macos 15.0, *)) { 468 if (platformCredential.prf) { 469 if (platformCredential.prf.first) { 470 prfFirst.emplace(NSDataToArray(platformCredential.prf.first)); 471 } 472 if (platformCredential.prf.second) { 473 prfSecond.emplace(NSDataToArray(platformCredential.prf.second)); 474 } 475 } 476 } 477 } else if ([credential 478 isKindOfClass: 479 [ASAuthorizationSecurityKeyPublicKeyCredentialAssertion 480 class]]) { 481 ASAuthorizationSecurityKeyPublicKeyCredentialAssertion* 482 securityKeyCredential = 483 (ASAuthorizationSecurityKeyPublicKeyCredentialAssertion*) 484 credential; 485 if (__builtin_available(macos 14.5, *)) { 486 usedAppId.emplace(securityKeyCredential.appID); 487 } 488 authenticatorAttachment.emplace(u"cross-platform"_ns); 489 } 490 mCallback->FinishGetAssertion(credentialId, signature, rawAuthenticatorData, 491 userHandle, authenticatorAttachment, 492 usedAppId, largeBlobValue, largeBlobWritten, 493 prfFirst, prfSecond); 494 } else { 495 MOZ_LOG( 496 gMacOSWebAuthnServiceLog, mozilla::LogLevel::Error, 497 ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: " 498 "authorization.credential is neither registration nor assertion!")); 499 MOZ_ASSERT_UNREACHABLE( 500 "should have ASAuthorizationPublicKeyCredentialRegistration or " 501 "ASAuthorizationPublicKeyCredentialAssertion"); 502 } 503 mCallback->ReleasePlatformResources(); 504 mCallback = nullptr; 505 } 506 507 - (void)authorizationController:(ASAuthorizationController*)controller 508 didCompleteWithError:(NSError*)error { 509 nsAutoString errorDescription; 510 nsCocoaUtils::GetStringForNSString(error.localizedDescription, 511 errorDescription); 512 nsAutoString errorDomain; 513 nsCocoaUtils::GetStringForNSString(error.domain, errorDomain); 514 MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Warning, 515 ("MacOSAuthenticatorRequestDelegate::didCompleteWithError: domain " 516 "'%s' code %ld (%s)", 517 NS_ConvertUTF16toUTF8(errorDomain).get(), error.code, 518 NS_ConvertUTF16toUTF8(errorDescription).get())); 519 nsresult rv = NS_ERROR_DOM_NOT_ALLOWED_ERR; 520 // For some reason, the error for "the credential used in a registration was 521 // on the exclude list" is in the "WKErrorDomain" domain with code 8, which 522 // is presumably WKErrorDuplicateCredential. 523 const NSInteger WKErrorDuplicateCredential = 8; 524 if (errorDomain.EqualsLiteral("WKErrorDomain") && 525 error.code == WKErrorDuplicateCredential) { 526 rv = NS_ERROR_DOM_INVALID_STATE_ERR; 527 } else if (error.domain == ASAuthorizationErrorDomain) { 528 switch (error.code) { 529 case ASAuthorizationErrorCanceled: 530 rv = NS_ERROR_DOM_NOT_ALLOWED_ERR; 531 break; 532 case ASAuthorizationErrorFailed: 533 // The message is right, but it's not about indexeddb. 534 // See https://webidl.spec.whatwg.org/#constrainterror 535 rv = NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR; 536 break; 537 case ASAuthorizationErrorUnknown: 538 rv = NS_ERROR_DOM_UNKNOWN_ERR; 539 break; 540 default: 541 // rv already has a default value 542 break; 543 } 544 } 545 mCallback->AbortTransaction(rv); 546 mCallback = nullptr; 547 } 548 @end 549 550 @implementation MacOSAuthenticatorPresentationContextProvider 551 @synthesize window = window; 552 553 - (ASPresentationAnchor)presentationAnchorForAuthorizationController: 554 (ASAuthorizationController*)controller { 555 return window; 556 } 557 @end 558 559 namespace mozilla::dom { 560 561 #pragma clang diagnostic push 562 #pragma clang diagnostic ignored "-Wnullability-completeness" 563 // Given a browsingContextId, attempts to determine the NSWindow associated 564 // with that browser. 565 nsresult BrowsingContextIdToNSWindow(uint64_t browsingContextId, 566 NSWindow** window) { 567 *window = nullptr; 568 RefPtr<BrowsingContext> browsingContext( 569 BrowsingContext::Get(browsingContextId)); 570 if (!browsingContext) { 571 return NS_ERROR_NOT_AVAILABLE; 572 } 573 CanonicalBrowsingContext* canonicalBrowsingContext = 574 browsingContext->Canonical(); 575 if (!canonicalBrowsingContext) { 576 return NS_ERROR_NOT_AVAILABLE; 577 } 578 nsCOMPtr<nsIWidget> widget( 579 canonicalBrowsingContext->GetParentProcessWidgetContaining()); 580 if (!widget) { 581 return NS_ERROR_NOT_AVAILABLE; 582 } 583 *window = static_cast<NSWindow*>(widget->GetNativeData(NS_NATIVE_WINDOW)); 584 return NS_OK; 585 } 586 #pragma clang diagnostic pop 587 588 already_AddRefed<nsIWebAuthnService> NewMacOSWebAuthnServiceIfAvailable() { 589 MOZ_ASSERT(XRE_IsParentProcess()); 590 if (!StaticPrefs::security_webauthn_enable_macos_passkeys()) { 591 MOZ_LOG( 592 gMacOSWebAuthnServiceLog, LogLevel::Debug, 593 ("macOS platform support for webauthn (passkeys) disabled by pref")); 594 return nullptr; 595 } 596 // This code checks for the entitlement 597 // 'com.apple.developer.web-browser.public-key-credential', which must be 598 // true to be able to use the platform APIs. 599 CFTypeRefPtr<SecTaskRef> entitlementTask( 600 CFTypeRefPtr<SecTaskRef>::WrapUnderCreateRule( 601 SecTaskCreateFromSelf(nullptr))); 602 CFTypeRefPtr<CFBooleanRef> entitlementValue( 603 CFTypeRefPtr<CFBooleanRef>::WrapUnderCreateRule( 604 reinterpret_cast<CFBooleanRef>(SecTaskCopyValueForEntitlement( 605 entitlementTask.get(), 606 CFSTR("com.apple.developer.web-browser.public-key-credential"), 607 nullptr)))); 608 if (!entitlementValue || !CFBooleanGetValue(entitlementValue.get())) { 609 MOZ_LOG( 610 gMacOSWebAuthnServiceLog, LogLevel::Warning, 611 ("entitlement com.apple.developer.web-browser.public-key-credential " 612 "not present: platform passkey APIs will not be available")); 613 return nullptr; 614 } 615 nsCOMPtr<nsIWebAuthnService> service(new MacOSWebAuthnService()); 616 return service.forget(); 617 } 618 619 void MacOSWebAuthnService::AbortTransaction(nsresult aError) { 620 MOZ_ASSERT(NS_IsMainThread()); 621 if (mRegisterPromise) { 622 (void)mRegisterPromise->Reject(aError); 623 mRegisterPromise = nullptr; 624 } 625 if (mSignPromise) { 626 (void)mSignPromise->Reject(aError); 627 mSignPromise = nullptr; 628 } 629 ReleasePlatformResources(); 630 } 631 632 #pragma clang diagnostic push 633 #pragma clang diagnostic ignored "-Wnullability-completeness" 634 NS_IMPL_ISUPPORTS(MacOSWebAuthnService, nsIWebAuthnService) 635 #pragma clang diagnostic pop 636 637 NS_IMETHODIMP 638 MacOSWebAuthnService::MakeCredential(uint64_t aTransactionId, 639 uint64_t aBrowsingContextId, 640 nsIWebAuthnRegisterArgs* aArgs, 641 nsIWebAuthnRegisterPromise* aPromise) { 642 Reset(); 643 auto guard = mTransactionState.Lock(); 644 *guard = Some(TransactionState{ 645 aTransactionId, 646 aBrowsingContextId, 647 Nothing(), 648 Nothing(), 649 Nothing(), 650 }); 651 NS_DispatchToMainThread(NS_NewRunnableFunction( 652 "MacOSWebAuthnService::MakeCredential", 653 [self = RefPtr{this}, browsingContextId(aBrowsingContextId), 654 aArgs = nsCOMPtr{aArgs}, aPromise = nsCOMPtr{aPromise}]() { 655 // Bug 1884574 - The Reset() call above should have cancelled any 656 // transactions that were dispatched to the platform, the platform 657 // should have called didCompleteWithError, and didCompleteWithError 658 // should have rejected the pending promise. However, in some scenarios, 659 // the platform fails to call the callback, and this leads to a 660 // diagnostic assertion failure when we drop `mRegisterPromise`. Avoid 661 // this by aborting the transaction here. 662 if (self->mRegisterPromise) { 663 MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug, 664 ("MacOSAuthenticatorRequestDelegate::MakeCredential: " 665 "platform failed to call callback")); 666 self->AbortTransaction(NS_ERROR_DOM_ABORT_ERR); 667 } 668 self->mRegisterPromise = aPromise; 669 670 nsAutoString rpId; 671 (void)aArgs->GetRpId(rpId); 672 NSString* rpIdNS = nsCocoaUtils::ToNSString(rpId); 673 674 nsTArray<uint8_t> challenge; 675 (void)aArgs->GetChallenge(challenge); 676 NSData* challengeNS = [NSData dataWithBytes:challenge.Elements() 677 length:challenge.Length()]; 678 679 nsTArray<uint8_t> userId; 680 (void)aArgs->GetUserId(userId); 681 NSData* userIdNS = [NSData dataWithBytes:userId.Elements() 682 length:userId.Length()]; 683 684 nsAutoString userName; 685 (void)aArgs->GetUserName(userName); 686 NSString* userNameNS = nsCocoaUtils::ToNSString(userName); 687 688 nsAutoString userDisplayName; 689 (void)aArgs->GetUserDisplayName(userDisplayName); 690 NSString* userDisplayNameNS = nsCocoaUtils::ToNSString(userDisplayName); 691 692 nsTArray<int32_t> coseAlgs; 693 (void)aArgs->GetCoseAlgs(coseAlgs); 694 NSMutableArray* credentialParameters = [[NSMutableArray alloc] init]; 695 for (const auto& coseAlg : coseAlgs) { 696 ASAuthorizationPublicKeyCredentialParameters* credentialParameter = 697 [[ASAuthorizationPublicKeyCredentialParameters alloc] 698 initWithAlgorithm:coseAlg]; 699 [credentialParameters addObject:credentialParameter]; 700 } 701 702 nsTArray<nsTArray<uint8_t>> excludeList; 703 (void)aArgs->GetExcludeList(excludeList); 704 nsTArray<uint8_t> excludeListTransports; 705 (void)aArgs->GetExcludeListTransports(excludeListTransports); 706 if (excludeList.Length() != excludeListTransports.Length()) { 707 self->mRegisterPromise->Reject(NS_ERROR_INVALID_ARG); 708 return; 709 } 710 711 Maybe<ASAuthorizationPublicKeyCredentialUserVerificationPreference> 712 userVerificationPreference = Nothing(); 713 nsAutoString userVerification; 714 (void)aArgs->GetUserVerification(userVerification); 715 // This mapping needs to be reviewed if values are added to the 716 // UserVerificationRequirement enum. 717 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 718 if (userVerification.EqualsLiteral( 719 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { 720 userVerificationPreference.emplace( 721 ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired); 722 } else if (userVerification.EqualsLiteral( 723 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { 724 userVerificationPreference.emplace( 725 ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred); 726 } else if ( 727 userVerification.EqualsLiteral( 728 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) { 729 userVerificationPreference.emplace( 730 ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged); 731 } 732 733 // The API doesn't support attestation for platform passkeys, so this is 734 // only used for security keys. 735 ASAuthorizationPublicKeyCredentialAttestationKind attestationPreference; 736 nsAutoString mozAttestationPreference; 737 (void)aArgs->GetAttestationConveyancePreference( 738 mozAttestationPreference); 739 if (mozAttestationPreference.EqualsLiteral( 740 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT)) { 741 attestationPreference = 742 ASAuthorizationPublicKeyCredentialAttestationKindIndirect; 743 } else if (mozAttestationPreference.EqualsLiteral( 744 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT)) { 745 attestationPreference = 746 ASAuthorizationPublicKeyCredentialAttestationKindDirect; 747 } else if ( 748 mozAttestationPreference.EqualsLiteral( 749 MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ENTERPRISE)) { 750 attestationPreference = 751 ASAuthorizationPublicKeyCredentialAttestationKindEnterprise; 752 } else { 753 attestationPreference = 754 ASAuthorizationPublicKeyCredentialAttestationKindNone; 755 } 756 757 ASAuthorizationPublicKeyCredentialResidentKeyPreference 758 residentKeyPreference; 759 nsAutoString mozResidentKey; 760 (void)aArgs->GetResidentKey(mozResidentKey); 761 // This mapping needs to be reviewed if values are added to the 762 // ResidentKeyRequirement enum. 763 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 764 if (mozResidentKey.EqualsLiteral( 765 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) { 766 residentKeyPreference = 767 ASAuthorizationPublicKeyCredentialResidentKeyPreferenceRequired; 768 } else if (mozResidentKey.EqualsLiteral( 769 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED)) { 770 residentKeyPreference = 771 ASAuthorizationPublicKeyCredentialResidentKeyPreferencePreferred; 772 } else { 773 MOZ_ASSERT(mozResidentKey.EqualsLiteral( 774 MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED)); 775 residentKeyPreference = 776 ASAuthorizationPublicKeyCredentialResidentKeyPreferenceDiscouraged; 777 } 778 779 // Initialize the platform provider with the rpId. 780 ASAuthorizationPlatformPublicKeyCredentialProvider* platformProvider = 781 [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc] 782 initWithRelyingPartyIdentifier:rpIdNS]; 783 // Make a credential registration request with the challenge, userName, 784 // and userId. 785 ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest* 786 platformRegistrationRequest = [platformProvider 787 createCredentialRegistrationRequestWithChallenge:challengeNS 788 name:userNameNS 789 userID:userIdNS]; 790 [platformProvider release]; 791 792 // The API doesn't support attestation for platform passkeys 793 platformRegistrationRequest.attestationPreference = 794 ASAuthorizationPublicKeyCredentialAttestationKindNone; 795 if (userVerificationPreference.isSome()) { 796 platformRegistrationRequest.userVerificationPreference = 797 *userVerificationPreference; 798 } 799 800 // Initialize the cross-platform provider with the rpId. 801 ASAuthorizationSecurityKeyPublicKeyCredentialProvider* 802 crossPlatformProvider = 803 [[ASAuthorizationSecurityKeyPublicKeyCredentialProvider alloc] 804 initWithRelyingPartyIdentifier:rpIdNS]; 805 // Make a credential registration request with the challenge, 806 // userDisplayName, userName, and userId. 807 ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequest* 808 crossPlatformRegistrationRequest = [crossPlatformProvider 809 createCredentialRegistrationRequestWithChallenge:challengeNS 810 displayName: 811 userDisplayNameNS 812 name:userNameNS 813 userID:userIdNS]; 814 [crossPlatformProvider release]; 815 crossPlatformRegistrationRequest.attestationPreference = 816 attestationPreference; 817 crossPlatformRegistrationRequest.credentialParameters = 818 credentialParameters; 819 crossPlatformRegistrationRequest.residentKeyPreference = 820 residentKeyPreference; 821 if (userVerificationPreference.isSome()) { 822 crossPlatformRegistrationRequest.userVerificationPreference = 823 *userVerificationPreference; 824 } 825 826 if (__builtin_available(macos 13.5, *)) { 827 // Show the hybrid transport unless we have a non-empty hint list and 828 // none of the hints are for the hybrid transport. 829 bool hasHybridHint = false; 830 nsTArray<nsString> hints; 831 (void)aArgs->GetHints(hints); 832 for (nsString& hint : hints) { 833 if (hint.Equals(u"hybrid"_ns)) { 834 hasHybridHint = true; 835 } 836 } 837 platformRegistrationRequest.shouldShowHybridTransport = 838 hints.Length() == 0 || hasHybridHint; 839 } 840 if (__builtin_available(macos 14.0, *)) { 841 bool largeBlobSupportRequired; 842 nsresult rv = 843 aArgs->GetLargeBlobSupportRequired(&largeBlobSupportRequired); 844 if (rv != NS_ERROR_NOT_AVAILABLE) { 845 if (NS_FAILED(rv)) { 846 self->mRegisterPromise->Reject(rv); 847 return; 848 } 849 ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirement 850 largeBlobRequirement = 851 largeBlobSupportRequired 852 ? ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirementRequired 853 : ASAuthorizationPublicKeyCredentialLargeBlobSupportRequirementPreferred; 854 platformRegistrationRequest.largeBlob = 855 [[ASAuthorizationPublicKeyCredentialLargeBlobRegistrationInput 856 alloc] initWithSupportRequirement:largeBlobRequirement]; 857 } 858 } 859 if (__builtin_available(macos 15.0, *)) { 860 bool requestedPrf; 861 (void)aArgs->GetPrf(&requestedPrf); 862 if (requestedPrf) { 863 NSData* saltInput1 = nil; 864 NSData* saltInput2 = nil; 865 nsTArray<uint8_t> prfInput1; 866 nsresult rv = aArgs->GetPrfEvalFirst(prfInput1); 867 if (rv != NS_ERROR_NOT_AVAILABLE) { 868 if (NS_FAILED(rv)) { 869 self->mRegisterPromise->Reject(rv); 870 return; 871 } 872 saltInput1 = [NSData dataWithBytes:prfInput1.Elements() 873 length:prfInput1.Length()]; 874 } 875 nsTArray<uint8_t> prfInput2; 876 rv = aArgs->GetPrfEvalSecond(prfInput2); 877 if (rv != NS_ERROR_NOT_AVAILABLE) { 878 if (NS_FAILED(rv)) { 879 self->mRegisterPromise->Reject(rv); 880 return; 881 } 882 saltInput2 = [NSData dataWithBytes:prfInput2.Elements() 883 length:prfInput2.Length()]; 884 } 885 ASAuthorizationPublicKeyCredentialPRFAssertionInputValues* 886 prfInputs = 887 [[ASAuthorizationPublicKeyCredentialPRFAssertionInputValues 888 alloc] initWithSaltInput1:saltInput1 889 saltInput2:saltInput2]; 890 platformRegistrationRequest.prf = 891 [[ASAuthorizationPublicKeyCredentialPRFRegistrationInput alloc] 892 initWithInputValues:prfInputs]; 893 } 894 } 895 896 nsTArray<uint8_t> clientDataHash; 897 nsresult rv = aArgs->GetClientDataHash(clientDataHash); 898 if (NS_FAILED(rv)) { 899 self->mRegisterPromise->Reject(rv); 900 return; 901 } 902 nsAutoString authenticatorAttachment; 903 rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment); 904 if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) { 905 self->mRegisterPromise->Reject(rv); 906 return; 907 } 908 NSMutableArray* requests = [[NSMutableArray alloc] init]; 909 if (authenticatorAttachment.EqualsLiteral( 910 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) { 911 [requests addObject:platformRegistrationRequest]; 912 } else if (authenticatorAttachment.EqualsLiteral( 913 MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) { 914 [requests addObject:crossPlatformRegistrationRequest]; 915 } else { 916 // Regarding the value of authenticator attachment, according to the 917 // specification, "client platforms MUST ignore unknown values, 918 // treating an unknown value as if the member does not exist". 919 [requests addObject:platformRegistrationRequest]; 920 [requests addObject:crossPlatformRegistrationRequest]; 921 } 922 self->PerformRequests( 923 requests, std::move(clientDataHash), std::move(excludeList), 924 std::move(excludeListTransports), browsingContextId); 925 })); 926 return NS_OK; 927 } 928 929 void MacOSWebAuthnService::PerformRequests( 930 NSArray<ASAuthorizationRequest*>* aRequests, 931 nsTArray<uint8_t>&& aClientDataHash, 932 nsTArray<nsTArray<uint8_t>>&& aCredentialList, 933 nsTArray<uint8_t>&& aCredentialListTransports, 934 uint64_t aBrowsingContextId) { 935 MOZ_ASSERT(NS_IsMainThread()); 936 // Create a MacOSAuthorizationController and initialize it with the requests. 937 MOZ_ASSERT(!mAuthorizationController); 938 mAuthorizationController = [[MacOSAuthorizationController alloc] 939 initWithAuthorizationRequests:aRequests]; 940 [mAuthorizationController 941 stashClientDataHash:std::move(aClientDataHash) 942 andCredentialList:std::move(aCredentialList) 943 andCredentialListTransports:std::move(aCredentialListTransports)]; 944 945 // Set up the delegate to run when the operation completes. 946 MOZ_ASSERT(!mRequestDelegate); 947 mRequestDelegate = [[MacOSAuthenticatorRequestDelegate alloc] init]; 948 [mRequestDelegate setCallback:this]; 949 mAuthorizationController.delegate = mRequestDelegate; 950 951 // Create a presentation context provider so the API knows which window 952 // made the request. 953 NSWindow* window = nullptr; 954 nsresult rv = BrowsingContextIdToNSWindow(aBrowsingContextId, &window); 955 if (NS_FAILED(rv)) { 956 AbortTransaction(NS_ERROR_DOM_INVALID_STATE_ERR); 957 return; 958 } 959 MOZ_ASSERT(!mPresentationContextProvider); 960 mPresentationContextProvider = 961 [[MacOSAuthenticatorPresentationContextProvider alloc] init]; 962 mPresentationContextProvider.window = window; 963 mAuthorizationController.presentationContextProvider = 964 mPresentationContextProvider; 965 966 // Finally, perform the request. 967 [mAuthorizationController performRequests]; 968 } 969 970 void MacOSWebAuthnService::FinishMakeCredential( 971 const nsTArray<uint8_t>& aRawAttestationObject, 972 const nsTArray<uint8_t>& aCredentialId, 973 const nsTArray<nsString>& aTransports, 974 const Maybe<nsString>& aAuthenticatorAttachment, 975 const Maybe<bool>& aLargeBlobSupported, const Maybe<bool>& aPrfSupported, 976 const Maybe<nsTArray<uint8_t>>& aPrfFirst, 977 const Maybe<nsTArray<uint8_t>>& aPrfSecond) { 978 MOZ_ASSERT(NS_IsMainThread()); 979 if (!mRegisterPromise) { 980 return; 981 } 982 983 RefPtr<WebAuthnRegisterResult> result(new WebAuthnRegisterResult( 984 aRawAttestationObject, Nothing(), aCredentialId, aTransports, 985 aAuthenticatorAttachment, aLargeBlobSupported, aPrfSupported, aPrfFirst, 986 aPrfSecond)); 987 (void)mRegisterPromise->Resolve(result); 988 mRegisterPromise = nullptr; 989 } 990 991 NS_IMETHODIMP 992 MacOSWebAuthnService::GetAssertion(uint64_t aTransactionId, 993 uint64_t aBrowsingContextId, 994 nsIWebAuthnSignArgs* aArgs, 995 nsIWebAuthnSignPromise* aPromise) { 996 Reset(); 997 998 auto guard = mTransactionState.Lock(); 999 *guard = Some(TransactionState{ 1000 aTransactionId, 1001 aBrowsingContextId, 1002 Some(RefPtr{aArgs}), 1003 Some(RefPtr{aPromise}), 1004 Nothing(), 1005 }); 1006 1007 bool conditionallyMediated; 1008 (void)aArgs->GetConditionallyMediated(&conditionallyMediated); 1009 if (!conditionallyMediated) { 1010 DoGetAssertion(Nothing(), guard); 1011 return NS_OK; 1012 } 1013 1014 // Using conditional mediation, so dispatch a task to collect any available 1015 // passkeys. 1016 NS_DispatchToMainThread(NS_NewRunnableFunction( 1017 "platformCredentialsForRelyingParty", 1018 [self = RefPtr{this}, aTransactionId, aArgs = nsCOMPtr{aArgs}]() { 1019 // This handler is called when platformCredentialsForRelyingParty 1020 // completes. 1021 auto credentialsCompletionHandler = ^( 1022 NSArray<ASAuthorizationWebBrowserPlatformPublicKeyCredential*>* 1023 credentials) { 1024 nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>> autoFillEntries; 1025 for (NSUInteger i = 0; i < credentials.count; i++) { 1026 const auto& credential = credentials[i]; 1027 nsAutoString userName; 1028 nsCocoaUtils::GetStringForNSString(credential.name, userName); 1029 nsAutoString rpId; 1030 nsCocoaUtils::GetStringForNSString(credential.relyingParty, rpId); 1031 autoFillEntries.AppendElement(new WebAuthnAutoFillEntry( 1032 nsIWebAuthnAutoFillEntry::PROVIDER_PLATFORM_MACOS, userName, 1033 rpId, NSDataToArray(credential.credentialID))); 1034 } 1035 auto guard = self->mTransactionState.Lock(); 1036 if (guard->isSome() && guard->ref().transactionId == aTransactionId) { 1037 guard->ref().autoFillEntries.emplace(std::move(autoFillEntries)); 1038 } 1039 }; 1040 // This handler is called when 1041 // requestAuthorizationForPublicKeyCredentials completes. 1042 auto authorizationHandler = ^( 1043 ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState 1044 authorizationState) { 1045 // If authorized, list any available passkeys. 1046 if (authorizationState == 1047 ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationStateAuthorized) { 1048 nsAutoString rpId; 1049 (void)aArgs->GetRpId(rpId); 1050 [self->mCredentialManager 1051 platformCredentialsForRelyingParty:nsCocoaUtils::ToNSString( 1052 rpId) 1053 completionHandler: 1054 credentialsCompletionHandler]; 1055 } 1056 }; 1057 if (!self->mCredentialManager) { 1058 self->mCredentialManager = 1059 [[ASAuthorizationWebBrowserPublicKeyCredentialManager alloc] 1060 init]; 1061 } 1062 // Request authorization to examine any available passkeys. This will 1063 // cause a permission prompt to appear once. 1064 [self->mCredentialManager 1065 requestAuthorizationForPublicKeyCredentials:authorizationHandler]; 1066 })); 1067 1068 return NS_OK; 1069 } 1070 1071 void MacOSWebAuthnService::DoGetAssertion( 1072 Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId, 1073 const TransactionStateMutex::AutoLock& aGuard) { 1074 if (aGuard->isNothing() || aGuard->ref().pendingSignArgs.isNothing() || 1075 aGuard->ref().pendingSignPromise.isNothing()) { 1076 return; 1077 } 1078 1079 // Take the pending Args and Promise to prevent repeated calls to 1080 // DoGetAssertion for this transaction. 1081 RefPtr<nsIWebAuthnSignArgs> aArgs = aGuard->ref().pendingSignArgs.extract(); 1082 RefPtr<nsIWebAuthnSignPromise> aPromise = 1083 aGuard->ref().pendingSignPromise.extract(); 1084 uint64_t aBrowsingContextId = aGuard->ref().browsingContextId; 1085 1086 NS_DispatchToMainThread(NS_NewRunnableFunction( 1087 "MacOSWebAuthnService::MakeCredential", 1088 [self = RefPtr{this}, browsingContextId(aBrowsingContextId), aArgs, 1089 aPromise, 1090 aSelectedCredentialId = std::move(aSelectedCredentialId)]() mutable { 1091 // Bug 1884574 - This AbortTransaction call is necessary. 1092 // See comment in MacOSWebAuthnService::MakeCredential. 1093 if (self->mSignPromise) { 1094 MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug, 1095 ("MacOSAuthenticatorRequestDelegate::DoGetAssertion: " 1096 "platform failed to call callback")); 1097 self->AbortTransaction(NS_ERROR_DOM_ABORT_ERR); 1098 } 1099 self->mSignPromise = aPromise; 1100 1101 nsAutoString rpId; 1102 (void)aArgs->GetRpId(rpId); 1103 NSString* rpIdNS = nsCocoaUtils::ToNSString(rpId); 1104 1105 nsTArray<uint8_t> challenge; 1106 (void)aArgs->GetChallenge(challenge); 1107 NSData* challengeNS = [NSData dataWithBytes:challenge.Elements() 1108 length:challenge.Length()]; 1109 1110 nsTArray<nsTArray<uint8_t>> allowList; 1111 nsTArray<uint8_t> allowListTransports; 1112 if (aSelectedCredentialId.isSome()) { 1113 allowList.AppendElement(aSelectedCredentialId.extract()); 1114 allowListTransports.AppendElement( 1115 MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL); 1116 } else { 1117 (void)aArgs->GetAllowList(allowList); 1118 (void)aArgs->GetAllowListTransports(allowListTransports); 1119 } 1120 // Compute the union of the transport sets. 1121 uint8_t transports = 0; 1122 for (uint8_t credTransports : allowListTransports) { 1123 if (credTransports == 0) { 1124 // treat the empty transport set as "all transports". 1125 transports = ~0; 1126 break; 1127 } 1128 transports |= credTransports; 1129 } 1130 1131 NSMutableArray* platformAllowedCredentials = 1132 [[NSMutableArray alloc] init]; 1133 for (const auto& allowedCredentialId : allowList) { 1134 NSData* allowedCredentialIdNS = 1135 [NSData dataWithBytes:allowedCredentialId.Elements() 1136 length:allowedCredentialId.Length()]; 1137 ASAuthorizationPlatformPublicKeyCredentialDescriptor* 1138 allowedCredential = 1139 [[ASAuthorizationPlatformPublicKeyCredentialDescriptor alloc] 1140 initWithCredentialID:allowedCredentialIdNS]; 1141 [platformAllowedCredentials addObject:allowedCredential]; 1142 } 1143 const Class securityKeyPublicKeyCredentialDescriptorClass = 1144 NSClassFromString( 1145 @"ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor"); 1146 NSArray<ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor*>* 1147 crossPlatformAllowedCredentials = 1148 CredentialListsToCredentialDescriptorArray( 1149 allowList, allowListTransports, 1150 securityKeyPublicKeyCredentialDescriptorClass); 1151 1152 Maybe<ASAuthorizationPublicKeyCredentialUserVerificationPreference> 1153 userVerificationPreference = Nothing(); 1154 nsAutoString userVerification; 1155 (void)aArgs->GetUserVerification(userVerification); 1156 // This mapping needs to be reviewed if values are added to the 1157 // UserVerificationRequirement enum. 1158 static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3); 1159 if (userVerification.EqualsLiteral( 1160 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) { 1161 userVerificationPreference.emplace( 1162 ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired); 1163 } else if (userVerification.EqualsLiteral( 1164 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) { 1165 userVerificationPreference.emplace( 1166 ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred); 1167 } else if ( 1168 userVerification.EqualsLiteral( 1169 MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) { 1170 userVerificationPreference.emplace( 1171 ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged); 1172 } 1173 1174 // Initialize the platform provider with the rpId. 1175 ASAuthorizationPlatformPublicKeyCredentialProvider* platformProvider = 1176 [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc] 1177 initWithRelyingPartyIdentifier:rpIdNS]; 1178 // Make a credential assertion request with the challenge. 1179 ASAuthorizationPlatformPublicKeyCredentialAssertionRequest* 1180 platformAssertionRequest = [platformProvider 1181 createCredentialAssertionRequestWithChallenge:challengeNS]; 1182 [platformProvider release]; 1183 platformAssertionRequest.allowedCredentials = 1184 platformAllowedCredentials; 1185 if (userVerificationPreference.isSome()) { 1186 platformAssertionRequest.userVerificationPreference = 1187 *userVerificationPreference; 1188 } 1189 if (__builtin_available(macos 13.5, *)) { 1190 // Show the hybrid transport option if (1) none of the allowlist 1191 // credentials list transports, or (2) at least one allow list entry 1192 // lists the hybrid transport, or (3) the request has the hybrid hint. 1193 bool shouldShowHybridTransport = 1194 !transports || 1195 (transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID); 1196 nsTArray<nsString> hints; 1197 (void)aArgs->GetHints(hints); 1198 for (nsString& hint : hints) { 1199 if (hint.Equals(u"hybrid"_ns)) { 1200 shouldShowHybridTransport = true; 1201 } 1202 } 1203 platformAssertionRequest.shouldShowHybridTransport = 1204 shouldShowHybridTransport; 1205 } 1206 1207 // Initialize the cross-platform provider with the rpId. 1208 ASAuthorizationSecurityKeyPublicKeyCredentialProvider* 1209 crossPlatformProvider = 1210 [[ASAuthorizationSecurityKeyPublicKeyCredentialProvider alloc] 1211 initWithRelyingPartyIdentifier:rpIdNS]; 1212 // Make a credential assertion request with the challenge. 1213 ASAuthorizationSecurityKeyPublicKeyCredentialAssertionRequest* 1214 crossPlatformAssertionRequest = [crossPlatformProvider 1215 createCredentialAssertionRequestWithChallenge:challengeNS]; 1216 [crossPlatformProvider release]; 1217 crossPlatformAssertionRequest.allowedCredentials = 1218 crossPlatformAllowedCredentials; 1219 if (userVerificationPreference.isSome()) { 1220 crossPlatformAssertionRequest.userVerificationPreference = 1221 *userVerificationPreference; 1222 } 1223 1224 if (__builtin_available(macos 14.0, *)) { 1225 nsTArray<uint8_t> largeBlobWrite; 1226 bool largeBlobRead; 1227 nsresult rv = aArgs->GetLargeBlobRead(&largeBlobRead); 1228 if (rv != NS_ERROR_NOT_AVAILABLE) { 1229 if (NS_FAILED(rv)) { 1230 self->mSignPromise->Reject(rv); 1231 return; 1232 } 1233 if (largeBlobRead) { 1234 platformAssertionRequest 1235 .largeBlob = [[ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput 1236 alloc] 1237 initWithOperation: 1238 ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperationRead]; 1239 } else { 1240 rv = aArgs->GetLargeBlobWrite(largeBlobWrite); 1241 if (rv != NS_ERROR_NOT_AVAILABLE) { 1242 if (NS_FAILED(rv)) { 1243 self->mSignPromise->Reject(rv); 1244 return; 1245 } 1246 ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput* 1247 largeBlobAssertionInput = 1248 [[ASAuthorizationPublicKeyCredentialLargeBlobAssertionInput 1249 alloc] 1250 initWithOperation: 1251 ASAuthorizationPublicKeyCredentialLargeBlobAssertionOperationWrite]; 1252 // We need to fully form the input before assigning it to 1253 // platformAssertionRequest.largeBlob. See 1254 // https://bugs.webkit.org/show_bug.cgi?id=276961 1255 largeBlobAssertionInput.dataToWrite = 1256 [NSData dataWithBytes:largeBlobWrite.Elements() 1257 length:largeBlobWrite.Length()]; 1258 platformAssertionRequest.largeBlob = largeBlobAssertionInput; 1259 } 1260 } 1261 } 1262 } 1263 1264 if (__builtin_available(macos 14.5, *)) { 1265 nsString appId; 1266 nsresult rv = aArgs->GetAppId(appId); 1267 if (rv != NS_ERROR_NOT_AVAILABLE) { // AppID is set 1268 if (NS_FAILED(rv)) { 1269 self->mSignPromise->Reject(rv); 1270 return; 1271 } 1272 crossPlatformAssertionRequest.appID = 1273 nsCocoaUtils::ToNSString(appId); 1274 } 1275 } 1276 1277 if (__builtin_available(macos 15.0, *)) { 1278 bool requestedPrf; 1279 (void)aArgs->GetPrf(&requestedPrf); 1280 if (requestedPrf) { 1281 NSData* saltInput1 = nil; 1282 NSData* saltInput2 = nil; 1283 nsTArray<uint8_t> prfInput1; 1284 nsresult rv = aArgs->GetPrfEvalFirst(prfInput1); 1285 if (rv != NS_ERROR_NOT_AVAILABLE) { 1286 if (NS_FAILED(rv)) { 1287 self->mSignPromise->Reject(rv); 1288 return; 1289 } 1290 saltInput1 = [NSData dataWithBytes:prfInput1.Elements() 1291 length:prfInput1.Length()]; 1292 } 1293 nsTArray<uint8_t> prfInput2; 1294 rv = aArgs->GetPrfEvalSecond(prfInput2); 1295 if (rv != NS_ERROR_NOT_AVAILABLE) { 1296 if (NS_FAILED(rv)) { 1297 self->mSignPromise->Reject(rv); 1298 return; 1299 } 1300 saltInput2 = [NSData dataWithBytes:prfInput2.Elements() 1301 length:prfInput2.Length()]; 1302 } 1303 ASAuthorizationPublicKeyCredentialPRFAssertionInputValues* 1304 prfInputs = 1305 [[ASAuthorizationPublicKeyCredentialPRFAssertionInputValues 1306 alloc] initWithSaltInput1:saltInput1 1307 saltInput2:saltInput2]; 1308 1309 NSDictionary< 1310 NSData*, 1311 ASAuthorizationPublicKeyCredentialPRFAssertionInputValues*>* 1312 prfPerCredentialInputs = 1313 ConstructPrfEvalByCredentialEntries(aArgs); 1314 platformAssertionRequest.prf = 1315 [[ASAuthorizationPublicKeyCredentialPRFAssertionInput alloc] 1316 initWithInputValues:prfInputs 1317 perCredentialInputValues:prfPerCredentialInputs]; 1318 } 1319 } 1320 1321 nsTArray<uint8_t> clientDataHash; 1322 nsresult rv = aArgs->GetClientDataHash(clientDataHash); 1323 if (NS_FAILED(rv)) { 1324 self->mSignPromise->Reject(rv); 1325 return; 1326 } 1327 nsTArray<nsTArray<uint8_t>> unusedCredentialIds; 1328 nsTArray<uint8_t> unusedTransports; 1329 // allowList and allowListTransports won't actually get used. 1330 self->PerformRequests( 1331 @[ platformAssertionRequest, crossPlatformAssertionRequest ], 1332 std::move(clientDataHash), std::move(allowList), 1333 std::move(allowListTransports), browsingContextId); 1334 })); 1335 } 1336 1337 void MacOSWebAuthnService::FinishGetAssertion( 1338 const nsTArray<uint8_t>& aCredentialId, const nsTArray<uint8_t>& aSignature, 1339 const nsTArray<uint8_t>& aAuthenticatorData, 1340 const nsTArray<uint8_t>& aUserHandle, 1341 const Maybe<nsString>& aAuthenticatorAttachment, 1342 const Maybe<bool>& aUsedAppId, 1343 const Maybe<nsTArray<uint8_t>>& aLargeBlobValue, 1344 const Maybe<bool>& aLargeBlobWritten, 1345 const Maybe<nsTArray<uint8_t>>& aPrfFirst, 1346 const Maybe<nsTArray<uint8_t>>& aPrfSecond) { 1347 MOZ_ASSERT(NS_IsMainThread()); 1348 if (!mSignPromise) { 1349 return; 1350 } 1351 1352 RefPtr<WebAuthnSignResult> result(new WebAuthnSignResult( 1353 aAuthenticatorData, Nothing(), aCredentialId, aSignature, aUserHandle, 1354 aAuthenticatorAttachment, aUsedAppId, aLargeBlobValue, aLargeBlobWritten, 1355 aPrfFirst, aPrfSecond)); 1356 (void)mSignPromise->Resolve(result); 1357 mSignPromise = nullptr; 1358 } 1359 1360 void MacOSWebAuthnService::ReleasePlatformResources() { 1361 MOZ_ASSERT(NS_IsMainThread()); 1362 [mCredentialManager release]; 1363 mCredentialManager = nil; 1364 [mAuthorizationController release]; 1365 mAuthorizationController = nil; 1366 [mRequestDelegate release]; 1367 mRequestDelegate = nil; 1368 [mPresentationContextProvider release]; 1369 mPresentationContextProvider = nil; 1370 } 1371 1372 NS_IMETHODIMP 1373 MacOSWebAuthnService::Reset() { 1374 auto guard = mTransactionState.Lock(); 1375 if (guard->isSome()) { 1376 if (guard->ref().pendingSignPromise.isSome()) { 1377 // This request was never dispatched to the platform API, so 1378 // we need to reject the promise ourselves. 1379 guard->ref().pendingSignPromise.ref()->Reject( 1380 NS_ERROR_DOM_NOT_ALLOWED_ERR); 1381 } 1382 guard->reset(); 1383 } 1384 NS_DispatchToMainThread(NS_NewRunnableFunction( 1385 "MacOSWebAuthnService::Cancel", [self = RefPtr{this}] { 1386 // cancel results in the delegate's didCompleteWithError method being 1387 // called, which will release all platform resources. 1388 [self->mAuthorizationController cancel]; 1389 })); 1390 return NS_OK; 1391 } 1392 1393 NS_IMETHODIMP 1394 MacOSWebAuthnService::GetIsUVPAA(bool* aAvailable) { 1395 *aAvailable = true; 1396 return NS_OK; 1397 } 1398 1399 NS_IMETHODIMP 1400 MacOSWebAuthnService::Cancel(uint64_t aTransactionId) { 1401 return NS_ERROR_NOT_IMPLEMENTED; 1402 } 1403 1404 NS_IMETHODIMP 1405 MacOSWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId, 1406 const nsAString& aOrigin, 1407 uint64_t* aRv) { 1408 auto guard = mTransactionState.Lock(); 1409 if (guard->isNothing() || 1410 guard->ref().browsingContextId != aBrowsingContextId || 1411 guard->ref().pendingSignArgs.isNothing()) { 1412 *aRv = 0; 1413 return NS_OK; 1414 } 1415 1416 nsString origin; 1417 (void)guard->ref().pendingSignArgs.ref()->GetOrigin(origin); 1418 if (origin != aOrigin) { 1419 *aRv = 0; 1420 return NS_OK; 1421 } 1422 1423 *aRv = guard->ref().transactionId; 1424 return NS_OK; 1425 } 1426 1427 NS_IMETHODIMP 1428 MacOSWebAuthnService::GetAutoFillEntries( 1429 uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) { 1430 auto guard = mTransactionState.Lock(); 1431 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1432 guard->ref().pendingSignArgs.isNothing() || 1433 guard->ref().autoFillEntries.isNothing()) { 1434 return NS_ERROR_NOT_AVAILABLE; 1435 } 1436 aRv.Assign(guard->ref().autoFillEntries.ref()); 1437 return NS_OK; 1438 } 1439 1440 NS_IMETHODIMP 1441 MacOSWebAuthnService::SelectAutoFillEntry( 1442 uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) { 1443 auto guard = mTransactionState.Lock(); 1444 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1445 guard->ref().pendingSignArgs.isNothing()) { 1446 return NS_ERROR_NOT_AVAILABLE; 1447 } 1448 1449 nsTArray<nsTArray<uint8_t>> allowList; 1450 (void)guard->ref().pendingSignArgs.ref()->GetAllowList(allowList); 1451 if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) { 1452 return NS_ERROR_FAILURE; 1453 } 1454 1455 Maybe<nsTArray<uint8_t>> id; 1456 id.emplace(); 1457 id.ref().Assign(aCredentialId); 1458 DoGetAssertion(std::move(id), guard); 1459 1460 return NS_OK; 1461 } 1462 1463 NS_IMETHODIMP 1464 MacOSWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) { 1465 auto guard = mTransactionState.Lock(); 1466 if (guard->isNothing() || guard->ref().transactionId != aTransactionId || 1467 guard->ref().pendingSignArgs.isNothing()) { 1468 return NS_ERROR_NOT_AVAILABLE; 1469 } 1470 DoGetAssertion(Nothing(), guard); 1471 return NS_OK; 1472 } 1473 1474 NS_IMETHODIMP 1475 MacOSWebAuthnService::PinCallback(uint64_t aTransactionId, 1476 const nsACString& aPin) { 1477 return NS_ERROR_NOT_IMPLEMENTED; 1478 } 1479 1480 NS_IMETHODIMP 1481 MacOSWebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId, 1482 bool aHasConsent) { 1483 return NS_ERROR_NOT_IMPLEMENTED; 1484 } 1485 1486 NS_IMETHODIMP 1487 MacOSWebAuthnService::SelectionCallback(uint64_t aTransactionId, 1488 uint64_t aIndex) { 1489 return NS_ERROR_NOT_IMPLEMENTED; 1490 } 1491 1492 NS_IMETHODIMP 1493 MacOSWebAuthnService::AddVirtualAuthenticator( 1494 const nsACString& aProtocol, const nsACString& aTransport, 1495 bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting, 1496 bool aIsUserVerified, nsACString& aRetval) { 1497 return NS_ERROR_NOT_IMPLEMENTED; 1498 } 1499 1500 NS_IMETHODIMP 1501 MacOSWebAuthnService::RemoveVirtualAuthenticator( 1502 const nsACString& aAuthenticatorId) { 1503 return NS_ERROR_NOT_IMPLEMENTED; 1504 } 1505 1506 NS_IMETHODIMP 1507 MacOSWebAuthnService::AddCredential(const nsACString& aAuthenticatorId, 1508 const nsACString& aCredentialId, 1509 bool aIsResidentCredential, 1510 const nsACString& aRpId, 1511 const nsACString& aPrivateKey, 1512 const nsACString& aUserHandle, 1513 uint32_t aSignCount) { 1514 return NS_ERROR_NOT_IMPLEMENTED; 1515 } 1516 1517 NS_IMETHODIMP 1518 MacOSWebAuthnService::GetCredentials( 1519 const nsACString& aAuthenticatorId, 1520 nsTArray<RefPtr<nsICredentialParameters>>& _aRetval) { 1521 return NS_ERROR_NOT_IMPLEMENTED; 1522 } 1523 1524 NS_IMETHODIMP 1525 MacOSWebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId, 1526 const nsACString& aCredentialId) { 1527 return NS_ERROR_NOT_IMPLEMENTED; 1528 } 1529 1530 NS_IMETHODIMP 1531 MacOSWebAuthnService::RemoveAllCredentials(const nsACString& aAuthenticatorId) { 1532 return NS_ERROR_NOT_IMPLEMENTED; 1533 } 1534 1535 NS_IMETHODIMP 1536 MacOSWebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId, 1537 bool aIsUserVerified) { 1538 return NS_ERROR_NOT_IMPLEMENTED; 1539 } 1540 1541 NS_IMETHODIMP 1542 MacOSWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; } 1543 1544 NS_IMETHODIMP 1545 MacOSWebAuthnService::RunCommand(const nsACString& aCmd) { 1546 return NS_ERROR_NOT_IMPLEMENTED; 1547 } 1548 1549 } // namespace mozilla::dom 1550 1551 NS_ASSUME_NONNULL_END