summaryrefslogtreecommitdiffstats
path: root/dom/webauthn/MacOSWebAuthnService.mm
diff options
context:
space:
mode:
Diffstat (limited to 'dom/webauthn/MacOSWebAuthnService.mm')
-rw-r--r--dom/webauthn/MacOSWebAuthnService.mm1166
1 files changed, 1166 insertions, 0 deletions
diff --git a/dom/webauthn/MacOSWebAuthnService.mm b/dom/webauthn/MacOSWebAuthnService.mm
new file mode 100644
index 0000000000..79b9030541
--- /dev/null
+++ b/dom/webauthn/MacOSWebAuthnService.mm
@@ -0,0 +1,1166 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#import <AuthenticationServices/AuthenticationServices.h>
+
+#include "MacOSWebAuthnService.h"
+
+#include "CFTypeRefPtr.h"
+#include "WebAuthnAutoFillEntry.h"
+#include "WebAuthnEnumStrings.h"
+#include "WebAuthnResult.h"
+#include "WebAuthnTransportIdentifiers.h"
+#include "mozilla/Maybe.h"
+#include "mozilla/StaticPrefs_security.h"
+#include "mozilla/dom/CanonicalBrowsingContext.h"
+#include "nsCocoaUtils.h"
+#include "nsIWebAuthnPromise.h"
+#include "nsThreadUtils.h"
+
+// The documentation for the platform APIs used here can be found at:
+// https://developer.apple.com/documentation/authenticationservices/public-private_key_authentication/supporting_passkeys
+
+namespace {
+static mozilla::LazyLogModule gMacOSWebAuthnServiceLog("macoswebauthnservice");
+} // namespace
+
+namespace mozilla::dom {
+class API_AVAILABLE(macos(13.3)) MacOSWebAuthnService;
+} // namespace mozilla::dom
+
+// The following ASC* declarations are from the private framework
+// AuthenticationServicesCore. The full definitions can be found in WebKit's
+// source at Source/WebKit/Platform/spi/Cocoa/AuthenticationServicesCoreSPI.h.
+// Overriding ASAuthorizationController's _requestContextWithRequests is
+// currently the only way to provide the right information to the macOS
+// WebAuthn API (namely, the clientDataHash for requests made to physical
+// tokens).
+
+NS_ASSUME_NONNULL_BEGIN
+
+@class ASCPublicKeyCredentialDescriptor;
+@interface ASCPublicKeyCredentialDescriptor : NSObject <NSSecureCoding>
+- (instancetype)initWithCredentialID:(NSData*)credentialID
+ transports:
+ (nullable NSArray<NSString*>*)allowedTransports;
+@end
+
+@protocol ASCPublicKeyCredentialCreationOptions
+@property(nonatomic, copy) NSData* clientDataHash;
+@property(nonatomic, nullable, copy) NSData* challenge;
+@property(nonatomic, copy)
+ NSArray<ASCPublicKeyCredentialDescriptor*>* excludedCredentials;
+@end
+
+@protocol ASCPublicKeyCredentialAssertionOptions <NSCopying>
+@property(nonatomic, copy) NSData* clientDataHash;
+@end
+
+@protocol ASCCredentialRequestContext
+@property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialCreationOptions>
+ platformKeyCredentialCreationOptions;
+@property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialCreationOptions>
+ securityKeyCredentialCreationOptions;
+@property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialAssertionOptions>
+ platformKeyCredentialAssertionOptions;
+@property(nonatomic, nullable, copy) id<ASCPublicKeyCredentialAssertionOptions>
+ securityKeyCredentialAssertionOptions;
+@end
+
+@interface ASAuthorizationController (Secrets)
+- (id<ASCCredentialRequestContext>)
+ _requestContextWithRequests:(NSArray<ASAuthorizationRequest*>*)requests
+ error:(NSError**)outError;
+@end
+
+NSArray<NSString*>* TransportsByteToTransportsArray(const uint8_t aTransports)
+ API_AVAILABLE(macos(13.3)) {
+ NSMutableArray<NSString*>* transportsNS = [[NSMutableArray alloc] init];
+ if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) ==
+ MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_USB) {
+ [transportsNS
+ addObject:
+ ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportUSB];
+ }
+ if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) ==
+ MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_NFC) {
+ [transportsNS
+ addObject:
+ ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportNFC];
+ }
+ if ((aTransports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) ==
+ MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_BLE) {
+ [transportsNS
+ addObject:
+ ASAuthorizationSecurityKeyPublicKeyCredentialDescriptorTransportBluetooth];
+ }
+ // TODO (bug 1859367): the platform doesn't have a definition for "internal"
+ // transport. When it does, this code should be updated to handle it.
+ return transportsNS;
+}
+
+NSArray* CredentialListsToCredentialDescriptorArray(
+ const nsTArray<nsTArray<uint8_t>>& aCredentialList,
+ const nsTArray<uint8_t>& aCredentialListTransports,
+ const Class credentialDescriptorClass) API_AVAILABLE(macos(13.3)) {
+ MOZ_ASSERT(aCredentialList.Length() == aCredentialListTransports.Length());
+ NSMutableArray* credentials = [[NSMutableArray alloc] init];
+ for (size_t i = 0; i < aCredentialList.Length(); i++) {
+ const nsTArray<uint8_t>& credentialId = aCredentialList[i];
+ const uint8_t& credentialTransports = aCredentialListTransports[i];
+ NSData* credentialIdNS = [NSData dataWithBytes:credentialId.Elements()
+ length:credentialId.Length()];
+ NSArray<NSString*>* credentialTransportsNS =
+ TransportsByteToTransportsArray(credentialTransports);
+ NSObject* credential = [[credentialDescriptorClass alloc]
+ initWithCredentialID:credentialIdNS
+ transports:credentialTransportsNS];
+ [credentials addObject:credential];
+ }
+ return credentials;
+}
+
+// MacOSAuthorizationController is an ASAuthorizationController that overrides
+// _requestContextWithRequests so that the implementation can set some options
+// that aren't directly settable using the public API.
+API_AVAILABLE(macos(13.3))
+@interface MacOSAuthorizationController : ASAuthorizationController
+@end
+
+@implementation MacOSAuthorizationController {
+ nsTArray<uint8_t> mClientDataHash;
+ nsTArray<nsTArray<uint8_t>> mCredentialList;
+ nsTArray<uint8_t> mCredentialListTransports;
+}
+
+- (void)setRegistrationOptions:
+ (id<ASCPublicKeyCredentialCreationOptions>)registrationOptions {
+ registrationOptions.clientDataHash =
+ [NSData dataWithBytes:mClientDataHash.Elements()
+ length:mClientDataHash.Length()];
+ // Unset challenge so that the implementation uses clientDataHash (the API
+ // returns an error otherwise).
+ registrationOptions.challenge = nil;
+ const Class publicKeyCredentialDescriptorClass =
+ NSClassFromString(@"ASCPublicKeyCredentialDescriptor");
+ NSArray<ASCPublicKeyCredentialDescriptor*>* excludedCredentials =
+ CredentialListsToCredentialDescriptorArray(
+ mCredentialList, mCredentialListTransports,
+ publicKeyCredentialDescriptorClass);
+ if ([excludedCredentials count] > 0) {
+ registrationOptions.excludedCredentials = excludedCredentials;
+ }
+}
+
+- (void)stashClientDataHash:(nsTArray<uint8_t>&&)clientDataHash
+ andCredentialList:(nsTArray<nsTArray<uint8_t>>&&)credentialList
+ andCredentialListTransports:(nsTArray<uint8_t>&&)credentialListTransports {
+ mClientDataHash = std::move(clientDataHash);
+ mCredentialList = std::move(credentialList);
+ mCredentialListTransports = std::move(credentialListTransports);
+}
+
+- (id<ASCCredentialRequestContext>)
+ _requestContextWithRequests:(NSArray<ASAuthorizationRequest*>*)requests
+ error:(NSError**)outError {
+ id<ASCCredentialRequestContext> context =
+ [super _requestContextWithRequests:requests error:outError];
+
+ if (context.platformKeyCredentialCreationOptions) {
+ [self setRegistrationOptions:context.platformKeyCredentialCreationOptions];
+ }
+ if (context.securityKeyCredentialCreationOptions) {
+ [self setRegistrationOptions:context.securityKeyCredentialCreationOptions];
+ }
+
+ if (context.platformKeyCredentialAssertionOptions) {
+ id<ASCPublicKeyCredentialAssertionOptions> assertionOptions =
+ context.platformKeyCredentialAssertionOptions;
+ assertionOptions.clientDataHash =
+ [NSData dataWithBytes:mClientDataHash.Elements()
+ length:mClientDataHash.Length()];
+ context.platformKeyCredentialAssertionOptions =
+ [assertionOptions copyWithZone:nil];
+ }
+ if (context.securityKeyCredentialAssertionOptions) {
+ id<ASCPublicKeyCredentialAssertionOptions> assertionOptions =
+ context.securityKeyCredentialAssertionOptions;
+ assertionOptions.clientDataHash =
+ [NSData dataWithBytes:mClientDataHash.Elements()
+ length:mClientDataHash.Length()];
+ context.securityKeyCredentialAssertionOptions =
+ [assertionOptions copyWithZone:nil];
+ }
+
+ return context;
+}
+@end
+
+// MacOSAuthenticatorRequestDelegate is an ASAuthorizationControllerDelegate,
+// which can be set as an ASAuthorizationController's delegate to be called
+// back when a request for a platform authenticator attestation or assertion
+// response completes either with an attestation or assertion
+// (didCompleteWithAuthorization) or with an error (didCompleteWithError).
+API_AVAILABLE(macos(13.3))
+@interface MacOSAuthenticatorRequestDelegate
+ : NSObject <ASAuthorizationControllerDelegate>
+- (void)setCallback:(mozilla::dom::MacOSWebAuthnService*)callback;
+@end
+
+// MacOSAuthenticatorPresentationContextProvider is an
+// ASAuthorizationControllerPresentationContextProviding, which can be set as
+// an ASAuthorizationController's presentationContextProvider, and provides a
+// presentation anchor for the ASAuthorizationController. Basically, this
+// provides the API a handle to the window that made the request.
+API_AVAILABLE(macos(13.3))
+@interface MacOSAuthenticatorPresentationContextProvider
+ : NSObject <ASAuthorizationControllerPresentationContextProviding>
+@property(nonatomic, strong) NSWindow* window;
+@end
+
+namespace mozilla::dom {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+class API_AVAILABLE(macos(13.3)) MacOSWebAuthnService final
+ : public nsIWebAuthnService {
+ public:
+ MacOSWebAuthnService()
+ : mTransactionState(Nothing(),
+ "MacOSWebAuthnService::mTransactionState") {}
+
+ NS_DECL_THREADSAFE_ISUPPORTS
+ NS_DECL_NSIWEBAUTHNSERVICE
+
+ void FinishMakeCredential(const nsTArray<uint8_t>& aRawAttestationObject,
+ const nsTArray<uint8_t>& aCredentialId,
+ const nsTArray<nsString>& aTransports,
+ const Maybe<nsString>& aAuthenticatorAttachment);
+
+ void FinishGetAssertion(const nsTArray<uint8_t>& aCredentialId,
+ const nsTArray<uint8_t>& aSignature,
+ const nsTArray<uint8_t>& aAuthenticatorData,
+ const nsTArray<uint8_t>& aUserHandle,
+ const Maybe<nsString>& aAuthenticatorAttachment);
+ void ReleasePlatformResources();
+ void AbortTransaction(nsresult aError);
+
+ private:
+ ~MacOSWebAuthnService() = default;
+
+ void PerformRequests(NSArray<ASAuthorizationRequest*>* aRequests,
+ nsTArray<uint8_t>&& aClientDataHash,
+ nsTArray<nsTArray<uint8_t>>&& aCredentialList,
+ nsTArray<uint8_t>&& aCredentialListTransports,
+ uint64_t aBrowsingContextId);
+
+ struct TransactionState {
+ uint64_t transactionId;
+ uint64_t browsingContextId;
+ Maybe<RefPtr<nsIWebAuthnSignArgs>> pendingSignArgs;
+ Maybe<RefPtr<nsIWebAuthnSignPromise>> pendingSignPromise;
+ Maybe<nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>> autoFillEntries;
+ };
+
+ using TransactionStateMutex = DataMutex<Maybe<TransactionState>>;
+ void DoGetAssertion(Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId,
+ const TransactionStateMutex::AutoLock& aGuard);
+
+ TransactionStateMutex mTransactionState;
+
+ // Main thread only:
+ ASAuthorizationWebBrowserPublicKeyCredentialManager* mCredentialManager = nil;
+ nsCOMPtr<nsIWebAuthnRegisterPromise> mRegisterPromise;
+ nsCOMPtr<nsIWebAuthnSignPromise> mSignPromise;
+ MacOSAuthorizationController* mAuthorizationController = nil;
+ MacOSAuthenticatorRequestDelegate* mRequestDelegate = nil;
+ MacOSAuthenticatorPresentationContextProvider* mPresentationContextProvider =
+ nil;
+};
+#pragma clang diagnostic pop
+
+} // namespace mozilla::dom
+
+nsTArray<uint8_t> NSDataToArray(NSData* data) {
+ nsTArray<uint8_t> array(reinterpret_cast<const uint8_t*>(data.bytes),
+ data.length);
+ return array;
+}
+
+@implementation MacOSAuthenticatorRequestDelegate {
+ RefPtr<mozilla::dom::MacOSWebAuthnService> mCallback;
+}
+
+- (void)setCallback:(mozilla::dom::MacOSWebAuthnService*)callback {
+ mCallback = callback;
+}
+
+- (void)authorizationController:(ASAuthorizationController*)controller
+ didCompleteWithAuthorization:(ASAuthorization*)authorization {
+ if ([authorization.credential
+ conformsToProtocol:
+ @protocol(ASAuthorizationPublicKeyCredentialRegistration)]) {
+ MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug,
+ ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: "
+ "got registration"));
+ id<ASAuthorizationPublicKeyCredentialRegistration> credential =
+ (id<ASAuthorizationPublicKeyCredentialRegistration>)
+ authorization.credential;
+ nsTArray<uint8_t> rawAttestationObject(
+ NSDataToArray(credential.rawAttestationObject));
+ nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID));
+ nsTArray<nsString> transports;
+ mozilla::Maybe<nsString> authenticatorAttachment;
+ if ([credential isKindOfClass:
+ [ASAuthorizationPlatformPublicKeyCredentialRegistration
+ class]]) {
+ transports.AppendElement(u"internal"_ns);
+#if defined(MAC_OS_VERSION_13_5) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_5
+ if (__builtin_available(macos 13.5, *)) {
+ ASAuthorizationPlatformPublicKeyCredentialRegistration*
+ platformCredential =
+ (ASAuthorizationPlatformPublicKeyCredentialRegistration*)
+ credential;
+ switch (platformCredential.attachment) {
+ case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
+ authenticatorAttachment.emplace(u"cross-platform"_ns);
+ break;
+ case ASAuthorizationPublicKeyCredentialAttachmentPlatform:
+ authenticatorAttachment.emplace(u"platform"_ns);
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ } else {
+ authenticatorAttachment.emplace(u"cross-platform"_ns);
+ }
+ mCallback->FinishMakeCredential(rawAttestationObject, credentialId,
+ transports, authenticatorAttachment);
+ } else if ([authorization.credential
+ conformsToProtocol:
+ @protocol(ASAuthorizationPublicKeyCredentialAssertion)]) {
+ MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug,
+ ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: "
+ "got assertion"));
+ id<ASAuthorizationPublicKeyCredentialAssertion> credential =
+ (id<ASAuthorizationPublicKeyCredentialAssertion>)
+ authorization.credential;
+ nsTArray<uint8_t> credentialId(NSDataToArray(credential.credentialID));
+ nsTArray<uint8_t> signature(NSDataToArray(credential.signature));
+ nsTArray<uint8_t> rawAuthenticatorData(
+ NSDataToArray(credential.rawAuthenticatorData));
+ nsTArray<uint8_t> userHandle(NSDataToArray(credential.userID));
+ mozilla::Maybe<nsString> authenticatorAttachment;
+ if ([credential
+ isKindOfClass:[ASAuthorizationPlatformPublicKeyCredentialAssertion
+ class]]) {
+#if defined(MAC_OS_VERSION_13_5) && \
+ MAC_OS_X_VERSION_MAX_ALLOWED >= MAC_OS_VERSION_13_5
+ if (__builtin_available(macos 13.5, *)) {
+ ASAuthorizationPlatformPublicKeyCredentialAssertion*
+ platformCredential =
+ (ASAuthorizationPlatformPublicKeyCredentialAssertion*)
+ credential;
+ switch (platformCredential.attachment) {
+ case ASAuthorizationPublicKeyCredentialAttachmentCrossPlatform:
+ authenticatorAttachment.emplace(u"cross-platform"_ns);
+ break;
+ case ASAuthorizationPublicKeyCredentialAttachmentPlatform:
+ authenticatorAttachment.emplace(u"platform"_ns);
+ break;
+ default:
+ break;
+ }
+ }
+#endif
+ } else {
+ authenticatorAttachment.emplace(u"cross-platform"_ns);
+ }
+ mCallback->FinishGetAssertion(credentialId, signature, rawAuthenticatorData,
+ userHandle, authenticatorAttachment);
+ } else {
+ MOZ_LOG(
+ gMacOSWebAuthnServiceLog, mozilla::LogLevel::Error,
+ ("MacOSAuthenticatorRequestDelegate::didCompleteWithAuthorization: "
+ "authorization.credential is neither registration nor assertion!"));
+ MOZ_ASSERT_UNREACHABLE(
+ "should have ASAuthorizationPublicKeyCredentialRegistration or "
+ "ASAuthorizationPublicKeyCredentialAssertion");
+ }
+ mCallback->ReleasePlatformResources();
+ mCallback = nullptr;
+}
+
+- (void)authorizationController:(ASAuthorizationController*)controller
+ didCompleteWithError:(NSError*)error {
+ nsAutoString errorDescription;
+ nsCocoaUtils::GetStringForNSString(error.localizedDescription,
+ errorDescription);
+ nsAutoString errorDomain;
+ nsCocoaUtils::GetStringForNSString(error.domain, errorDomain);
+ MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Warning,
+ ("MacOSAuthenticatorRequestDelegate::didCompleteWithError: domain "
+ "'%s' code %ld (%s)",
+ NS_ConvertUTF16toUTF8(errorDomain).get(), error.code,
+ NS_ConvertUTF16toUTF8(errorDescription).get()));
+ nsresult rv = NS_ERROR_DOM_NOT_ALLOWED_ERR;
+ // For some reason, the error for "the credential used in a registration was
+ // on the exclude list" is in the "WKErrorDomain" domain with code 8, which
+ // is presumably WKErrorDuplicateCredential.
+ const NSInteger WKErrorDuplicateCredential = 8;
+ if (errorDomain.EqualsLiteral("WKErrorDomain") &&
+ error.code == WKErrorDuplicateCredential) {
+ rv = NS_ERROR_DOM_INVALID_STATE_ERR;
+ } else if (error.domain == ASAuthorizationErrorDomain) {
+ switch (error.code) {
+ case ASAuthorizationErrorCanceled:
+ rv = NS_ERROR_DOM_ABORT_ERR;
+ break;
+ case ASAuthorizationErrorFailed:
+ // The message is right, but it's not about indexeddb.
+ // See https://webidl.spec.whatwg.org/#constrainterror
+ rv = NS_ERROR_DOM_INDEXEDDB_CONSTRAINT_ERR;
+ break;
+ case ASAuthorizationErrorUnknown:
+ rv = NS_ERROR_DOM_UNKNOWN_ERR;
+ break;
+ default:
+ // rv already has a default value
+ break;
+ }
+ }
+ mCallback->AbortTransaction(rv);
+ mCallback = nullptr;
+}
+@end
+
+@implementation MacOSAuthenticatorPresentationContextProvider
+@synthesize window = window;
+
+- (ASPresentationAnchor)presentationAnchorForAuthorizationController:
+ (ASAuthorizationController*)controller {
+ return window;
+}
+@end
+
+namespace mozilla::dom {
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+// Given a browsingContextId, attempts to determine the NSWindow associated
+// with that browser.
+nsresult BrowsingContextIdToNSWindow(uint64_t browsingContextId,
+ NSWindow** window) {
+ *window = nullptr;
+ RefPtr<BrowsingContext> browsingContext(
+ BrowsingContext::Get(browsingContextId));
+ if (!browsingContext) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ CanonicalBrowsingContext* canonicalBrowsingContext =
+ browsingContext->Canonical();
+ if (!canonicalBrowsingContext) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ nsCOMPtr<nsIWidget> widget(
+ canonicalBrowsingContext->GetParentProcessWidgetContaining());
+ if (!widget) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ *window = static_cast<NSWindow*>(widget->GetNativeData(NS_NATIVE_WINDOW));
+ return NS_OK;
+}
+#pragma clang diagnostic pop
+
+already_AddRefed<nsIWebAuthnService> NewMacOSWebAuthnServiceIfAvailable() {
+ MOZ_ASSERT(XRE_IsParentProcess());
+ if (!StaticPrefs::security_webauthn_enable_macos_passkeys()) {
+ MOZ_LOG(
+ gMacOSWebAuthnServiceLog, LogLevel::Debug,
+ ("macOS platform support for webauthn (passkeys) disabled by pref"));
+ return nullptr;
+ }
+ // This code checks for the entitlement
+ // 'com.apple.developer.web-browser.public-key-credential', which must be
+ // true to be able to use the platform APIs.
+ CFTypeRefPtr<SecTaskRef> entitlementTask(
+ CFTypeRefPtr<SecTaskRef>::WrapUnderCreateRule(
+ SecTaskCreateFromSelf(nullptr)));
+ CFTypeRefPtr<CFBooleanRef> entitlementValue(
+ CFTypeRefPtr<CFBooleanRef>::WrapUnderCreateRule(
+ reinterpret_cast<CFBooleanRef>(SecTaskCopyValueForEntitlement(
+ entitlementTask.get(),
+ CFSTR("com.apple.developer.web-browser.public-key-credential"),
+ nullptr))));
+ if (!entitlementValue || !CFBooleanGetValue(entitlementValue.get())) {
+ MOZ_LOG(
+ gMacOSWebAuthnServiceLog, LogLevel::Warning,
+ ("entitlement com.apple.developer.web-browser.public-key-credential "
+ "not present: platform passkey APIs will not be available"));
+ return nullptr;
+ }
+ nsCOMPtr<nsIWebAuthnService> service(new MacOSWebAuthnService());
+ return service.forget();
+}
+
+void MacOSWebAuthnService::AbortTransaction(nsresult aError) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (mRegisterPromise) {
+ Unused << mRegisterPromise->Reject(aError);
+ mRegisterPromise = nullptr;
+ }
+ if (mSignPromise) {
+ Unused << mSignPromise->Reject(aError);
+ mSignPromise = nullptr;
+ }
+ ReleasePlatformResources();
+}
+
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wnullability-completeness"
+NS_IMPL_ISUPPORTS(MacOSWebAuthnService, nsIWebAuthnService)
+#pragma clang diagnostic pop
+
+NS_IMETHODIMP
+MacOSWebAuthnService::MakeCredential(uint64_t aTransactionId,
+ uint64_t aBrowsingContextId,
+ nsIWebAuthnRegisterArgs* aArgs,
+ nsIWebAuthnRegisterPromise* aPromise) {
+ Reset();
+ auto guard = mTransactionState.Lock();
+ *guard = Some(TransactionState{
+ aTransactionId,
+ aBrowsingContextId,
+ Nothing(),
+ Nothing(),
+ Nothing(),
+ });
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "MacOSWebAuthnService::MakeCredential",
+ [self = RefPtr{this}, browsingContextId(aBrowsingContextId),
+ aArgs = nsCOMPtr{aArgs}, aPromise = nsCOMPtr{aPromise}]() {
+ self->mRegisterPromise = aPromise;
+
+ nsAutoString rpId;
+ Unused << aArgs->GetRpId(rpId);
+ NSString* rpIdNS = nsCocoaUtils::ToNSString(rpId);
+
+ nsTArray<uint8_t> challenge;
+ Unused << aArgs->GetChallenge(challenge);
+ NSData* challengeNS = [NSData dataWithBytes:challenge.Elements()
+ length:challenge.Length()];
+
+ nsTArray<uint8_t> userId;
+ Unused << aArgs->GetUserId(userId);
+ NSData* userIdNS = [NSData dataWithBytes:userId.Elements()
+ length:userId.Length()];
+
+ nsAutoString userName;
+ Unused << aArgs->GetUserName(userName);
+ NSString* userNameNS = nsCocoaUtils::ToNSString(userName);
+
+ nsAutoString userDisplayName;
+ Unused << aArgs->GetUserName(userDisplayName);
+ NSString* userDisplayNameNS = nsCocoaUtils::ToNSString(userDisplayName);
+
+ nsTArray<int32_t> coseAlgs;
+ Unused << aArgs->GetCoseAlgs(coseAlgs);
+ NSMutableArray* credentialParameters = [[NSMutableArray alloc] init];
+ for (const auto& coseAlg : coseAlgs) {
+ ASAuthorizationPublicKeyCredentialParameters* credentialParameter =
+ [[ASAuthorizationPublicKeyCredentialParameters alloc]
+ initWithAlgorithm:coseAlg];
+ [credentialParameters addObject:credentialParameter];
+ }
+
+ nsTArray<nsTArray<uint8_t>> excludeList;
+ Unused << aArgs->GetExcludeList(excludeList);
+ nsTArray<uint8_t> excludeListTransports;
+ Unused << aArgs->GetExcludeListTransports(excludeListTransports);
+ if (excludeList.Length() != excludeListTransports.Length()) {
+ self->mRegisterPromise->Reject(NS_ERROR_INVALID_ARG);
+ return;
+ }
+
+ Maybe<ASAuthorizationPublicKeyCredentialUserVerificationPreference>
+ userVerificationPreference = Nothing();
+ nsAutoString userVerification;
+ Unused << aArgs->GetUserVerification(userVerification);
+ if (userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired);
+ } else if (userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred);
+ } else if (
+ userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged);
+ }
+
+ // The API doesn't support attestation for platform passkeys and shows
+ // no consent UI for non-none attestation for cross-platform devices,
+ // so this must always be none.
+ ASAuthorizationPublicKeyCredentialAttestationKind
+ attestationPreference =
+ ASAuthorizationPublicKeyCredentialAttestationKindNone;
+
+ // Initialize the platform provider with the rpId.
+ ASAuthorizationPlatformPublicKeyCredentialProvider* platformProvider =
+ [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc]
+ initWithRelyingPartyIdentifier:rpIdNS];
+ // Make a credential registration request with the challenge, userName,
+ // and userId.
+ ASAuthorizationPlatformPublicKeyCredentialRegistrationRequest*
+ platformRegistrationRequest = [platformProvider
+ createCredentialRegistrationRequestWithChallenge:challengeNS
+ name:userNameNS
+ userID:userIdNS];
+ [platformProvider release];
+ platformRegistrationRequest.attestationPreference =
+ attestationPreference;
+ if (userVerificationPreference.isSome()) {
+ platformRegistrationRequest.userVerificationPreference =
+ *userVerificationPreference;
+ }
+
+ // Initialize the cross-platform provider with the rpId.
+ ASAuthorizationSecurityKeyPublicKeyCredentialProvider*
+ crossPlatformProvider =
+ [[ASAuthorizationSecurityKeyPublicKeyCredentialProvider alloc]
+ initWithRelyingPartyIdentifier:rpIdNS];
+ // Make a credential registration request with the challenge,
+ // userDisplayName, userName, and userId.
+ ASAuthorizationSecurityKeyPublicKeyCredentialRegistrationRequest*
+ crossPlatformRegistrationRequest = [crossPlatformProvider
+ createCredentialRegistrationRequestWithChallenge:challengeNS
+ displayName:
+ userDisplayNameNS
+ name:userNameNS
+ userID:userIdNS];
+ [crossPlatformProvider release];
+ crossPlatformRegistrationRequest.attestationPreference =
+ attestationPreference;
+ crossPlatformRegistrationRequest.credentialParameters =
+ credentialParameters;
+ if (userVerificationPreference.isSome()) {
+ crossPlatformRegistrationRequest.userVerificationPreference =
+ *userVerificationPreference;
+ }
+ nsTArray<uint8_t> clientDataHash;
+ nsresult rv = aArgs->GetClientDataHash(clientDataHash);
+ if (NS_FAILED(rv)) {
+ self->mRegisterPromise->Reject(rv);
+ return;
+ }
+ nsAutoString authenticatorAttachment;
+ rv = aArgs->GetAuthenticatorAttachment(authenticatorAttachment);
+ if (NS_FAILED(rv) && rv != NS_ERROR_NOT_AVAILABLE) {
+ self->mRegisterPromise->Reject(rv);
+ return;
+ }
+ NSMutableArray* requests = [[NSMutableArray alloc] init];
+ if (authenticatorAttachment.EqualsLiteral(
+ MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_PLATFORM)) {
+ [requests addObject:platformRegistrationRequest];
+ } else if (authenticatorAttachment.EqualsLiteral(
+ MOZ_WEBAUTHN_AUTHENTICATOR_ATTACHMENT_CROSS_PLATFORM)) {
+ [requests addObject:crossPlatformRegistrationRequest];
+ } else {
+ // Regarding the value of authenticator attachment, according to the
+ // specification, "client platforms MUST ignore unknown values,
+ // treating an unknown value as if the member does not exist".
+ [requests addObject:platformRegistrationRequest];
+ [requests addObject:crossPlatformRegistrationRequest];
+ }
+ self->PerformRequests(
+ requests, std::move(clientDataHash), std::move(excludeList),
+ std::move(excludeListTransports), browsingContextId);
+ }));
+ return NS_OK;
+}
+
+void MacOSWebAuthnService::PerformRequests(
+ NSArray<ASAuthorizationRequest*>* aRequests,
+ nsTArray<uint8_t>&& aClientDataHash,
+ nsTArray<nsTArray<uint8_t>>&& aCredentialList,
+ nsTArray<uint8_t>&& aCredentialListTransports,
+ uint64_t aBrowsingContextId) {
+ MOZ_ASSERT(NS_IsMainThread());
+ // Create a MacOSAuthorizationController and initialize it with the requests.
+ MOZ_ASSERT(!mAuthorizationController);
+ mAuthorizationController = [[MacOSAuthorizationController alloc]
+ initWithAuthorizationRequests:aRequests];
+ [mAuthorizationController
+ stashClientDataHash:std::move(aClientDataHash)
+ andCredentialList:std::move(aCredentialList)
+ andCredentialListTransports:std::move(aCredentialListTransports)];
+
+ // Set up the delegate to run when the operation completes.
+ MOZ_ASSERT(!mRequestDelegate);
+ mRequestDelegate = [[MacOSAuthenticatorRequestDelegate alloc] init];
+ [mRequestDelegate setCallback:this];
+ mAuthorizationController.delegate = mRequestDelegate;
+
+ // Create a presentation context provider so the API knows which window
+ // made the request.
+ NSWindow* window = nullptr;
+ nsresult rv = BrowsingContextIdToNSWindow(aBrowsingContextId, &window);
+ if (NS_FAILED(rv)) {
+ AbortTransaction(NS_ERROR_DOM_INVALID_STATE_ERR);
+ return;
+ }
+ MOZ_ASSERT(!mPresentationContextProvider);
+ mPresentationContextProvider =
+ [[MacOSAuthenticatorPresentationContextProvider alloc] init];
+ mPresentationContextProvider.window = window;
+ mAuthorizationController.presentationContextProvider =
+ mPresentationContextProvider;
+
+ // Finally, perform the request.
+ [mAuthorizationController performRequests];
+}
+
+void MacOSWebAuthnService::FinishMakeCredential(
+ const nsTArray<uint8_t>& aRawAttestationObject,
+ const nsTArray<uint8_t>& aCredentialId,
+ const nsTArray<nsString>& aTransports,
+ const Maybe<nsString>& aAuthenticatorAttachment) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mRegisterPromise) {
+ return;
+ }
+
+ RefPtr<WebAuthnRegisterResult> result(new WebAuthnRegisterResult(
+ aRawAttestationObject, Nothing(), aCredentialId, aTransports,
+ aAuthenticatorAttachment));
+ Unused << mRegisterPromise->Resolve(result);
+ mRegisterPromise = nullptr;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::GetAssertion(uint64_t aTransactionId,
+ uint64_t aBrowsingContextId,
+ nsIWebAuthnSignArgs* aArgs,
+ nsIWebAuthnSignPromise* aPromise) {
+ Reset();
+
+ auto guard = mTransactionState.Lock();
+ *guard = Some(TransactionState{
+ aTransactionId,
+ aBrowsingContextId,
+ Some(RefPtr{aArgs}),
+ Some(RefPtr{aPromise}),
+ Nothing(),
+ });
+
+ bool conditionallyMediated;
+ Unused << aArgs->GetConditionallyMediated(&conditionallyMediated);
+ if (!conditionallyMediated) {
+ DoGetAssertion(Nothing(), guard);
+ return NS_OK;
+ }
+
+ // Using conditional mediation, so dispatch a task to collect any available
+ // passkeys.
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "platformCredentialsForRelyingParty",
+ [self = RefPtr{this}, aTransactionId, aArgs = nsCOMPtr{aArgs}]() {
+ // This handler is called when platformCredentialsForRelyingParty
+ // completes.
+ auto credentialsCompletionHandler = ^(
+ NSArray<ASAuthorizationWebBrowserPlatformPublicKeyCredential*>*
+ credentials) {
+ nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>> autoFillEntries;
+ for (NSUInteger i = 0; i < credentials.count; i++) {
+ const auto& credential = credentials[i];
+ nsAutoString userName;
+ nsCocoaUtils::GetStringForNSString(credential.name, userName);
+ nsAutoString rpId;
+ nsCocoaUtils::GetStringForNSString(credential.relyingParty, rpId);
+ autoFillEntries.AppendElement(new WebAuthnAutoFillEntry(
+ nsIWebAuthnAutoFillEntry::PROVIDER_PLATFORM_MACOS, userName,
+ rpId, NSDataToArray(credential.credentialID)));
+ }
+ auto guard = self->mTransactionState.Lock();
+ if (guard->isSome() && guard->ref().transactionId == aTransactionId) {
+ guard->ref().autoFillEntries.emplace(std::move(autoFillEntries));
+ }
+ };
+ // This handler is called when
+ // requestAuthorizationForPublicKeyCredentials completes.
+ auto authorizationHandler = ^(
+ ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationState
+ authorizationState) {
+ // If authorized, list any available passkeys.
+ if (authorizationState ==
+ ASAuthorizationWebBrowserPublicKeyCredentialManagerAuthorizationStateAuthorized) {
+ nsAutoString rpId;
+ Unused << aArgs->GetRpId(rpId);
+ [self->mCredentialManager
+ platformCredentialsForRelyingParty:nsCocoaUtils::ToNSString(
+ rpId)
+ completionHandler:
+ credentialsCompletionHandler];
+ }
+ };
+ if (!self->mCredentialManager) {
+ self->mCredentialManager =
+ [[ASAuthorizationWebBrowserPublicKeyCredentialManager alloc]
+ init];
+ }
+ // Request authorization to examine any available passkeys. This will
+ // cause a permission prompt to appear once.
+ [self->mCredentialManager
+ requestAuthorizationForPublicKeyCredentials:authorizationHandler];
+ }));
+
+ return NS_OK;
+}
+
+void MacOSWebAuthnService::DoGetAssertion(
+ Maybe<nsTArray<uint8_t>>&& aSelectedCredentialId,
+ const TransactionStateMutex::AutoLock& aGuard) {
+ if (aGuard->isNothing() || aGuard->ref().pendingSignArgs.isNothing() ||
+ aGuard->ref().pendingSignPromise.isNothing()) {
+ return;
+ }
+
+ // Take the pending Args and Promise to prevent repeated calls to
+ // DoGetAssertion for this transaction.
+ RefPtr<nsIWebAuthnSignArgs> aArgs = aGuard->ref().pendingSignArgs.extract();
+ RefPtr<nsIWebAuthnSignPromise> aPromise =
+ aGuard->ref().pendingSignPromise.extract();
+ uint64_t aBrowsingContextId = aGuard->ref().browsingContextId;
+
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "MacOSWebAuthnService::MakeCredential",
+ [self = RefPtr{this}, browsingContextId(aBrowsingContextId), aArgs,
+ aPromise,
+ aSelectedCredentialId = std::move(aSelectedCredentialId)]() mutable {
+ self->mSignPromise = aPromise;
+
+ nsAutoString rpId;
+ Unused << aArgs->GetRpId(rpId);
+ NSString* rpIdNS = nsCocoaUtils::ToNSString(rpId);
+
+ nsTArray<uint8_t> challenge;
+ Unused << aArgs->GetChallenge(challenge);
+ NSData* challengeNS = [NSData dataWithBytes:challenge.Elements()
+ length:challenge.Length()];
+
+ nsTArray<nsTArray<uint8_t>> allowList;
+ nsTArray<uint8_t> allowListTransports;
+ if (aSelectedCredentialId.isSome()) {
+ allowList.AppendElement(aSelectedCredentialId.extract());
+ allowListTransports.AppendElement(
+ MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL);
+ } else {
+ Unused << aArgs->GetAllowList(allowList);
+ Unused << aArgs->GetAllowListTransports(allowListTransports);
+ }
+ NSMutableArray* platformAllowedCredentials =
+ [[NSMutableArray alloc] init];
+ for (const auto& allowedCredentialId : allowList) {
+ NSData* allowedCredentialIdNS =
+ [NSData dataWithBytes:allowedCredentialId.Elements()
+ length:allowedCredentialId.Length()];
+ ASAuthorizationPlatformPublicKeyCredentialDescriptor*
+ allowedCredential =
+ [[ASAuthorizationPlatformPublicKeyCredentialDescriptor alloc]
+ initWithCredentialID:allowedCredentialIdNS];
+ [platformAllowedCredentials addObject:allowedCredential];
+ }
+ const Class securityKeyPublicKeyCredentialDescriptorClass =
+ NSClassFromString(
+ @"ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor");
+ NSArray<ASAuthorizationSecurityKeyPublicKeyCredentialDescriptor*>*
+ crossPlatformAllowedCredentials =
+ CredentialListsToCredentialDescriptorArray(
+ allowList, allowListTransports,
+ securityKeyPublicKeyCredentialDescriptorClass);
+
+ Maybe<ASAuthorizationPublicKeyCredentialUserVerificationPreference>
+ userVerificationPreference = Nothing();
+ nsAutoString userVerification;
+ Unused << aArgs->GetUserVerification(userVerification);
+ if (userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_REQUIRED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferenceRequired);
+ } else if (userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_PREFERRED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferencePreferred);
+ } else if (
+ userVerification.EqualsLiteral(
+ MOZ_WEBAUTHN_USER_VERIFICATION_REQUIREMENT_DISCOURAGED)) {
+ userVerificationPreference.emplace(
+ ASAuthorizationPublicKeyCredentialUserVerificationPreferenceDiscouraged);
+ }
+
+ // Initialize the platform provider with the rpId.
+ ASAuthorizationPlatformPublicKeyCredentialProvider* platformProvider =
+ [[ASAuthorizationPlatformPublicKeyCredentialProvider alloc]
+ initWithRelyingPartyIdentifier:rpIdNS];
+ // Make a credential assertion request with the challenge.
+ ASAuthorizationPlatformPublicKeyCredentialAssertionRequest*
+ platformAssertionRequest = [platformProvider
+ createCredentialAssertionRequestWithChallenge:challengeNS];
+ [platformProvider release];
+ platformAssertionRequest.allowedCredentials =
+ platformAllowedCredentials;
+ if (userVerificationPreference.isSome()) {
+ platformAssertionRequest.userVerificationPreference =
+ *userVerificationPreference;
+ }
+
+ // Initialize the cross-platform provider with the rpId.
+ ASAuthorizationSecurityKeyPublicKeyCredentialProvider*
+ crossPlatformProvider =
+ [[ASAuthorizationSecurityKeyPublicKeyCredentialProvider alloc]
+ initWithRelyingPartyIdentifier:rpIdNS];
+ // Make a credential assertion request with the challenge.
+ ASAuthorizationSecurityKeyPublicKeyCredentialAssertionRequest*
+ crossPlatformAssertionRequest = [crossPlatformProvider
+ createCredentialAssertionRequestWithChallenge:challengeNS];
+ [crossPlatformProvider release];
+ crossPlatformAssertionRequest.allowedCredentials =
+ crossPlatformAllowedCredentials;
+ if (userVerificationPreference.isSome()) {
+ crossPlatformAssertionRequest.userVerificationPreference =
+ *userVerificationPreference;
+ }
+ nsTArray<uint8_t> clientDataHash;
+ nsresult rv = aArgs->GetClientDataHash(clientDataHash);
+ if (NS_FAILED(rv)) {
+ self->mSignPromise->Reject(rv);
+ return;
+ }
+ nsTArray<nsTArray<uint8_t>> unusedCredentialIds;
+ nsTArray<uint8_t> unusedTransports;
+ // allowList and allowListTransports won't actually get used.
+ self->PerformRequests(
+ @[ platformAssertionRequest, crossPlatformAssertionRequest ],
+ std::move(clientDataHash), std::move(allowList),
+ std::move(allowListTransports), browsingContextId);
+ }));
+}
+
+void MacOSWebAuthnService::FinishGetAssertion(
+ const nsTArray<uint8_t>& aCredentialId, const nsTArray<uint8_t>& aSignature,
+ const nsTArray<uint8_t>& aAuthenticatorData,
+ const nsTArray<uint8_t>& aUserHandle,
+ const Maybe<nsString>& aAuthenticatorAttachment) {
+ MOZ_ASSERT(NS_IsMainThread());
+ if (!mSignPromise) {
+ return;
+ }
+
+ RefPtr<WebAuthnSignResult> result(new WebAuthnSignResult(
+ aAuthenticatorData, Nothing(), aCredentialId, aSignature, aUserHandle,
+ aAuthenticatorAttachment));
+ Unused << mSignPromise->Resolve(result);
+ mSignPromise = nullptr;
+}
+
+void MacOSWebAuthnService::ReleasePlatformResources() {
+ MOZ_ASSERT(NS_IsMainThread());
+ [mCredentialManager release];
+ mCredentialManager = nil;
+ [mAuthorizationController release];
+ mAuthorizationController = nil;
+ [mRequestDelegate release];
+ mRequestDelegate = nil;
+ [mPresentationContextProvider release];
+ mPresentationContextProvider = nil;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::Reset() {
+ auto guard = mTransactionState.Lock();
+ if (guard->isSome()) {
+ if (guard->ref().pendingSignPromise.isSome()) {
+ // This request was never dispatched to the platform API, so
+ // we need to reject the promise ourselves.
+ guard->ref().pendingSignPromise.ref()->Reject(
+ NS_ERROR_DOM_NOT_ALLOWED_ERR);
+ }
+ guard->reset();
+ }
+ NS_DispatchToMainThread(NS_NewRunnableFunction(
+ "MacOSWebAuthnService::Cancel", [self = RefPtr{this}] {
+ // cancel results in the delegate's didCompleteWithError method being
+ // called, which will release all platform resources.
+ [self->mAuthorizationController cancel];
+ }));
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::GetIsUVPAA(bool* aAvailable) {
+ *aAvailable = true;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::Cancel(uint64_t aTransactionId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
+ const nsAString& aOrigin,
+ uint64_t* aRv) {
+ auto guard = mTransactionState.Lock();
+ if (guard->isNothing() ||
+ guard->ref().browsingContextId != aBrowsingContextId ||
+ guard->ref().pendingSignArgs.isNothing()) {
+ *aRv = 0;
+ return NS_OK;
+ }
+
+ nsString origin;
+ Unused << guard->ref().pendingSignArgs.ref()->GetOrigin(origin);
+ if (origin != aOrigin) {
+ *aRv = 0;
+ return NS_OK;
+ }
+
+ *aRv = guard->ref().transactionId;
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::GetAutoFillEntries(
+ uint64_t aTransactionId, nsTArray<RefPtr<nsIWebAuthnAutoFillEntry>>& aRv) {
+ auto guard = mTransactionState.Lock();
+ if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
+ guard->ref().pendingSignArgs.isNothing() ||
+ guard->ref().autoFillEntries.isNothing()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ aRv.Assign(guard->ref().autoFillEntries.ref());
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::SelectAutoFillEntry(
+ uint64_t aTransactionId, const nsTArray<uint8_t>& aCredentialId) {
+ auto guard = mTransactionState.Lock();
+ if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
+ guard->ref().pendingSignArgs.isNothing()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+
+ nsTArray<nsTArray<uint8_t>> allowList;
+ Unused << guard->ref().pendingSignArgs.ref()->GetAllowList(allowList);
+ if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ Maybe<nsTArray<uint8_t>> id;
+ id.emplace();
+ id.ref().Assign(aCredentialId);
+ DoGetAssertion(std::move(id), guard);
+
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
+ auto guard = mTransactionState.Lock();
+ if (guard->isNothing() || guard->ref().transactionId != aTransactionId ||
+ guard->ref().pendingSignArgs.isNothing()) {
+ return NS_ERROR_NOT_AVAILABLE;
+ }
+ DoGetAssertion(Nothing(), guard);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::PinCallback(uint64_t aTransactionId,
+ const nsACString& aPin) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::ResumeMakeCredential(uint64_t aTransactionId,
+ bool aForceNoneAttestation) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::SelectionCallback(uint64_t aTransactionId,
+ uint64_t aIndex) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::AddVirtualAuthenticator(
+ const nsACString& protocol, const nsACString& transport,
+ bool hasResidentKey, bool hasUserVerification, bool isUserConsenting,
+ bool isUserVerified, uint64_t* _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::RemoveVirtualAuthenticator(uint64_t authenticatorId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::AddCredential(uint64_t authenticatorId,
+ const nsACString& credentialId,
+ bool isResidentCredential,
+ const nsACString& rpId,
+ const nsACString& privateKey,
+ const nsACString& userHandle,
+ uint32_t signCount) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::GetCredentials(
+ uint64_t authenticatorId,
+ nsTArray<RefPtr<nsICredentialParameters>>& _retval) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::RemoveCredential(uint64_t authenticatorId,
+ const nsACString& credentialId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::RemoveAllCredentials(uint64_t authenticatorId) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::SetUserVerified(uint64_t authenticatorId,
+ bool isUserVerified) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+MacOSWebAuthnService::Listen() { return NS_ERROR_NOT_IMPLEMENTED; }
+
+NS_IMETHODIMP
+MacOSWebAuthnService::RunCommand(const nsACString& cmd) {
+ return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+} // namespace mozilla::dom
+
+NS_ASSUME_NONNULL_END