Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/credentialmanagement/identity/   (Firefox Browser Version 153.0.1©)  Datei vom 27.6.2026 mit Größe 50 kB image not shown  

Quelle  WebIdentityParent.cpp

  Sprache: 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 "mozilla/dom/WebIdentityParent.h"

#include "mozilla/Components.h"
#include "mozilla/IdentityCredentialRequestManager.h"
#include "mozilla/dom/IdentityNetworkHelpers.h"
#include "mozilla/dom/NavigatorLogin.h"
#include "mozilla/dom/WindowGlobalParent.h"
#include "nsIEffectiveTLDService.h"
#include "nsIIdentityCredentialPromptService.h"
#include "nsIIdentityCredentialStorageService.h"
#include "nsIXPConnect.h"
#include "nsScriptSecurityManager.h"
#include "nsURLHelper.h"

namespace mozilla::dom {

void WebIdentityParent::ActorDestroy(ActorDestroyReason aWhy) {
  MOZ_ASSERT(NS_IsMainThread());
}

mozilla::ipc::IPCResult WebIdentityParent::RecvRequestCancel() {
  MOZ_ASSERT(NS_IsMainThread());
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvGetIdentityCredential(
    IdentityCredentialRequestOptions&& aOptions,
    const CredentialMediationRequirement& aMediationRequirement,
    bool aHasUserActivation, const GetIdentityCredentialResolver& aResolver) {
  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
  if (!manager) {
    aResolver(NS_ERROR_FAILURE);
    return IPC_OK();
  }
  identity::GetCredentialInMainProcess(
      manager->DocumentPrincipal(), this, std::move(aOptions),
      aMediationRequirement, aHasUserActivation)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aResolver](const IPCIdentityCredential& aResult) {
            return aResolver(aResult);
          },
          [aResolver](nsresult aErr) { aResolver(aErr); });
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvDisconnectIdentityCredential(
    const IdentityCredentialDisconnectOptions& aOptions,
    const DisconnectIdentityCredentialResolver& aResolver) {
  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
  if (!manager) {
    aResolver(NS_ERROR_FAILURE);
    return IPC_OK();
  }
  identity::DisconnectInMainProcess(manager->DocumentPrincipal(), aOptions)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aResolver](const bool& aResult) { aResolver(NS_OK); },
          [aResolver](nsresult aErr) { aResolver(aErr); });
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvPreventSilentAccess(
    const PreventSilentAccessResolver& aResolver) {
  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
  if (!manager) {
    aResolver(NS_ERROR_FAILURE);
    return IPC_OK();
  }
  nsIPrincipal* principal = manager->DocumentPrincipal();
  if (principal) {
    nsCOMPtr<nsIPermissionManager> permissionManager =
        components::PermissionManager::Service();
    if (permissionManager) {
      permissionManager->RemoveFromPrincipal(
          principal, "credential-allow-silent-access"_ns);
      aResolver(NS_OK);
      return IPC_OK();
    }
  }

  aResolver(NS_ERROR_NOT_AVAILABLE);
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvSetLoginStatus(
    LoginStatus aStatus, const SetLoginStatusResolver& aResolver) {
  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
  if (!manager) {
    aResolver(NS_ERROR_FAILURE);
    return IPC_OK();
  }
  nsIPrincipal* principal = manager->DocumentPrincipal();
  if (!principal) {
    aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR);
    return IPC_OK();
  }
  nsresult rv = NavigatorLogin::SetLoginStatus(principal, aStatus);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvResolveContinuationWindow(
    nsCString&& aToken, IdentityResolveOptions&& aOptions,
    const ResolveContinuationWindowResolver& aResolver) {
  // Pass the resolution on to the ICRM to handle it.
  // Faithfully convey its error in resolution.
  IdentityCredentialRequestManager* requestManager =
      IdentityCredentialRequestManager::GetInstance();
  if (!requestManager) {
    aResolver(NS_ERROR_NOT_AVAILABLE);
    return IPC_OK();
  }
  nsresult rv = requestManager->MaybeResolvePopup(this, aToken, aOptions);
  aResolver(rv);
  return IPC_OK();
}

mozilla::ipc::IPCResult WebIdentityParent::RecvIsActiveContinuationWindow(
    const IsActiveContinuationWindowResolver& aResolver) {
  IdentityCredentialRequestManager* requestManager =
      IdentityCredentialRequestManager::GetInstance();
  if (!requestManager) {
    aResolver(false);
    return IPC_OK();
  }
  aResolver(requestManager->IsActivePopup(this));
  return IPC_OK();
}

namespace identity {

nsresult CanSilentlyCollect(nsIPrincipal* aPrincipal,
                            nsIPrincipal* aIDPPrincipal, bool* aResult) {
  NS_ENSURE_ARG_POINTER(aPrincipal);
  NS_ENSURE_ARG_POINTER(aIDPPrincipal);
  nsCString origin;
  nsresult rv = aIDPPrincipal->GetOrigin(origin);
  NS_ENSURE_SUCCESS(rv, rv);

  uint32_t permit = nsIPermissionManager::UNKNOWN_ACTION;
  nsCOMPtr<nsIPermissionManager> permissionManager =
      components::PermissionManager::Service();
  if (!permissionManager) {
    return NS_ERROR_SERVICE_NOT_AVAILABLE;
  }

  rv = permissionManager->TestPermissionFromPrincipal(
      aPrincipal, "credential-allow-silent-access^"_ns + origin, &permit);
  NS_ENSURE_SUCCESS(rv, rv);
  *aResult = (permit == nsIPermissionManager::ALLOW_ACTION);
  if (!*aResult) {
    return NS_OK;
  }
  rv = permissionManager->TestPermissionFromPrincipal(
      aPrincipal, "credential-allow-silent-access"_ns, &permit);
  NS_ENSURE_SUCCESS(rv, rv);
  *aResult = permit == nsIPermissionManager::ALLOW_ACTION;
  return NS_OK;
}

// static
RefPtr<GetIPCIdentityCredentialPromise> GetCredentialInMainProcess(
    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    IdentityCredentialRequestOptions&& aOptions,
    const CredentialMediationRequirement& aMediationRequirement,
    bool aHasUserActivation) {
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(aRelyingParty);

  if (aOptions.mMode == IdentityCredentialRequestOptionsMode::Active) {
    // If the site is operating in "Active Mode" we need user activation  to
    // proceed.
    if (!aHasUserActivation) {
      return GetIPCIdentityCredentialPromise::CreateAndReject(
          NS_ERROR_DOM_NETWORK_ERR, __func__);
    }
  } else {
    // Otherwise we are in "Passive Mode" and since this doesn't require user
    // activation we constrain the credentials that are allowed to be be shown
    // to the user so they don't get annoyed.
    // Specifically, they need to have this credential registered for use on
    // this website.
    nsresult rv;
    nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
        mozilla::components::IdentityCredentialStorageService::Service(&rv);
    if (NS_WARN_IF(!icStorageService)) {
      return GetIPCIdentityCredentialPromise::CreateAndReject(rv, __func__);
    }
    aOptions.mProviders.RemoveElementsBy(
        [icStorageService,
         aPrincipal](const IdentityProviderRequestOptions& provider) {
          nsCString configLocation = provider.mConfigURL;
          nsCOMPtr<nsIURI> configURI;
          nsresult rv = NS_NewURI(getter_AddRefs(configURI), configLocation);
          if (NS_FAILED(rv)) {
            return true;
          }
          bool thirdParty = true;
          rv = aPrincipal->IsThirdPartyURI(configURI, &thirdParty);
          if (!thirdParty) {
            return false;
          }
          nsCOMPtr<nsIPrincipal> idpPrincipal =
              BasePrincipal::CreateContentPrincipal(
                  configURI, aPrincipal->OriginAttributesRef());
          bool connected = false;
          rv =
              icStorageService->Connected(aPrincipal, idpPrincipal, &connected);
          if (NS_FAILED(rv)) {
            return true;
          }
          return !connected;
        });
  }

  if (aOptions.mProviders.IsEmpty()) {
    return GetIPCIdentityCredentialPromise::CreateAndReject(
        NS_ERROR_NOT_AVAILABLE, __func__);
  }

  RefPtr<GetIPCIdentityCredentialPromise::Private> result =
      new GetIPCIdentityCredentialPromise::Private(__func__);
  DiscoverFromExternalSourceInMainProcess(aPrincipal, aRelyingParty, aOptions,
                                          aMediationRequirement)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [result](const IPCIdentityCredential& credential) {
            result->Resolve(credential, __func__);
          },
          [result](nsresult rv) { result->Reject(rv, __func__); });
  return result.forget();
}

// static
RefPtr<GetIPCIdentityCredentialPromise> DiscoverFromExternalSourceInMainProcess(
    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    const IdentityCredentialRequestOptions& aOptions,
    const CredentialMediationRequirement& aMediationRequirement) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(aRelyingParty);

  // Make sure we have providers.
  if (aOptions.mProviders.Length() < 1) {
    return GetIPCIdentityCredentialPromise::CreateAndReject(
        NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
  }

  nsCOMPtr<nsIPrincipal> principal(aPrincipal);
  RefPtr<CanonicalBrowsingContext> browsingContext =
      aRelyingParty->MaybeBrowsingContext();
  if (!browsingContext) {
    return GetIPCIdentityCredentialPromise::CreateAndReject(
        NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
  }
  RefPtr<WebIdentityParent> relyingParty = aRelyingParty;

  RefPtr<GetIPCIdentityCredentialPromise::Private> result =
      new GetIPCIdentityCredentialPromise::Private(__func__);

  RefPtr<nsITimer> timeout;
  if (StaticPrefs::
          dom_security_credentialmanagement_identity_reject_delay_enabled()) {
    nsresult rv = NS_NewTimerWithCallback(
        getter_AddRefs(timeout),
        [=](auto) {
          result->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
          CloseUserInterface(browsingContext);
        },
        StaticPrefs::
            dom_security_credentialmanagement_identity_reject_delay_duration_ms(),
        nsITimer::TYPE_ONE_SHOT, "IdentityCredentialTimeoutCallback"_ns);
    if (NS_WARN_IF(NS_FAILED(rv))) {
      result->Reject(NS_ERROR_FAILURE, __func__);
      return result.forget();
    }
  }

  // Construct an array of requests to fetch manifests for every provider.
  // We need this to show their branding information
  nsTArray<RefPtr<GetManifestPromise>> manifestPromises;
  for (const IdentityProviderRequestOptions& provider : aOptions.mProviders) {
    RefPtr<GetManifestPromise> manifest = FetchManifest(principal, provider);
    manifestPromises.AppendElement(manifest);
  }

  // We use AllSettled here so that failures will be included- we use default
  // values there.
  GetManifestPromise::AllSettled(GetCurrentSerialEventTarget(),
                                 manifestPromises)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [browsingContext, aOptions](
              const GetManifestPromise::AllSettledPromiseType::ResolveValueType&
                  aResults) {
            // Convert the
            // GetManifestPromise::AllSettledPromiseType::ResolveValueType to a
            // Sequence<MozPromise>
            CopyableTArray<MozPromise<IdentityProviderAPIConfig, nsresult,
                                      true>::ResolveOrRejectValue>
                results = aResults;
            const Sequence<MozPromise<IdentityProviderAPIConfig, nsresult,
                                      true>::ResolveOrRejectValue>
                resultsSequence(std::move(results));

            // If we can skip the provider check, because there is only one
            // option and it is already linked, do so!
            Maybe<IdentityProviderRequestOptionsWithManifest>
                autoSelectedIdentityProvider =
                    SkipAccountChooser(aOptions.mProviders, resultsSequence);
            if (autoSelectedIdentityProvider.isSome()) {
              return GetIdentityProviderRequestOptionsWithManifestPromise::
                  CreateAndResolve(autoSelectedIdentityProvider.extract(),
                                   __func__);
            }

            // The user picks from the providers
            return PromptUserToSelectProvider(
                browsingContext, aOptions.mProviders, resultsSequence);
          },
          [](bool error) {
            return GetIdentityProviderRequestOptionsWithManifestPromise::
                CreateAndReject(NS_ERROR_FAILURE, __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aMediationRequirement, principal,
           relyingParty](const IdentityProviderRequestOptionsWithManifest&
                             providerAndManifest) {
            IdentityProviderAPIConfig manifest;
            IdentityProviderRequestOptions provider;
            std::tie(provider, manifest) = providerAndManifest;
            return CreateCredentialDuringDiscovery(principal, relyingParty,
                                                   provider, manifest,
                                                   aMediationRequirement);
          },
          [](nsresult error) {
            return GetIPCIdentityCredentialPromise::CreateAndReject(error,
                                                                    __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [result, timeout = std::move(timeout)](
              const GetIPCIdentityCredentialPromise::ResolveOrRejectValue&&
                  value) {
            // Resolve the result
            result->ResolveOrReject(value, __func__);

            // Cancel the timer (if it is still pending) and
            // release the hold on the variables leaked into the timer.
            if (timeout &&
                StaticPrefs::
                    dom_security_credentialmanagement_identity_reject_delay_enabled()) {
              timeout->Cancel();
            }
          });

  return result;
}

// static
Maybe<IdentityProviderRequestOptionsWithManifest> SkipAccountChooser(
    const Sequence<IdentityProviderRequestOptions>& aProviders,
    const Sequence<GetManifestPromise::ResolveOrRejectValue>& aManifests) {
  if (aProviders.Length() != 1) {
    return Nothing();
  }
  if (aManifests.Length() != 1) {
    return Nothing();
  }
  if (!aManifests.ElementAt(0).IsResolve()) {
    return Nothing();
  }
  const IdentityProviderRequestOptions& resolvedProvider =
      aProviders.ElementAt(0);
  const IdentityProviderAPIConfig& resolvedManifest =
      aManifests.ElementAt(0).ResolveValue();
  return Some(std::make_tuple(resolvedProvider, resolvedManifest));
}

// static
Maybe<IdentityProviderAccount> FindAccountToReauthenticate(
    const IdentityProviderRequestOptions& aProvider, nsIPrincipal* aRPPrincipal,
    const IdentityProviderAccountList& aAccountList) {
  if (!aAccountList.mAccounts.WasPassed()) {
    return Nothing();
  }

  nsresult rv;
  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
      mozilla::components::IdentityCredentialStorageService::Service(&rv);
  if (NS_WARN_IF(!icStorageService)) {
    return Nothing();
  }

  Maybe<IdentityProviderAccount> result = Nothing();
  for (const IdentityProviderAccount& account :
       aAccountList.mAccounts.Value()) {
    // Don't reauthenticate accounts that have an approved clients list but no
    // matching clientID from navigator.credentials.get's argument
    if (account.mApproved_clients.WasPassed()) {
      if (!account.mApproved_clients.Value().Contains(
              NS_ConvertUTF8toUTF16(aProvider.mClientId))) {
        continue;
      }
    }

    RefPtr<nsIURI> configURI;
    nsresult rv = NS_NewURI(getter_AddRefs(configURI), aProvider.mConfigURL);
    if (NS_FAILED(rv)) {
      continue;
    }
    nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
        configURI, aRPPrincipal->OriginAttributesRef());

    // Don't reauthenticate unconnected accounts
    bool connected = false;
    rv = icStorageService->Connected(aRPPrincipal, idpPrincipal, &connected);
    if (NS_WARN_IF(NS_FAILED(rv)) || !connected) {
      continue;
    }

    // Don't reauthenticate if silent access is disabled
    bool silentAllowed = false;
    rv = CanSilentlyCollect(aRPPrincipal, idpPrincipal, &silentAllowed);
    if (!NS_WARN_IF(NS_FAILED(rv)) && !silentAllowed) {
      continue;
    }

    // We only auto-reauthenticate if we have one candidate.
    if (result.isSome()) {
      return Nothing();
    }

    // Remember our first candidate so we can return it after
    // this loop, or return nothing if we find another!
    result = Some(account);
  }

  return result;
}

// static
RefPtr<GetIPCIdentityCredentialPromise> CreateCredentialDuringDiscovery(
    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    const IdentityProviderRequestOptions& aProvider,
    const IdentityProviderAPIConfig& aManifest,
    const CredentialMediationRequirement& aMediationRequirement) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(aPrincipal);
  MOZ_ASSERT(aRelyingParty);

  nsCOMPtr<nsIPrincipal> argumentPrincipal = aPrincipal;
  RefPtr<WebIdentityParent> relyingParty(aRelyingParty);
  RefPtr<CanonicalBrowsingContext> browsingContext(
      aRelyingParty->MaybeBrowsingContext());
  if (!browsingContext) {
    return GetIPCIdentityCredentialPromise::CreateAndReject(NS_ERROR_FAILURE,
                                                            __func__);
  }

  return FetchAccountList(argumentPrincipal, aProvider, aManifest)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [argumentPrincipal, browsingContext, aManifest, aMediationRequirement,
           aProvider](
              const std::tuple<IdentityProviderAPIConfig,
                               IdentityProviderAccountList>& promiseResult) {
            IdentityProviderAPIConfig currentManifest;
            IdentityProviderAccountList accountList;
            std::tie(currentManifest, accountList) = promiseResult;
            if (!accountList.mAccounts.WasPassed() ||
                accountList.mAccounts.Value().Length() == 0) {
              return GetAccountPromise::CreateAndReject(NS_ERROR_FAILURE,
                                                        __func__);
            }

            // Remove accounts without a matching login hint if one was provided
            // in the JS call
            if (aProvider.mLoginHint.WasPassed()) {
              const nsCString& loginHint = aProvider.mLoginHint.Value();
              accountList.mAccounts.Value().RemoveElementsBy(
                  [loginHint](const IdentityProviderAccount& account) {
                    if (!account.mLogin_hints.WasPassed() ||
                        account.mLogin_hints.Value().Length() == 0) {
                      return true;
                    }
                    if (account.mLogin_hints.Value().Contains(loginHint)) {
                      return false;
                    }
                    return true;
                  });
            }

            // Remove accounts without a matching domain hint if one was
            // provided in the JS call
            if (aProvider.mDomainHint.WasPassed()) {
              const nsCString& domainHint = aProvider.mDomainHint.Value();
              accountList.mAccounts.Value().RemoveElementsBy(
                  [domainHint](const IdentityProviderAccount& account) {
                    if (!account.mDomain_hints.WasPassed() ||
                        account.mDomain_hints.Value().Length() == 0) {
                      return true;
                    }
                    // The domain hint "any" matches any hint.
                    if (domainHint.Equals("any")) {
                      return false;
                    }
                    if (account.mDomain_hints.Value().Contains(domainHint)) {
                      return false;
                    }
                    return true;
                  });
            }

            // Remove accounts without a matching account hint if a label was
            // provided in the IDP config
            if (currentManifest.mAccount_label.WasPassed()) {
              const nsCString& accountHint =
                  currentManifest.mAccount_label.Value();
              accountList.mAccounts.Value().RemoveElementsBy(
                  [accountHint](const IdentityProviderAccount& account) {
                    if (!account.mLabel_hints.WasPassed() ||
                        account.mLabel_hints.Value().Length() == 0) {
                      return true;
                    }
                    if (account.mLabel_hints.Value().Contains(accountHint)) {
                      return false;
                    }
                    return true;
                  });
            }

            // If we can skip showing the user any UI by just doing a silent
            // renewal, do so.
            if (aMediationRequirement !=
                CredentialMediationRequirement::Required) {
              Maybe<IdentityProviderAccount> reauthenticatingAccount =
                  FindAccountToReauthenticate(aProvider, argumentPrincipal,
                                              accountList);
              if (reauthenticatingAccount.isSome()) {
                return GetAccountPromise::CreateAndResolve(
                    std::make_tuple(aManifest,
                                    reauthenticatingAccount.extract(), true),
                    __func__);
              }
            }

            if (accountList.mAccounts.Value().Length() < 1) {
              return GetAccountPromise::CreateAndReject(
                  NS_ERROR_DOM_NETWORK_ERR, __func__);
            }

            return PromptUserToSelectAccount(browsingContext, accountList,
                                             aProvider, currentManifest);
          },
          [](nsresult error) {
            return GetAccountPromise::CreateAndReject(error, __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [argumentPrincipal, aProvider,
           relyingParty](const std::tuple<IdentityProviderAPIConfig,
                                          IdentityProviderAccount, const bool>&
                             promiseResult) {
            IdentityProviderAPIConfig currentManifest;
            IdentityProviderAccount account;
            bool isAutoSelected;
            std::tie(currentManifest, account, isAutoSelected) = promiseResult;
            return FetchToken(argumentPrincipal, relyingParty, aProvider,
                              currentManifest, account, isAutoSelected);
          },
          [](nsresult error) {
            return GetTokenPromise::CreateAndReject(error, __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [argumentPrincipal,
           aProvider](const std::tuple<nsCString, nsCString, const bool>&
                          promiseResult) {
            nsCString token;
            nsCString accountId;
            bool isAutoSelected;
            std::tie(token, accountId, isAutoSelected) = promiseResult;
            IPCIdentityCredential credential;
            credential.token() = Some(token);
            credential.id() = NS_ConvertUTF8toUTF16(accountId);
            credential.isAutoSelected() = isAutoSelected;
            credential.configURL() = aProvider.mConfigURL;
            // We always make sure accounts are linked after we successfully
            // fetch a token
            nsresult rv = LinkAccount(argumentPrincipal, accountId, aProvider);
            if (NS_FAILED(rv)) {
              return GetIPCIdentityCredentialPromise::CreateAndReject(rv,
                                                                      __func__);
            }
            return GetIPCIdentityCredentialPromise::CreateAndResolve(credential,
                                                                     __func__);
          },
          [browsingContext](nsresult error) {
            CloseUserInterface(browsingContext);
            return GetIPCIdentityCredentialPromise::CreateAndReject(error,
                                                                    __func__);
          });
}

// static
RefPtr<GetRootManifestPromise> FetchRootManifest(
    nsIPrincipal* aPrincipal, const IdentityProviderConfig& aProvider) {
  MOZ_ASSERT(XRE_IsParentProcess());
  if (StaticPrefs::
          dom_security_credentialmanagement_identity_test_ignore_well_known()) {
    return GetRootManifestPromise::CreateAndResolve(Nothing(), __func__);
  }

  // Build the URL
  nsCString configLocation = aProvider.mConfigURL;
  nsCOMPtr<nsIURI> configURI;
  nsresult rv = NS_NewURI(getter_AddRefs(configURI), configLocation);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetRootManifestPromise::CreateAndReject(rv, __func__);
  }
  RefPtr<nsIEffectiveTLDService> etld =
      mozilla::components::EffectiveTLD::Service();
  if (!etld) {
    return GetRootManifestPromise::CreateAndReject(
        NS_ERROR_SERVICE_NOT_AVAILABLE, __func__);
  }
  nsCString manifestURIString;
  rv = etld->GetSite(configURI, manifestURIString);
  if (NS_FAILED(rv)) {
    return GetRootManifestPromise::CreateAndReject(NS_ERROR_INVALID_ARG,
                                                   __func__);
  }
  nsAutoCString wellKnownPathForTesting;
  rv = Preferences::GetCString(
      "dom.security.credentialmanagement.identity.test_well_known_path",
      wellKnownPathForTesting);
  if (NS_SUCCEEDED(rv) && !wellKnownPathForTesting.IsVoid() &&
      !wellKnownPathForTesting.IsEmpty()) {
    manifestURIString.Append(wellKnownPathForTesting);
  } else {
    manifestURIString.AppendLiteral("/.well-known/web-identity");
  }
  nsCOMPtr<nsIURI> manifestURI;
  rv = NS_NewURI(getter_AddRefs(manifestURI), manifestURIString, nullptr);
  if (NS_FAILED(rv)) {
    return GetRootManifestPromise::CreateAndReject(NS_ERROR_INVALID_ARG,
                                                   __func__);
  }

  // We actually don't need to do any of this well-known stuff if the
  // requesting principal is same-site to the manifest URI. There is no
  // privacy risk in that case, because the requests could be sent with
  // their unpartitioned cookies anyway.
  if (!aPrincipal->GetIsNullPrincipal()) {
    bool thirdParty = true;
    rv = aPrincipal->IsThirdPartyURI(manifestURI, &thirdParty);
    if (NS_SUCCEEDED(rv) && !thirdParty) {
      return GetRootManifestPromise::CreateAndResolve(Nothing(), __func__);
    }
  }

  return IdentityNetworkHelpers::FetchWellKnownHelper(manifestURI, aPrincipal)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aProvider](const IdentityProviderWellKnown& manifest) {
            // Resolve whether or not the argument URL is found in
            // the well-known
            if (manifest.mProvider_urls.Contains(aProvider.mConfigURL)) {
              return GetRootManifestPromise::CreateAndResolve(Some(manifest),
                                                              __func__);
            }
            return GetRootManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
                                                           __func__);
          },
          [](nsresult error) {
            return GetRootManifestPromise::CreateAndReject(error, __func__);
          });
}

// static
RefPtr<GetManifestPromise> FetchManifest(
    nsIPrincipal* aPrincipal, const IdentityProviderConfig& aProvider) {
  MOZ_ASSERT(XRE_IsParentProcess());

  nsCOMPtr<nsIPrincipal> requestingPrincipal(aPrincipal);
  return FetchRootManifest(aPrincipal, aProvider)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aProvider,
           requestingPrincipal](Maybe<IdentityProviderWellKnown> rootManifest) {
            // Build the URL
            nsCString configLocation = aProvider.mConfigURL;
            nsCOMPtr<nsIURI> manifestURI;
            nsresult rv =
                NS_NewURI(getter_AddRefs(manifestURI), configLocation, nullptr);
            if (NS_FAILED(rv)) {
              return MozPromise<std::tuple<Maybe<IdentityProviderWellKnown>,
                                           IdentityProviderAPIConfig>,
                                nsresult,
                                true>::CreateAndReject(NS_ERROR_INVALID_ARG,
                                                       __func__);
            }
            return IdentityNetworkHelpers::FetchConfigHelper(
                manifestURI, requestingPrincipal, rootManifest);
          },
          [](nsresult error) {
            return MozPromise<std::tuple<Maybe<IdentityProviderWellKnown>,
                                         IdentityProviderAPIConfig>,
                              nsresult, true>::CreateAndReject(error, __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aProvider](std::tuple<Maybe<IdentityProviderWellKnown>,
                                 IdentityProviderAPIConfig>
                          manifests) {
            IdentityProviderAPIConfig currentManifest;
            Maybe<IdentityProviderWellKnown> fetchedWellKnown;
            std::tie(fetchedWellKnown, currentManifest) = manifests;
            // If we have more than one provider URL, we need to make sure that
            // the accounts endpoint matches
            nsCString configLocation = aProvider.mConfigURL;
            if (fetchedWellKnown.isSome()) {
              IdentityProviderWellKnown wellKnown(fetchedWellKnown.extract());
              if (wellKnown.mProvider_urls.Length() == 1) {
                if (!wellKnown.mProvider_urls.Contains(configLocation)) {
                  return GetManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
                                                             __func__);
                }
              } else if (!wellKnown.mProvider_urls.Contains(configLocation) ||
                         !wellKnown.mAccounts_endpoint.WasPassed() ||
                         !wellKnown.mAccounts_endpoint.Value().Equals(
                             currentManifest.mAccounts_endpoint)) {
                return GetManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
                                                           __func__);
              }
            }
            return GetManifestPromise::CreateAndResolve<
                mozilla::dom::IdentityProviderAPIConfig>(
                IdentityProviderAPIConfig(currentManifest), __func__);
          },
          [](nsresult error) {
            return GetManifestPromise::CreateAndReject(error, __func__);
          });
}

