about:webauthn starts an interactive management session with nsIWebAuthnService::Listen(), which WebAuthnService routes unconditionally to the authrs service; authrs dispatches a status receiver that blocks on a channel recv() on the background I/O thread pool for the lifetime of the session. WebAuthnService::Cancel() only forwarded to a backend when there was a matching mActiveTransaction, which is created for modal register/sign operations but never for Listen(). So the page's beforeunload cancel(0) never reached authrs, the status receiver kept running and wedged a BgIOThreadPool thread, and the infinite-timeout background pool shutdown then hung the main thread at XPCOMShutdownThreads until the terminator watchdog crashed. Forward Cancel() to the authrs service for transactions not tracked as an mActiveTransaction, so an interactive session is torn down; authrs's cancel is a no-op when nothing matches. Differential Revision: https://phabricator.services.mozilla.com/D306269
544 lines
19 KiB
C++
544 lines
19 KiB
C++
/* 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/. */
|
|
|
|
#include "WebAuthnService.h"
|
|
|
|
#include "WebAuthnEnumStrings.h"
|
|
#include "WebAuthnTransportIdentifiers.h"
|
|
#include "mozilla/Services.h"
|
|
#include "mozilla/StaticPrefs_security.h"
|
|
#include "nsIObserverService.h"
|
|
#include "nsTextFormatter.h"
|
|
#include "nsThreadUtils.h"
|
|
|
|
namespace mozilla::dom {
|
|
|
|
already_AddRefed<nsIWebAuthnService> NewWebAuthnService() {
|
|
nsCOMPtr<nsIWebAuthnService> webauthnService(new WebAuthnService());
|
|
return webauthnService.forget();
|
|
}
|
|
|
|
NS_IMPL_ISUPPORTS(WebAuthnService, nsIWebAuthnService)
|
|
|
|
void WebAuthnService::ShowAttestationConsentPrompt(
|
|
const nsString& aOrigin, uint64_t aTransactionId,
|
|
uint64_t aBrowsingContextId) {
|
|
RefPtr<WebAuthnService> self = this;
|
|
#ifdef MOZ_WIDGET_ANDROID
|
|
// We don't have a way to prompt the user for consent on Android, so just
|
|
// assume consent not granted.
|
|
nsCOMPtr<nsIRunnable> runnable(
|
|
NS_NewRunnableFunction(__func__, [self, aTransactionId]() {
|
|
self->SetHasAttestationConsent(
|
|
aTransactionId,
|
|
StaticPrefs::security_webauthn_always_allow_direct_attestation());
|
|
}));
|
|
#else
|
|
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(
|
|
__func__, [self, aOrigin, aTransactionId, aBrowsingContextId]() {
|
|
if (StaticPrefs::security_webauthn_always_allow_direct_attestation()) {
|
|
self->SetHasAttestationConsent(aTransactionId, true);
|
|
return;
|
|
}
|
|
nsCOMPtr<nsIObserverService> os = services::GetObserverService();
|
|
if (!os) {
|
|
return;
|
|
}
|
|
const nsLiteralString jsonFmt =
|
|
u"{\"prompt\": {\"type\":\"attestation-consent\"},"_ns
|
|
u"\"origin\": \"%S\","_ns
|
|
u"\"tid\": %llu, \"browsingContextId\": %llu}"_ns;
|
|
nsString json;
|
|
nsTextFormatter::ssprintf(json, jsonFmt.get(), aOrigin.get(),
|
|
aTransactionId, aBrowsingContextId);
|
|
MOZ_ALWAYS_SUCCEEDS(
|
|
os->NotifyObservers(nullptr, "webauthn-prompt", json.get()));
|
|
}));
|
|
#endif
|
|
NS_DispatchToMainThread(runnable.forget());
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::MakeCredential(uint64_t aTransactionId,
|
|
uint64_t aBrowsingContextId,
|
|
nsIWebAuthnRegisterArgs* aArgs,
|
|
nsIWebAuthnRegisterPromise* aPromise) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aArgs);
|
|
MOZ_ASSERT(aPromise);
|
|
|
|
ResetActiveTransaction();
|
|
mActiveTransaction =
|
|
Some(TransactionState{.service = DefaultService(),
|
|
.transactionId = aTransactionId,
|
|
.parentRegisterPromise = Some(aPromise)});
|
|
|
|
// We may need to show an attestation consent prompt before we return a
|
|
// credential to WebAuthnTransactionParent, so we insert a new promise that
|
|
// chains to `aPromise` here.
|
|
|
|
nsString attestation;
|
|
(void)aArgs->GetAttestationConveyancePreference(attestation);
|
|
bool attestationRequested = !attestation.EqualsLiteral(
|
|
MOZ_WEBAUTHN_ATTESTATION_CONVEYANCE_PREFERENCE_NONE);
|
|
|
|
nsString origin;
|
|
(void)aArgs->GetOrigin(origin);
|
|
|
|
RefPtr<WebAuthnRegisterPromiseHolder> promiseHolder =
|
|
new WebAuthnRegisterPromiseHolder(GetCurrentSerialEventTarget());
|
|
|
|
RefPtr<WebAuthnService> self = this;
|
|
RefPtr<WebAuthnRegisterPromise> promise = promiseHolder->Ensure();
|
|
promise
|
|
->Then(
|
|
GetCurrentSerialEventTarget(), __func__,
|
|
[self, origin, aTransactionId, aBrowsingContextId,
|
|
attestationRequested](
|
|
const WebAuthnRegisterPromise::ResolveOrRejectValue& aValue) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (self->mActiveTransaction.isNothing()) {
|
|
return;
|
|
}
|
|
MOZ_ASSERT(
|
|
self->mActiveTransaction.ref().parentRegisterPromise.isSome());
|
|
MOZ_ASSERT(
|
|
self->mActiveTransaction.ref().registerResult.isNothing());
|
|
MOZ_ASSERT(
|
|
self->mActiveTransaction.ref().childRegisterRequest.Exists());
|
|
|
|
self->mActiveTransaction.ref().childRegisterRequest.Complete();
|
|
|
|
if (aValue.IsReject()) {
|
|
self->mActiveTransaction.ref()
|
|
.parentRegisterPromise.ref()
|
|
->Reject(aValue.RejectValue());
|
|
self->mActiveTransaction.reset();
|
|
return;
|
|
}
|
|
|
|
nsIWebAuthnRegisterResult* result = aValue.ResolveValue();
|
|
// We can return whatever result we have if the authenticator
|
|
// handled attestation consent for us.
|
|
bool attestationConsentPromptShown = false;
|
|
(void)result->GetAttestationConsentPromptShown(
|
|
&attestationConsentPromptShown);
|
|
if (attestationConsentPromptShown) {
|
|
self->mActiveTransaction.ref()
|
|
.parentRegisterPromise.ref()
|
|
->Resolve(result);
|
|
self->mActiveTransaction.reset();
|
|
return;
|
|
}
|
|
// If the RP requested attestation and the response contains
|
|
// identifying information, then we need to show a consent
|
|
// prompt.
|
|
bool resultIsIdentifying = true;
|
|
(void)result->HasIdentifyingAttestation(&resultIsIdentifying);
|
|
if (attestationRequested && resultIsIdentifying) {
|
|
self->mActiveTransaction.ref().registerResult = Some(result);
|
|
self->ShowAttestationConsentPrompt(origin, aTransactionId,
|
|
aBrowsingContextId);
|
|
return;
|
|
}
|
|
// In all other cases we strip out identifying information.
|
|
result->Anonymize();
|
|
self->mActiveTransaction.ref().parentRegisterPromise.ref()->Resolve(
|
|
result);
|
|
self->mActiveTransaction.reset();
|
|
})
|
|
->Track(mActiveTransaction.ref().childRegisterRequest);
|
|
|
|
nsresult rv = ActiveService()->MakeCredential(
|
|
aTransactionId, aBrowsingContextId, aArgs, promiseHolder);
|
|
if (NS_FAILED(rv)) {
|
|
promiseHolder->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::GetAssertion(uint64_t aTransactionId,
|
|
uint64_t aBrowsingContextId,
|
|
nsIWebAuthnSignArgs* aArgs,
|
|
nsIWebAuthnSignPromise* aPromise) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(aArgs);
|
|
MOZ_ASSERT(aPromise);
|
|
|
|
bool conditionallyMediated;
|
|
(void)aArgs->GetConditionallyMediated(&conditionallyMediated);
|
|
|
|
if (conditionallyMediated) {
|
|
// Conditional gets coexist with transactions from other browsing
|
|
// contexts. We hold them here until they are promoted to a modal flow via
|
|
// SelectAutoFillEntry or ResumeConditionalGet, or cancelled.
|
|
mConditionalGets.RemoveElementsBy(
|
|
[aBrowsingContextId](ConditionalGet& aEntry) {
|
|
if (aEntry.browsingContextId != aBrowsingContextId) {
|
|
return false;
|
|
}
|
|
aEntry.signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
return true;
|
|
});
|
|
mConditionalGets.AppendElement(ConditionalGet{
|
|
.transactionId = aTransactionId,
|
|
.browsingContextId = aBrowsingContextId,
|
|
.signArgs = aArgs,
|
|
.signPromise = aPromise,
|
|
});
|
|
|
|
// Defer the observer notification to the next event-loop turn so it fires
|
|
// after this call has fully returned to the caller.
|
|
nsCOMPtr<nsIRunnable> runnable(NS_NewRunnableFunction(__func__, []() {
|
|
nsCOMPtr<nsIObserverService> os = mozilla::services::GetObserverService();
|
|
if (os) {
|
|
os->NotifyObservers(nullptr, "webauthn:conditional-get-pending",
|
|
nullptr);
|
|
}
|
|
}));
|
|
NS_DispatchToMainThread(runnable.forget());
|
|
return NS_OK;
|
|
}
|
|
|
|
ResetActiveTransaction();
|
|
mActiveTransaction = Some(TransactionState{.service = DefaultService(),
|
|
.transactionId = aTransactionId});
|
|
|
|
#if defined(XP_MACOSX)
|
|
if (__builtin_available(macos 14.5, *)) {
|
|
// This branch is intentionally empty; using `!__builtin_available(...)`
|
|
// results in a compiler warning.
|
|
} else {
|
|
// On macOS < 14.5 the security key API doesn't handle the AppID
|
|
// extension. So we'll use authenticator-rs if it's likely that the
|
|
// request requires AppID. We consider it likely if 1) the AppID
|
|
// extension is present, 2) the allow list is non-empty, and 3) none of
|
|
// the allowed credentials use the "internal" or "hybrid" transport.
|
|
nsString appId;
|
|
nsresult rv = aArgs->GetAppId(appId);
|
|
if (rv == NS_OK) { // AppID is set
|
|
uint8_t transportSet = 0;
|
|
nsTArray<uint8_t> allowListTransports;
|
|
(void)aArgs->GetAllowListTransports(allowListTransports);
|
|
for (const uint8_t& transport : allowListTransports) {
|
|
transportSet |= transport;
|
|
}
|
|
uint8_t passkeyTransportMask =
|
|
MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_INTERNAL |
|
|
MOZ_WEBAUTHN_AUTHENTICATOR_TRANSPORT_ID_HYBRID;
|
|
if (allowListTransports.Length() > 0 &&
|
|
(transportSet & passkeyTransportMask) == 0) {
|
|
mActiveTransaction.ref().service = AuthrsService();
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
|
|
nsresult rv = ActiveService()->GetAssertion(
|
|
aTransactionId, aBrowsingContextId, aArgs, aPromise);
|
|
if (NS_FAILED(rv)) {
|
|
mActiveTransaction.reset();
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::GetIsUVPAA(bool* aAvailable) {
|
|
return DefaultService()->GetIsUVPAA(aAvailable);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::HasPendingConditionalGet(uint64_t aBrowsingContextId,
|
|
const nsAString& aOrigin,
|
|
uint64_t* aRv) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
for (const auto& entry : mConditionalGets) {
|
|
if (entry.browsingContextId != aBrowsingContextId) {
|
|
continue;
|
|
}
|
|
nsString entryOrigin;
|
|
(void)entry.signArgs->GetOrigin(entryOrigin);
|
|
if (entryOrigin == aOrigin) {
|
|
*aRv = entry.transactionId;
|
|
return NS_OK;
|
|
}
|
|
}
|
|
*aRv = 0;
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::GetAutoFillEntries(
|
|
uint64_t aTransactionId, nsIWebAuthnAutoFillEntriesCallback* aCallback) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
nsCOMPtr<nsIWebAuthnSignArgs> signArgs;
|
|
for (const auto& entry : mConditionalGets) {
|
|
if (entry.transactionId == aTransactionId) {
|
|
signArgs = entry.signArgs;
|
|
break;
|
|
}
|
|
}
|
|
if (!signArgs) {
|
|
aCallback->Reject(NS_ERROR_NOT_AVAILABLE);
|
|
return NS_OK;
|
|
}
|
|
nsString rpId;
|
|
nsTArray<nsTArray<uint8_t>> allowList;
|
|
(void)signArgs->GetRpId(rpId);
|
|
(void)signArgs->GetAllowList(allowList);
|
|
return DefaultService()->GetAutoFillEntriesForRpId(rpId, allowList,
|
|
aCallback);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::GetAutoFillEntriesForRpId(
|
|
const nsAString& aRpId, const nsTArray<nsTArray<uint8_t>>& aAllowList,
|
|
nsIWebAuthnAutoFillEntriesCallback* aCallback) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return DefaultService()->GetAutoFillEntriesForRpId(aRpId, aAllowList,
|
|
aCallback);
|
|
}
|
|
|
|
Maybe<WebAuthnService::ConditionalGet> WebAuthnService::TakeConditionalByTid(
|
|
uint64_t aTransactionId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
Maybe<ConditionalGet> found;
|
|
mConditionalGets.RemoveElementsBy([&](ConditionalGet& aEntry) {
|
|
if (aEntry.transactionId != aTransactionId) {
|
|
return false;
|
|
}
|
|
found = Some(std::move(aEntry));
|
|
return true;
|
|
});
|
|
return found;
|
|
}
|
|
|
|
nsresult WebAuthnService::DispatchConditionalGetAssertion(
|
|
const ConditionalGet& aPending, nsIWebAuthnSignArgs* aArgs) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
ResetActiveTransaction();
|
|
mActiveTransaction = Some(TransactionState{
|
|
.service = DefaultService(), .transactionId = aPending.transactionId});
|
|
nsresult rv = ActiveService()->GetAssertion(aPending.transactionId,
|
|
aPending.browsingContextId, aArgs,
|
|
aPending.signPromise);
|
|
if (NS_FAILED(rv)) {
|
|
aPending.signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
mActiveTransaction.reset();
|
|
return rv;
|
|
}
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::SelectAutoFillEntry(uint64_t aTransactionId,
|
|
const nsTArray<uint8_t>& aCredentialId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
|
|
if (found.isNothing()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
|
|
// If the original allow list is non-empty, the selected credential must be
|
|
// in it.
|
|
nsTArray<nsTArray<uint8_t>> allowList;
|
|
(void)found.ref().signArgs->GetAllowList(allowList);
|
|
if (!allowList.IsEmpty() && !allowList.Contains(aCredentialId)) {
|
|
found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
return NS_ERROR_FAILURE;
|
|
}
|
|
|
|
nsCOMPtr<nsIWebAuthnSignArgs> args;
|
|
nsresult rv = found.ref().signArgs->CloneWithSelectedCredential(
|
|
aCredentialId, getter_AddRefs(args));
|
|
if (NS_FAILED(rv)) {
|
|
found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
return rv;
|
|
}
|
|
return DispatchConditionalGetAssertion(found.ref(), args);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::ResumeConditionalGet(uint64_t aTransactionId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
|
|
if (found.isNothing()) {
|
|
return NS_ERROR_NOT_AVAILABLE;
|
|
}
|
|
return DispatchConditionalGetAssertion(found.ref(), found.ref().signArgs);
|
|
}
|
|
|
|
void WebAuthnService::RejectActiveRegisterPromise() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
MOZ_ASSERT(mActiveTransaction.isSome());
|
|
mActiveTransaction.ref().childRegisterRequest.DisconnectIfExists();
|
|
if (mActiveTransaction.ref().parentRegisterPromise.isSome()) {
|
|
mActiveTransaction.ref().parentRegisterPromise.ref()->Reject(
|
|
NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
}
|
|
}
|
|
|
|
void WebAuthnService::ResetActiveTransaction() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mActiveTransaction.isSome()) {
|
|
RejectActiveRegisterPromise();
|
|
ActiveService()->Reset();
|
|
mActiveTransaction.reset();
|
|
}
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::Reset() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
ResetActiveTransaction();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::Cancel(uint64_t aTransactionId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (Maybe<ConditionalGet> found = TakeConditionalByTid(aTransactionId);
|
|
found.isSome()) {
|
|
found.ref().signPromise->Reject(NS_ERROR_DOM_NOT_ALLOWED_ERR);
|
|
return NS_OK;
|
|
}
|
|
|
|
if (mActiveTransaction.isSome() &&
|
|
mActiveTransaction.ref().transactionId == aTransactionId) {
|
|
RejectActiveRegisterPromise();
|
|
ActiveService()->Cancel(aTransactionId);
|
|
mActiveTransaction.reset();
|
|
return NS_OK;
|
|
}
|
|
|
|
// about:webauthn drives an interactive management session through Listen()
|
|
// and RunCommand(), which always run on the authrs service and are not
|
|
// tracked as an mActiveTransaction. Its Cancel() must reach authrs too, or
|
|
// the session's status receiver (a blocking background task) is never torn
|
|
// down and blocks shutdown.
|
|
AuthrsService()->Cancel(aTransactionId);
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::SetHasAttestationConsent(uint64_t aTransactionId,
|
|
bool aHasConsent) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mActiveTransaction.isNothing() ||
|
|
mActiveTransaction.ref().transactionId != aTransactionId) {
|
|
// This could happen if the transaction was reset just when the prompt was
|
|
// receiving user input.
|
|
return NS_OK;
|
|
}
|
|
|
|
MOZ_ASSERT(mActiveTransaction.ref().parentRegisterPromise.isSome());
|
|
MOZ_ASSERT(mActiveTransaction.ref().registerResult.isSome());
|
|
MOZ_ASSERT(!mActiveTransaction.ref().childRegisterRequest.Exists());
|
|
|
|
if (!aHasConsent) {
|
|
mActiveTransaction.ref().registerResult.ref()->Anonymize();
|
|
}
|
|
mActiveTransaction.ref().parentRegisterPromise.ref()->Resolve(
|
|
mActiveTransaction.ref().registerResult.ref());
|
|
|
|
mActiveTransaction.reset();
|
|
return NS_OK;
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::PinCallback(uint64_t aTransactionId, const nsACString& aPin) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mActiveTransaction.isNothing() ||
|
|
mActiveTransaction.ref().transactionId != aTransactionId) {
|
|
return NS_OK;
|
|
}
|
|
return ActiveService()->PinCallback(aTransactionId, aPin);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::SelectionCallback(uint64_t aTransactionId, uint64_t aIndex) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
if (mActiveTransaction.isNothing() ||
|
|
mActiveTransaction.ref().transactionId != aTransactionId) {
|
|
return NS_OK;
|
|
}
|
|
return ActiveService()->SelectionCallback(aTransactionId, aIndex);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::AddVirtualAuthenticator(
|
|
const nsACString& aProtocol, const nsACString& aTransport,
|
|
bool aHasResidentKey, bool aHasUserVerification, bool aIsUserConsenting,
|
|
bool aIsUserVerified, nsACString& aRetval) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->AddVirtualAuthenticator(
|
|
aProtocol, aTransport, aHasResidentKey, aHasUserVerification,
|
|
aIsUserConsenting, aIsUserVerified, aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::RemoveVirtualAuthenticator(
|
|
const nsACString& aAuthenticatorId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->RemoveVirtualAuthenticator(aAuthenticatorId);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::AddCredential(const nsACString& aAuthenticatorId,
|
|
const nsACString& aCredentialId,
|
|
bool aIsResidentCredential,
|
|
const nsACString& aRpId,
|
|
const nsACString& aPrivateKey,
|
|
const nsACString& aUserHandle,
|
|
uint32_t aSignCount) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->AddCredential(aAuthenticatorId, aCredentialId,
|
|
aIsResidentCredential, aRpId,
|
|
aPrivateKey, aUserHandle, aSignCount);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::GetCredentials(
|
|
const nsACString& aAuthenticatorId,
|
|
nsTArray<RefPtr<nsICredentialParameters>>& aRetval) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->GetCredentials(aAuthenticatorId, aRetval);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::RemoveCredential(const nsACString& aAuthenticatorId,
|
|
const nsACString& aCredentialId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->RemoveCredential(aAuthenticatorId, aCredentialId);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::RemoveAllCredentials(const nsACString& aAuthenticatorId) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->RemoveAllCredentials(aAuthenticatorId);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::SetUserVerified(const nsACString& aAuthenticatorId,
|
|
bool aIsUserVerified) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->SetUserVerified(aAuthenticatorId, aIsUserVerified);
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::Listen() {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->Listen();
|
|
}
|
|
|
|
NS_IMETHODIMP
|
|
WebAuthnService::RunCommand(const nsACString& aCmd) {
|
|
MOZ_ASSERT(NS_IsMainThread());
|
|
return AuthrsService()->RunCommand(aCmd);
|
|
}
|
|
|
|
} // namespace mozilla::dom
|