tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

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