// static
RefPtr<GetAccountListPromise> FetchAccountList(
    nsIPrincipal* aPrincipal, const IdentityProviderRequestOptions& aProvider,
    const IdentityProviderAPIConfig& aManifest) {
  MOZ_ASSERT(XRE_IsParentProcess());
  // Build the URL
  nsCOMPtr<nsIURI> baseURI;
  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aProvider.mConfigURL);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetAccountListPromise::CreateAndReject(rv, __func__);
  }
  nsCOMPtr<nsIURI> idpURI;
  rv = NS_NewURI(getter_AddRefs(idpURI), aManifest.mAccounts_endpoint, nullptr,
                 baseURI);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetAccountListPromise::CreateAndReject(rv, __func__);
  }
  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
      idpURI, aPrincipal->OriginAttributesRef());

  return IdentityNetworkHelpers::FetchAccountsHelper(idpURI, idpPrincipal)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aManifest](const IdentityProviderAccountList& accountList) {
            return GetAccountListPromise::CreateAndResolve(
                std::make_tuple(aManifest, accountList), __func__);
          },
          [](nsresult error) {
            return GetAccountListPromise::CreateAndReject(error, __func__);
          });
}

// static
RefPtr<GetTokenPromise> FetchToken(
    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    const IdentityProviderRequestOptions& aProvider,
    const IdentityProviderAPIConfig& aManifest,
    const IdentityProviderAccount& aAccount, const bool isAutoSelected) {
  MOZ_ASSERT(XRE_IsParentProcess());
  // Build the URL
  nsCOMPtr<nsIURI> baseURI;
  nsCString baseURIString = aProvider.mConfigURL;
  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), baseURIString);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetTokenPromise::CreateAndReject(rv, __func__);
  }
  nsCOMPtr<nsIURI> idpURI;
  nsCString tokenSpec = aManifest.mId_assertion_endpoint;
  rv = NS_NewURI(getter_AddRefs(idpURI), tokenSpec.get(), baseURI);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetTokenPromise::CreateAndReject(rv, __func__);
  }
  nsCString tokenLocation;
  rv = idpURI->GetSpec(tokenLocation);
  if (NS_WARN_IF(NS_FAILED(rv))) {
    return GetTokenPromise::CreateAndReject(rv, __func__);
  }

  // Create a new request
  URLParams bodyValue;
  bodyValue.Set("account_id"_ns, NS_ConvertUTF16toUTF8(aAccount.mId));
  bodyValue.Set("client_id"_ns, aProvider.mClientId);
  if (aProvider.mNonce.WasPassed()) {
    bodyValue.Set("nonce"_ns, aProvider.mNonce.Value());
  }
  bodyValue.Set("disclosure_text_shown"_ns, "false"_ns);
  nsCString serializedIsAutoSelected = isAutoSelected ? "true"_ns : "false"_ns;
  bodyValue.Set("is_auto_selected"_ns, serializedIsAutoSelected);
  nsAutoCString bodyCString;
  bodyValue.Serialize(bodyCString, true);

  RefPtr<WebIdentityParent> relyingParty(aRelyingParty);
  return IdentityNetworkHelpers::FetchTokenHelper(idpURI, bodyCString,
                                                  aPrincipal)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aAccount, idpURI, relyingParty,
           isAutoSelected](const IdentityAssertionResponse& response) {
            // If we were provided a token, resolve with it.
            if (response.mToken.WasPassed()) {
              return GetTokenPromise::CreateAndResolve(
                  std::make_tuple(response.mToken.Value(),
                                  NS_ConvertUTF16toUTF8(aAccount.mId),
                                  isAutoSelected),
                  __func__);
            }
            // If we don't have a continuation window to open at this stage,
            // reject with the appropriate error.
            if (!response.mContinue_on.WasPassed()) {
              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
                                                      __func__);
            }
            // Construct the URI we are going to open
            nsCOMPtr<nsIURI> continueURI;
            nsCString continueSpec = response.mContinue_on.Value();
            nsresult rv =
                NS_NewURI(getter_AddRefs(continueURI), continueSpec.get());
            if (NS_FAILED(rv)) {
              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
                                                      __func__);
            }
            // It must be same-origin to the URL used to fetch to identity
            // assertion
            if (!nsScriptSecurityManager::SecurityCompareURIs(continueURI,
                                                              idpURI)) {
              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
                                                      __func__);
            }
            // Open the popup, and return the result of its interaction
            return AuthorizationPopupForToken(continueURI, relyingParty,
                                              aAccount, isAutoSelected);
          },
          [](nsresult error) {
            return GetTokenPromise::CreateAndReject(error, __func__);
          });
}

