gecko-dev/dom/webauthn/MacOSWebAuthnService.mm
Dana Keeler ff461e274d Bug 1909441 - webauthn on macOS: use user.displayName rather than user.name, as intended r=jschanck
This was probably just a copy/paste issue. The intent was to use the
displayName for the displayName parameter when creating a cross-platform
webauthn credential on macOS.

Differential Revision: https://phabricator.services.mozilla.com/D218466
2024-08-02 20:55:51 +00:00

1263 lines
52 KiB
Plaintext

/* -*- 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 {
// The platform didn't tell us what transport was used, but we know it
// wasn't the internal transport. The transport response is not signed by
// the authenticator. It represents the "transports that the authenticator
// is believed to support, or an empty sequence if the information is
// unavailable". We believe macOS supports usb, so we return usb.
transports.AppendElement(u"usb"_ns);
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}]() {
// Bug 1884574 - The Reset() call above should have cancelled any
// transactions that were dispatched to the platform, the platform
// should have called didCompleteWithError, and didCompleteWithError
// should have rejected the pending promise. However, in some scenarios,
// the platform fails to call the callback, and this leads to a
// diagnostic assertion failure when we drop `mRegisterPromise`. Avoid
// this by aborting the transaction here.
if (self->mRegisterPromise) {
MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug,
("MacOSAuthenticatorRequestDelegate::MakeCredential: "
"platform failed to call callback"));
self->AbortTransaction(NS_ERROR_DOM_ABORT_ERR);
}
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->GetUserDisplayName(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);
// This mapping needs to be reviewed if values are added to the
// UserVerificationRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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, so this is
// only used for security keys.
ASAuthorizationPublicKeyCredentialAttestationKind attestationPreference;
nsAutoString mozAttestationPreference;
Unused << aArgs->GetAttestationConveyancePreference(
mozAttestationPreference);
if (mozAttestationPreference.EqualsLiteral(
MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_INDIRECT)) {
attestationPreference =
ASAuthorizationPublicKeyCredentialAttestationKindIndirect;
} else if (mozAttestationPreference.EqualsLiteral(
MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_DIRECT)) {
attestationPreference =
ASAuthorizationPublicKeyCredentialAttestationKindDirect;
} else if (
mozAttestationPreference.EqualsLiteral(
MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_ENTERPRISE)) {
attestationPreference =
ASAuthorizationPublicKeyCredentialAttestationKindEnterprise;
} else {
attestationPreference =
ASAuthorizationPublicKeyCredentialAttestationKindNone;
}
ASAuthorizationPublicKeyCredentialResidentKeyPreference
residentKeyPreference;
nsAutoString mozResidentKey;
Unused << aArgs->GetResidentKey(mozResidentKey);
// This mapping needs to be reviewed if values are added to the
// ResidentKeyRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
if (mozResidentKey.EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_REQUIRED)) {
residentKeyPreference =
ASAuthorizationPublicKeyCredentialResidentKeyPreferenceRequired;
} else if (mozResidentKey.EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_PREFERRED)) {
residentKeyPreference =
ASAuthorizationPublicKeyCredentialResidentKeyPreferencePreferred;
} else {
MOZ_ASSERT(mozResidentKey.EqualsLiteral(
MOZ_WEBAUTHN_RESIDENT_KEY_REQUIREMENT_DISCOURAGED));
residentKeyPreference =
ASAuthorizationPublicKeyCredentialResidentKeyPreferenceDiscouraged;
}
// 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];
// The API doesn't support attestation for platform passkeys
platformRegistrationRequest.attestationPreference =
ASAuthorizationPublicKeyCredentialAttestationKindNone;
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;
crossPlatformRegistrationRequest.residentKeyPreference =
residentKeyPreference;
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 {
// Bug 1884574 - This AbortTransaction call is necessary.
// See comment in MacOSWebAuthnService::MakeCredential.
if (self->mSignPromise) {
MOZ_LOG(gMacOSWebAuthnServiceLog, mozilla::LogLevel::Debug,
("MacOSAuthenticatorRequestDelegate::DoGetAssertion: "
"platform failed to call callback"));
self->AbortTransaction(NS_ERROR_DOM_ABORT_ERR);
}
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);
}
// Compute the union of the transport sets.
uint8_t transports = 0;
for (uint8_t credTransports : allowListTransports) {
if (credTransports == 0) {
// treat the empty transport set as "all transports".
transports = ~0;
break;
}
transports |= credTransports;
}
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);
// This mapping needs to be reviewed if values are added to the
// UserVerificationRequirement enum.
static_assert(MOZ_WEBAUTHN_ENUM_STRINGS_VERSION == 3);
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;
}
if (__builtin_available(macos 13.5, *)) {
// Show the hybrid transport option if (1) we have no transport hints
// or (2) at least one allow list entry lists the hybrid transport.
bool shouldShowHybridTransport =
!transports ||
(transports & MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID);
platformAssertionRequest.shouldShowHybridTransport =
shouldShowHybridTransport;
}
// 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::SetHasAttestationConsent(uint64_t aTransactionId,
bool aHasConsent) {
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