RefPtr<GetTokenPromise> AuthorizationPopupForToken(
    nsIURI* aContinueURI, WebIdentityParent* aRelyingParty,
    const IdentityProviderAccount& aAccount, const bool isAutoSelected) {
  MOZ_ASSERT(aContinueURI);
  IdentityCredentialRequestManager* requestManager =
      IdentityCredentialRequestManager::GetInstance();
  if (!requestManager) {
    return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR, __func__);
  }
  // Start the process of getting a token for the popup.
  // The request manager opens the popup and holds a ref to the promise
  // returned. This then gets settles depending on the popup page's behavior (or
  // rejects on close).
  return requestManager->GetTokenFromPopup(aRelyingParty, aContinueURI)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [aAccount, isAutoSelected](
              const std::tuple<nsCString, Maybe<nsCString>>& promiseResult) {
            // We will resolve either way with our token from here, but
            // We may have an account ID to override the user's selection in the
            // account chooser.
            nsCString token;
            Maybe<nsCString> overridingAccountId;
            std::tie(token, overridingAccountId) = promiseResult;
            if (overridingAccountId.isSome()) {
              return GetTokenPromise::CreateAndResolve(
                  std::make_tuple(token, overridingAccountId.value(),
                                  isAutoSelected),
                  __func__);
            }
            return GetTokenPromise::CreateAndResolve(
                std::make_tuple(token, NS_ConvertUTF16toUTF8(aAccount.mId),
                                isAutoSelected),
                __func__);
          },
          [](nsresult rv) {
            return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
                                                    __func__);
          });
}

// static
RefPtr<MozPromise<bool, nsresult, true>> DisconnectInMainProcess(
    nsIPrincipal* aDocumentPrincipal,
    const IdentityCredentialDisconnectOptions& aOptions) {
  MOZ_ASSERT(XRE_IsParentProcess());
  nsresult rv;
  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
      mozilla::components::IdentityCredentialStorageService::Service(&rv);
  if (NS_WARN_IF(!icStorageService)) {
    return MozPromise<bool, nsresult, true>::CreateAndReject(rv, __func__);
  }

  RefPtr<MozPromise<bool, nsresult, true>::Private> resultPromise =
      new MozPromise<bool, nsresult, true>::Private(__func__);

  RefPtr<nsIURI> configURI;
  rv = NS_NewURI(getter_AddRefs(configURI), aOptions.mConfigURL);
  if (NS_FAILED(rv)) {
    resultPromise->Reject(NS_ERROR_DOM_MALFORMED_URI, __func__);
    return resultPromise;
  }

  nsCOMPtr<nsIPrincipal> principal(aDocumentPrincipal);
  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
      configURI, principal->OriginAttributesRef());

  FetchManifest(principal, aOptions)
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [resultPromise, aOptions, icStorageService, configURI, idpPrincipal,
           principal](const IdentityProviderAPIConfig& aConfig) {
            if (!aConfig.mDisconnect_endpoint.WasPassed()) {
              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
              return MozPromise<DisconnectedAccount, nsresult,
                                true>::CreateAndReject(NS_OK, __func__);
            }
            RefPtr<nsIURI> disconnectURI;
            nsCString disconnectArgument = aConfig.mDisconnect_endpoint.Value();
            nsresult rv = NS_NewURI(getter_AddRefs(disconnectURI),
                                    disconnectArgument, nullptr, configURI);
            if (NS_FAILED(rv)) {
              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
              return MozPromise<DisconnectedAccount, nsresult,
                                true>::CreateAndReject(NS_OK, __func__);
            }

            bool connected = false;
            rv = icStorageService->Connected(principal, idpPrincipal,
                                             &connected);
            if (NS_WARN_IF(NS_FAILED(rv)) || !connected) {
              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
              return MozPromise<DisconnectedAccount, nsresult,
                                true>::CreateAndReject(NS_OK, __func__);
            }

            // Create a new request
            URLParams bodyValue;
            bodyValue.Set("client_id"_ns, aOptions.mClientId);
            bodyValue.Set("account_hint"_ns, aOptions.mAccountHint);
            nsAutoCString bodyCString;
            bodyValue.Serialize(bodyCString, true);
            return IdentityNetworkHelpers::FetchDisconnectHelper(
                disconnectURI, bodyCString, principal);
          },
          [resultPromise](nsresult aError) {
            resultPromise->Reject(aError, __func__);
            // We reject with NS_OK, so that we don't disconnect accounts in the
            // reject callback here.
            return MozPromise<DisconnectedAccount, nsresult,
                              true>::CreateAndReject(NS_OK, __func__);
          })
      ->Then(
          GetCurrentSerialEventTarget(), __func__,
          [icStorageService, principal, idpPrincipal,
           resultPromise](const DisconnectedAccount& token) {
            bool registered = false, notUsed = false;
            nsresult rv = icStorageService->GetState(principal, idpPrincipal,
                                                     token.mAccount_id,
                                                     ®istered, ¬Used);
            if (NS_WARN_IF(NS_FAILED(rv))) {
              resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
              return;
            }
            if (registered) {
              nsresult rv = icStorageService->Delete(principal, idpPrincipal,
                                                     token.mAccount_id);
              if (NS_WARN_IF(NS_FAILED(rv))) {
                resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
                return;
              }
              resultPromise->Resolve(true, __func__);
            } else {
              nsresult rv =
                  icStorageService->Disconnect(principal, idpPrincipal);
              if (NS_WARN_IF(NS_FAILED(rv))) {
                resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
                return;
              }
              resultPromise->Resolve(true, __func__);
            }
            return;
          },
          [icStorageService, principal, idpPrincipal,
           resultPromise](nsresult error) {
            // Bail out if we already rejected the result above.
            if (error == NS_OK) {
              return;
            }

            // If we issued the request and it failed, fall back
            // to clearing all.
            nsresult rv = icStorageService->Disconnect(principal, idpPrincipal);
            if (NS_WARN_IF(NS_FAILED(rv))) {
              resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
              return;
            }
            resultPromise->Resolve(true, __func__);
            return;
          });

  return resultPromise;
}

// static
RefPtr<GetIdentityProviderRequestOptionsWithManifestPromise>
PromptUserToSelectProvider(
    BrowsingContext* aBrowsingContext,
    const Sequence<IdentityProviderRequestOptions>& aProviders,
    const Sequence<GetManifestPromise::ResolveOrRejectValue>& aManifests) {
  MOZ_ASSERT(aBrowsingContext);
  RefPtr<GetIdentityProviderRequestOptionsWithManifestPromise::Private>
      resultPromise =
          new GetIdentityProviderRequestOptionsWithManifestPromise::Private(
              __func__);

  if (NS_WARN_IF(!aBrowsingContext)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  nsresult error;
  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
      mozilla::components::IdentityCredentialPromptService::Service(&error);
  if (NS_WARN_IF(!icPromptService)) {
    resultPromise->Reject(error, __func__);
    return resultPromise;
  }

  nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(icPromptService);
  AutoJSAPI jsapi;
  if (NS_WARN_IF(!jsapi.Init(wrapped->GetJSObjectGlobal()))) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  JS::Rooted<JS::Value> providersJS(jsapi.cx());
  bool success = ToJSValue(jsapi.cx(), aProviders, &providersJS);
  if (NS_WARN_IF(!success)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  // Convert each settled MozPromise into a Nullable<ResolveValue>
  Sequence<Nullable<IdentityProviderAPIConfig>> manifests;
  for (GetManifestPromise::ResolveOrRejectValue manifest : aManifests) {
    if (manifest.IsResolve()) {
      if (NS_WARN_IF(
              !manifests.AppendElement(manifest.ResolveValue(), fallible))) {
        resultPromise->Reject(NS_ERROR_FAILURE, __func__);
        return resultPromise;
      }
    } else {
      if (NS_WARN_IF(!manifests.AppendElement(
              Nullable<IdentityProviderAPIConfig>(), fallible))) {
        resultPromise->Reject(NS_ERROR_FAILURE, __func__);
        return resultPromise;
      }
    }
  }
  JS::Rooted<JS::Value> manifestsJS(jsapi.cx());
  success = ToJSValue(jsapi.cx(), manifests, &manifestsJS);
  if (NS_WARN_IF(!success)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  RefPtr<Promise> showPromptPromise;
  icPromptService->ShowProviderPrompt(aBrowsingContext, providersJS,
                                      manifestsJS,
                                      getter_AddRefs(showPromptPromise));

  showPromptPromise->AddCallbacksWithCycleCollectedArgs(
      [aProviders, aManifests, resultPromise](
          JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
        int32_t result = aValue.toInt32();
        if (result < 0 || (uint32_t)result > aProviders.Length() ||
            (uint32_t)result > aManifests.Length()) {
          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
          return;
        }
        const IdentityProviderRequestOptions& resolvedProvider =
            aProviders.ElementAt(result);
        if (!aManifests.ElementAt(result).IsResolve()) {
          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
          return;
        }
        const IdentityProviderAPIConfig& resolvedManifest =
            aManifests.ElementAt(result).ResolveValue();
        resultPromise->Resolve(
            std::make_tuple(resolvedProvider, resolvedManifest), __func__);
      },
      [resultPromise](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
        resultPromise->Reject(
            Promise::TryExtractNSResultFromRejectionValue(aValue), __func__);
      });
  // Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
  showPromptPromise->AppendNativeHandler(
      new MozPromiseRejectOnDestruction{resultPromise, __func__});

  return resultPromise;
}

// static
RefPtr<GetAccountPromise> PromptUserToSelectAccount(
    BrowsingContext* aBrowsingContext,
    const IdentityProviderAccountList& aAccounts,
    const IdentityProviderRequestOptions& aProvider,
    const IdentityProviderAPIConfig& aManifest) {
  MOZ_ASSERT(aBrowsingContext);
  RefPtr<GetAccountPromise::Private> resultPromise =
      new GetAccountPromise::Private(__func__);

  if (NS_WARN_IF(!aBrowsingContext)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  nsresult error;
  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
      mozilla::components::IdentityCredentialPromptService::Service(&error);
  if (NS_WARN_IF(!icPromptService)) {
    resultPromise->Reject(error, __func__);
    return resultPromise;
  }

  nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(icPromptService);
  AutoJSAPI jsapi;
  if (NS_WARN_IF(!jsapi.Init(wrapped->GetJSObjectGlobal()))) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  JS::Rooted<JS::Value> accountsJS(jsapi.cx());
  bool success = ToJSValue(jsapi.cx(), aAccounts, &accountsJS);
  if (NS_WARN_IF(!success)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  JS::Rooted<JS::Value> providerJS(jsapi.cx());
  success = ToJSValue(jsapi.cx(), aProvider, &providerJS);
  if (NS_WARN_IF(!success)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  JS::Rooted<JS::Value> manifestJS(jsapi.cx());
  success = ToJSValue(jsapi.cx(), aManifest, &manifestJS);
  if (NS_WARN_IF(!success)) {
    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
    return resultPromise;
  }

  RefPtr<Promise> showPromptPromise;
  icPromptService->ShowAccountListPrompt(aBrowsingContext, accountsJS,
                                         providerJS, manifestJS,
                                         getter_AddRefs(showPromptPromise));

  showPromptPromise->AddCallbacksWithCycleCollectedArgs(
      [aAccounts, resultPromise, aManifest](
          JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
        int32_t result = aValue.toInt32();
        if (!aAccounts.mAccounts.WasPassed() || result < 0 ||
            (uint32_t)result > aAccounts.mAccounts.Value().Length()) {
          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
          return;
        }
        const IdentityProviderAccount& resolved =
            aAccounts.mAccounts.Value().ElementAt(result);
        resultPromise->Resolve(std::make_tuple(aManifest, resolved, false),
                               __func__);
      },
      [resultPromise](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
        resultPromise->Reject(
            Promise::TryExtractNSResultFromRejectionValue(aValue), __func__);
      });
  // Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
  showPromptPromise->AppendNativeHandler(
      new MozPromiseRejectOnDestruction{resultPromise, __func__});

  return resultPromise;
}

// static
nsresult LinkAccount(nsIPrincipal* aPrincipal, const nsCString& aAccountId,
                     const IdentityProviderRequestOptions& aProvider) {
  MOZ_ASSERT(aPrincipal);

  nsresult error;
  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
      mozilla::components::IdentityCredentialStorageService::Service(&error);
  if (NS_WARN_IF(!icStorageService)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  // Check the storage bit
  nsCString configLocation = aProvider.mConfigURL;
  nsCOMPtr<nsIURI> idpURI;
  error = NS_NewURI(getter_AddRefs(idpURI), configLocation);
  if (NS_WARN_IF(NS_FAILED(error))) {
    return error;
  }
  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
      idpURI, aPrincipal->OriginAttributesRef());

  // Mark as logged in and return
  icStorageService->SetState(aPrincipal, idpPrincipal, aAccountId, true, true);

  nsCOMPtr<nsIPermissionManager> permissionManager =
      components::PermissionManager::Service();

  if (NS_WARN_IF(!permissionManager)) {
    return NS_ERROR_NOT_AVAILABLE;
  }

  nsCString idpOrigin;
  error = idpPrincipal->GetOrigin(idpOrigin);
  NS_ENSURE_SUCCESS(error, error);

  permissionManager->AddFromPrincipal(
      aPrincipal, "credential-allow-silent-access^"_ns + idpOrigin,
      nsIPermissionManager::ALLOW_ACTION, nsIPermissionManager::EXPIRE_SESSION,
      0);
  permissionManager->AddFromPrincipal(aPrincipal,
                                      "credential-allow-silent-access"_ns,
                                      nsIPermissionManager::ALLOW_ACTION,
                                      nsIPermissionManager::EXPIRE_SESSION, 0);
  return NS_OK;
}

// static
void CloseUserInterface(BrowsingContext* aBrowsingContext) {
  nsresult error;
  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
      mozilla::components::IdentityCredentialPromptService::Service(&error);
  if (NS_WARN_IF(!icPromptService)) {
    return;
  }
  icPromptService->Close(aBrowsingContext);
}

}  // namespace identity

}  // namespace mozilla::dom

Messung V0.5 in Prozent
C=91 H=96 G=93

¤ Dauer der Verarbeitung: 0.21 Sekunden  (vorverarbeitet am  2026-08-25) ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.