tor-browser

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

WebIdentityParent.cpp (52213B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "mozilla/dom/WebIdentityParent.h"
      8 
      9 #include "mozilla/Components.h"
     10 #include "mozilla/IdentityCredentialRequestManager.h"
     11 #include "mozilla/dom/IdentityNetworkHelpers.h"
     12 #include "mozilla/dom/NavigatorLogin.h"
     13 #include "mozilla/dom/WindowGlobalParent.h"
     14 #include "nsIEffectiveTLDService.h"
     15 #include "nsIIdentityCredentialPromptService.h"
     16 #include "nsIIdentityCredentialStorageService.h"
     17 #include "nsIXPConnect.h"
     18 #include "nsScriptSecurityManager.h"
     19 #include "nsURLHelper.h"
     20 
     21 namespace mozilla::dom {
     22 
     23 void WebIdentityParent::ActorDestroy(ActorDestroyReason aWhy) {
     24  MOZ_ASSERT(NS_IsMainThread());
     25 }
     26 
     27 mozilla::ipc::IPCResult WebIdentityParent::RecvRequestCancel() {
     28  MOZ_ASSERT(NS_IsMainThread());
     29  return IPC_OK();
     30 }
     31 
     32 mozilla::ipc::IPCResult WebIdentityParent::RecvGetIdentityCredential(
     33    IdentityCredentialRequestOptions&& aOptions,
     34    const CredentialMediationRequirement& aMediationRequirement,
     35    bool aHasUserActivation, const GetIdentityCredentialResolver& aResolver) {
     36  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
     37  if (!manager) {
     38    aResolver(NS_ERROR_FAILURE);
     39  }
     40  identity::GetCredentialInMainProcess(
     41      manager->DocumentPrincipal(), this, std::move(aOptions),
     42      aMediationRequirement, aHasUserActivation)
     43      ->Then(
     44          GetCurrentSerialEventTarget(), __func__,
     45          [aResolver](const IPCIdentityCredential& aResult) {
     46            return aResolver(aResult);
     47          },
     48          [aResolver](nsresult aErr) { aResolver(aErr); });
     49  return IPC_OK();
     50 }
     51 
     52 mozilla::ipc::IPCResult WebIdentityParent::RecvDisconnectIdentityCredential(
     53    const IdentityCredentialDisconnectOptions& aOptions,
     54    const DisconnectIdentityCredentialResolver& aResolver) {
     55  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
     56  if (!manager) {
     57    aResolver(NS_ERROR_FAILURE);
     58  }
     59  identity::DisconnectInMainProcess(manager->DocumentPrincipal(), aOptions)
     60      ->Then(
     61          GetCurrentSerialEventTarget(), __func__,
     62          [aResolver](const bool& aResult) { aResolver(NS_OK); },
     63          [aResolver](nsresult aErr) { aResolver(aErr); });
     64  return IPC_OK();
     65 }
     66 
     67 mozilla::ipc::IPCResult WebIdentityParent::RecvPreventSilentAccess(
     68    const PreventSilentAccessResolver& aResolver) {
     69  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
     70  if (!manager) {
     71    aResolver(NS_ERROR_FAILURE);
     72  }
     73  nsIPrincipal* principal = manager->DocumentPrincipal();
     74  if (principal) {
     75    nsCOMPtr<nsIPermissionManager> permissionManager =
     76        components::PermissionManager::Service();
     77    if (permissionManager) {
     78      permissionManager->RemoveFromPrincipal(
     79          principal, "credential-allow-silent-access"_ns);
     80      aResolver(NS_OK);
     81      return IPC_OK();
     82    }
     83  }
     84 
     85  aResolver(NS_ERROR_NOT_AVAILABLE);
     86  return IPC_OK();
     87 }
     88 
     89 mozilla::ipc::IPCResult WebIdentityParent::RecvSetLoginStatus(
     90    LoginStatus aStatus, const SetLoginStatusResolver& aResolver) {
     91  WindowGlobalParent* manager = static_cast<WindowGlobalParent*>(Manager());
     92  if (!manager) {
     93    aResolver(NS_ERROR_FAILURE);
     94    return IPC_OK();
     95  }
     96  nsIPrincipal* principal = manager->DocumentPrincipal();
     97  if (!principal) {
     98    aResolver(NS_ERROR_DOM_NOT_ALLOWED_ERR);
     99    return IPC_OK();
    100  }
    101  nsresult rv = NavigatorLogin::SetLoginStatus(principal, aStatus);
    102  aResolver(rv);
    103  return IPC_OK();
    104 }
    105 
    106 mozilla::ipc::IPCResult WebIdentityParent::RecvResolveContinuationWindow(
    107    nsCString&& aToken, IdentityResolveOptions&& aOptions,
    108    const ResolveContinuationWindowResolver& aResolver) {
    109  // Pass the resolution on to the ICRM to handle it.
    110  // Faithfully convey its error in resolution.
    111  IdentityCredentialRequestManager* requestManager =
    112      IdentityCredentialRequestManager::GetInstance();
    113  if (!requestManager) {
    114    aResolver(NS_ERROR_NOT_AVAILABLE);
    115    return IPC_OK();
    116  }
    117  nsresult rv = requestManager->MaybeResolvePopup(this, aToken, aOptions);
    118  aResolver(rv);
    119  return IPC_OK();
    120 }
    121 
    122 mozilla::ipc::IPCResult WebIdentityParent::RecvIsActiveContinuationWindow(
    123    const IsActiveContinuationWindowResolver& aResolver) {
    124  IdentityCredentialRequestManager* requestManager =
    125      IdentityCredentialRequestManager::GetInstance();
    126  if (!requestManager) {
    127    aResolver(false);
    128    return IPC_OK();
    129  }
    130  aResolver(requestManager->IsActivePopup(this));
    131  return IPC_OK();
    132 }
    133 
    134 namespace identity {
    135 
    136 nsresult CanSilentlyCollect(nsIPrincipal* aPrincipal,
    137                            nsIPrincipal* aIDPPrincipal, bool* aResult) {
    138  NS_ENSURE_ARG_POINTER(aPrincipal);
    139  NS_ENSURE_ARG_POINTER(aIDPPrincipal);
    140  nsCString origin;
    141  nsresult rv = aIDPPrincipal->GetOrigin(origin);
    142  NS_ENSURE_SUCCESS(rv, rv);
    143 
    144  uint32_t permit = nsIPermissionManager::UNKNOWN_ACTION;
    145  nsCOMPtr<nsIPermissionManager> permissionManager =
    146      components::PermissionManager::Service();
    147  if (!permissionManager) {
    148    return NS_ERROR_SERVICE_NOT_AVAILABLE;
    149  }
    150 
    151  rv = permissionManager->TestPermissionFromPrincipal(
    152      aPrincipal, "credential-allow-silent-access^"_ns + origin, &permit);
    153  NS_ENSURE_SUCCESS(rv, rv);
    154  *aResult = (permit == nsIPermissionManager::ALLOW_ACTION);
    155  if (!*aResult) {
    156    return NS_OK;
    157  }
    158  rv = permissionManager->TestPermissionFromPrincipal(
    159      aPrincipal, "credential-allow-silent-access"_ns, &permit);
    160  NS_ENSURE_SUCCESS(rv, rv);
    161  *aResult = permit == nsIPermissionManager::ALLOW_ACTION;
    162  return NS_OK;
    163 }
    164 
    165 // static
    166 RefPtr<GetIPCIdentityCredentialPromise> GetCredentialInMainProcess(
    167    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    168    IdentityCredentialRequestOptions&& aOptions,
    169    const CredentialMediationRequirement& aMediationRequirement,
    170    bool aHasUserActivation) {
    171  MOZ_ASSERT(aPrincipal);
    172  MOZ_ASSERT(aRelyingParty);
    173 
    174  if (aOptions.mMode == IdentityCredentialRequestOptionsMode::Active) {
    175    // If the site is operating in "Active Mode" we need user activation  to
    176    // proceed.
    177    if (!aHasUserActivation) {
    178      return GetIPCIdentityCredentialPromise::CreateAndReject(
    179          NS_ERROR_DOM_NETWORK_ERR, __func__);
    180    }
    181  } else {
    182    // Otherwise we are in "Passive Mode" and since this doesn't require user
    183    // activation we constrain the credentials that are allowed to be be shown
    184    // to the user so they don't get annoyed.
    185    // Specifically, they need to have this credential registered for use on
    186    // this website.
    187    nsresult rv;
    188    nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
    189        mozilla::components::IdentityCredentialStorageService::Service(&rv);
    190    if (NS_WARN_IF(!icStorageService)) {
    191      return GetIPCIdentityCredentialPromise::CreateAndReject(rv, __func__);
    192    }
    193    aOptions.mProviders.RemoveElementsBy(
    194        [icStorageService,
    195         aPrincipal](const IdentityProviderRequestOptions& provider) {
    196          nsCString configLocation = provider.mConfigURL;
    197          nsCOMPtr<nsIURI> configURI;
    198          nsresult rv = NS_NewURI(getter_AddRefs(configURI), configLocation);
    199          if (NS_FAILED(rv)) {
    200            return true;
    201          }
    202          bool thirdParty = true;
    203          rv = aPrincipal->IsThirdPartyURI(configURI, &thirdParty);
    204          if (!thirdParty) {
    205            return false;
    206          }
    207          nsCOMPtr<nsIPrincipal> idpPrincipal =
    208              BasePrincipal::CreateContentPrincipal(
    209                  configURI, aPrincipal->OriginAttributesRef());
    210          bool connected = false;
    211          rv =
    212              icStorageService->Connected(aPrincipal, idpPrincipal, &connected);
    213          if (NS_FAILED(rv)) {
    214            return true;
    215          }
    216          return !connected;
    217        });
    218  }
    219 
    220  if (aOptions.mProviders.IsEmpty()) {
    221    return GetIPCIdentityCredentialPromise::CreateAndReject(
    222        NS_ERROR_NOT_AVAILABLE, __func__);
    223  }
    224 
    225  RefPtr<GetIPCIdentityCredentialPromise::Private> result =
    226      new GetIPCIdentityCredentialPromise::Private(__func__);
    227  DiscoverFromExternalSourceInMainProcess(aPrincipal, aRelyingParty, aOptions,
    228                                          aMediationRequirement)
    229      ->Then(
    230          GetCurrentSerialEventTarget(), __func__,
    231          [result](const IPCIdentityCredential& credential) {
    232            result->Resolve(credential, __func__);
    233          },
    234          [result](nsresult rv) { result->Reject(rv, __func__); });
    235  return result.forget();
    236 }
    237 
    238 // static
    239 RefPtr<GetIPCIdentityCredentialPromise> DiscoverFromExternalSourceInMainProcess(
    240    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    241    const IdentityCredentialRequestOptions& aOptions,
    242    const CredentialMediationRequirement& aMediationRequirement) {
    243  MOZ_ASSERT(XRE_IsParentProcess());
    244  MOZ_ASSERT(aPrincipal);
    245  MOZ_ASSERT(aRelyingParty);
    246 
    247  // Make sure we have providers.
    248  if (aOptions.mProviders.Length() < 1) {
    249    return GetIPCIdentityCredentialPromise::CreateAndReject(
    250        NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
    251  }
    252 
    253  nsCOMPtr<nsIPrincipal> principal(aPrincipal);
    254  RefPtr<CanonicalBrowsingContext> browsingContext =
    255      aRelyingParty->MaybeBrowsingContext();
    256  if (!browsingContext) {
    257    return GetIPCIdentityCredentialPromise::CreateAndReject(
    258        NS_ERROR_DOM_NOT_ALLOWED_ERR, __func__);
    259  }
    260  RefPtr<WebIdentityParent> relyingParty = aRelyingParty;
    261 
    262  RefPtr<GetIPCIdentityCredentialPromise::Private> result =
    263      new GetIPCIdentityCredentialPromise::Private(__func__);
    264 
    265  RefPtr<nsITimer> timeout;
    266  if (StaticPrefs::
    267          dom_security_credentialmanagement_identity_reject_delay_enabled()) {
    268    nsresult rv = NS_NewTimerWithCallback(
    269        getter_AddRefs(timeout),
    270        [=](auto) {
    271          result->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
    272          CloseUserInterface(browsingContext);
    273        },
    274        StaticPrefs::
    275            dom_security_credentialmanagement_identity_reject_delay_duration_ms(),
    276        nsITimer::TYPE_ONE_SHOT, "IdentityCredentialTimeoutCallback"_ns);
    277    if (NS_WARN_IF(NS_FAILED(rv))) {
    278      result->Reject(NS_ERROR_FAILURE, __func__);
    279      return result.forget();
    280    }
    281  }
    282 
    283  // Construct an array of requests to fetch manifests for every provider.
    284  // We need this to show their branding information
    285  nsTArray<RefPtr<GetManifestPromise>> manifestPromises;
    286  for (const IdentityProviderRequestOptions& provider : aOptions.mProviders) {
    287    RefPtr<GetManifestPromise> manifest = FetchManifest(principal, provider);
    288    manifestPromises.AppendElement(manifest);
    289  }
    290 
    291  // We use AllSettled here so that failures will be included- we use default
    292  // values there.
    293  GetManifestPromise::AllSettled(GetCurrentSerialEventTarget(),
    294                                 manifestPromises)
    295      ->Then(
    296          GetCurrentSerialEventTarget(), __func__,
    297          [browsingContext, aOptions](
    298              const GetManifestPromise::AllSettledPromiseType::ResolveValueType&
    299                  aResults) {
    300            // Convert the
    301            // GetManifestPromise::AllSettledPromiseType::ResolveValueType to a
    302            // Sequence<MozPromise>
    303            CopyableTArray<MozPromise<IdentityProviderAPIConfig, nsresult,
    304                                      true>::ResolveOrRejectValue>
    305                results = aResults;
    306            const Sequence<MozPromise<IdentityProviderAPIConfig, nsresult,
    307                                      true>::ResolveOrRejectValue>
    308                resultsSequence(std::move(results));
    309 
    310            // If we can skip the provider check, because there is only one
    311            // option and it is already linked, do so!
    312            Maybe<IdentityProviderRequestOptionsWithManifest>
    313                autoSelectedIdentityProvider =
    314                    SkipAccountChooser(aOptions.mProviders, resultsSequence);
    315            if (autoSelectedIdentityProvider.isSome()) {
    316              return GetIdentityProviderRequestOptionsWithManifestPromise::
    317                  CreateAndResolve(autoSelectedIdentityProvider.extract(),
    318                                   __func__);
    319            }
    320 
    321            // The user picks from the providers
    322            return PromptUserToSelectProvider(
    323                browsingContext, aOptions.mProviders, resultsSequence);
    324          },
    325          [](bool error) {
    326            return GetIdentityProviderRequestOptionsWithManifestPromise::
    327                CreateAndReject(NS_ERROR_FAILURE, __func__);
    328          })
    329      ->Then(
    330          GetCurrentSerialEventTarget(), __func__,
    331          [aMediationRequirement, principal,
    332           relyingParty](const IdentityProviderRequestOptionsWithManifest&
    333                             providerAndManifest) {
    334            IdentityProviderAPIConfig manifest;
    335            IdentityProviderRequestOptions provider;
    336            std::tie(provider, manifest) = providerAndManifest;
    337            return CreateCredentialDuringDiscovery(principal, relyingParty,
    338                                                   provider, manifest,
    339                                                   aMediationRequirement);
    340          },
    341          [](nsresult error) {
    342            return GetIPCIdentityCredentialPromise::CreateAndReject(error,
    343                                                                    __func__);
    344          })
    345      ->Then(
    346          GetCurrentSerialEventTarget(), __func__,
    347          [result, timeout = std::move(timeout)](
    348              const GetIPCIdentityCredentialPromise::ResolveOrRejectValue&&
    349                  value) {
    350            // Resolve the result
    351            result->ResolveOrReject(value, __func__);
    352 
    353            // Cancel the timer (if it is still pending) and
    354            // release the hold on the variables leaked into the timer.
    355            if (timeout &&
    356                StaticPrefs::
    357                    dom_security_credentialmanagement_identity_reject_delay_enabled()) {
    358              timeout->Cancel();
    359            }
    360          });
    361 
    362  return result;
    363 }
    364 
    365 // static
    366 Maybe<IdentityProviderRequestOptionsWithManifest> SkipAccountChooser(
    367    const Sequence<IdentityProviderRequestOptions>& aProviders,
    368    const Sequence<GetManifestPromise::ResolveOrRejectValue>& aManifests) {
    369  if (aProviders.Length() != 1) {
    370    return Nothing();
    371  }
    372  if (aManifests.Length() != 1) {
    373    return Nothing();
    374  }
    375  if (!aManifests.ElementAt(0).IsResolve()) {
    376    return Nothing();
    377  }
    378  const IdentityProviderRequestOptions& resolvedProvider =
    379      aProviders.ElementAt(0);
    380  const IdentityProviderAPIConfig& resolvedManifest =
    381      aManifests.ElementAt(0).ResolveValue();
    382  return Some(std::make_tuple(resolvedProvider, resolvedManifest));
    383 }
    384 
    385 // static
    386 Maybe<IdentityProviderAccount> FindAccountToReauthenticate(
    387    const IdentityProviderRequestOptions& aProvider, nsIPrincipal* aRPPrincipal,
    388    const IdentityProviderAccountList& aAccountList) {
    389  if (!aAccountList.mAccounts.WasPassed()) {
    390    return Nothing();
    391  }
    392 
    393  nsresult rv;
    394  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
    395      mozilla::components::IdentityCredentialStorageService::Service(&rv);
    396  if (NS_WARN_IF(!icStorageService)) {
    397    return Nothing();
    398  }
    399 
    400  Maybe<IdentityProviderAccount> result = Nothing();
    401  for (const IdentityProviderAccount& account :
    402       aAccountList.mAccounts.Value()) {
    403    // Don't reauthenticate accounts that have an approved clients list but no
    404    // matching clientID from navigator.credentials.get's argument
    405    if (account.mApproved_clients.WasPassed()) {
    406      if (!account.mApproved_clients.Value().Contains(
    407              NS_ConvertUTF8toUTF16(aProvider.mClientId))) {
    408        continue;
    409      }
    410    }
    411 
    412    RefPtr<nsIURI> configURI;
    413    nsresult rv = NS_NewURI(getter_AddRefs(configURI), aProvider.mConfigURL);
    414    if (NS_FAILED(rv)) {
    415      continue;
    416    }
    417    nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
    418        configURI, aRPPrincipal->OriginAttributesRef());
    419 
    420    // Don't reauthenticate unconnected accounts
    421    bool connected = false;
    422    rv = icStorageService->Connected(aRPPrincipal, idpPrincipal, &connected);
    423    if (NS_WARN_IF(NS_FAILED(rv)) || !connected) {
    424      continue;
    425    }
    426 
    427    // Don't reauthenticate if silent access is disabled
    428    bool silentAllowed = false;
    429    rv = CanSilentlyCollect(aRPPrincipal, idpPrincipal, &silentAllowed);
    430    if (!NS_WARN_IF(NS_FAILED(rv)) && !silentAllowed) {
    431      continue;
    432    }
    433 
    434    // We only auto-reauthenticate if we have one candidate.
    435    if (result.isSome()) {
    436      return Nothing();
    437    }
    438 
    439    // Remember our first candidate so we can return it after
    440    // this loop, or return nothing if we find another!
    441    result = Some(account);
    442  }
    443 
    444  return result;
    445 }
    446 
    447 // static
    448 RefPtr<GetIPCIdentityCredentialPromise> CreateCredentialDuringDiscovery(
    449    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    450    const IdentityProviderRequestOptions& aProvider,
    451    const IdentityProviderAPIConfig& aManifest,
    452    const CredentialMediationRequirement& aMediationRequirement) {
    453  MOZ_ASSERT(XRE_IsParentProcess());
    454  MOZ_ASSERT(aPrincipal);
    455  MOZ_ASSERT(aRelyingParty);
    456 
    457  nsCOMPtr<nsIPrincipal> argumentPrincipal = aPrincipal;
    458  RefPtr<WebIdentityParent> relyingParty(aRelyingParty);
    459  RefPtr<CanonicalBrowsingContext> browsingContext(
    460      aRelyingParty->MaybeBrowsingContext());
    461  if (!browsingContext) {
    462    return GetIPCIdentityCredentialPromise::CreateAndReject(NS_ERROR_FAILURE,
    463                                                            __func__);
    464  }
    465 
    466  return FetchAccountList(argumentPrincipal, aProvider, aManifest)
    467      ->Then(
    468          GetCurrentSerialEventTarget(), __func__,
    469          [argumentPrincipal, browsingContext, aManifest, aMediationRequirement,
    470           aProvider](
    471              const std::tuple<IdentityProviderAPIConfig,
    472                               IdentityProviderAccountList>& promiseResult) {
    473            IdentityProviderAPIConfig currentManifest;
    474            IdentityProviderAccountList accountList;
    475            std::tie(currentManifest, accountList) = promiseResult;
    476            if (!accountList.mAccounts.WasPassed() ||
    477                accountList.mAccounts.Value().Length() == 0) {
    478              return GetAccountPromise::CreateAndReject(NS_ERROR_FAILURE,
    479                                                        __func__);
    480            }
    481 
    482            // Remove accounts without a matching login hint if one was provided
    483            // in the JS call
    484            if (aProvider.mLoginHint.WasPassed()) {
    485              const nsCString& loginHint = aProvider.mLoginHint.Value();
    486              accountList.mAccounts.Value().RemoveElementsBy(
    487                  [loginHint](const IdentityProviderAccount& account) {
    488                    if (!account.mLogin_hints.WasPassed() ||
    489                        account.mLogin_hints.Value().Length() == 0) {
    490                      return true;
    491                    }
    492                    if (account.mLogin_hints.Value().Contains(loginHint)) {
    493                      return false;
    494                    }
    495                    return true;
    496                  });
    497            }
    498 
    499            // Remove accounts without a matching domain hint if one was
    500            // provided in the JS call
    501            if (aProvider.mDomainHint.WasPassed()) {
    502              const nsCString& domainHint = aProvider.mDomainHint.Value();
    503              accountList.mAccounts.Value().RemoveElementsBy(
    504                  [domainHint](const IdentityProviderAccount& account) {
    505                    if (!account.mDomain_hints.WasPassed() ||
    506                        account.mDomain_hints.Value().Length() == 0) {
    507                      return true;
    508                    }
    509                    // The domain hint "any" matches any hint.
    510                    if (domainHint.Equals("any")) {
    511                      return false;
    512                    }
    513                    if (account.mDomain_hints.Value().Contains(domainHint)) {
    514                      return false;
    515                    }
    516                    return true;
    517                  });
    518            }
    519 
    520            // Remove accounts without a matching account hint if a label was
    521            // provided in the IDP config
    522            if (currentManifest.mAccount_label.WasPassed()) {
    523              const nsCString& accountHint =
    524                  currentManifest.mAccount_label.Value();
    525              accountList.mAccounts.Value().RemoveElementsBy(
    526                  [accountHint](const IdentityProviderAccount& account) {
    527                    if (!account.mLabel_hints.WasPassed() ||
    528                        account.mLabel_hints.Value().Length() == 0) {
    529                      return true;
    530                    }
    531                    if (account.mLabel_hints.Value().Contains(accountHint)) {
    532                      return false;
    533                    }
    534                    return true;
    535                  });
    536            }
    537 
    538            // If we can skip showing the user any UI by just doing a silent
    539            // renewal, do so.
    540            if (aMediationRequirement !=
    541                CredentialMediationRequirement::Required) {
    542              Maybe<IdentityProviderAccount> reauthenticatingAccount =
    543                  FindAccountToReauthenticate(aProvider, argumentPrincipal,
    544                                              accountList);
    545              if (reauthenticatingAccount.isSome()) {
    546                return GetAccountPromise::CreateAndResolve(
    547                    std::make_tuple(aManifest,
    548                                    reauthenticatingAccount.extract(), true),
    549                    __func__);
    550              }
    551            }
    552 
    553            if (accountList.mAccounts.Value().Length() < 1) {
    554              return GetAccountPromise::CreateAndReject(
    555                  NS_ERROR_DOM_NETWORK_ERR, __func__);
    556            }
    557 
    558            return PromptUserToSelectAccount(browsingContext, accountList,
    559                                             aProvider, currentManifest);
    560          },
    561          [](nsresult error) {
    562            return GetAccountPromise::CreateAndReject(error, __func__);
    563          })
    564      ->Then(
    565          GetCurrentSerialEventTarget(), __func__,
    566          [argumentPrincipal, aProvider,
    567           relyingParty](const std::tuple<IdentityProviderAPIConfig,
    568                                          IdentityProviderAccount, const bool>&
    569                             promiseResult) {
    570            IdentityProviderAPIConfig currentManifest;
    571            IdentityProviderAccount account;
    572            bool isAutoSelected;
    573            std::tie(currentManifest, account, isAutoSelected) = promiseResult;
    574            return FetchToken(argumentPrincipal, relyingParty, aProvider,
    575                              currentManifest, account, isAutoSelected);
    576          },
    577          [](nsresult error) {
    578            return GetTokenPromise::CreateAndReject(error, __func__);
    579          })
    580      ->Then(
    581          GetCurrentSerialEventTarget(), __func__,
    582          [argumentPrincipal,
    583           aProvider](const std::tuple<nsCString, nsCString, const bool>&
    584                          promiseResult) {
    585            nsCString token;
    586            nsCString accountId;
    587            bool isAutoSelected;
    588            std::tie(token, accountId, isAutoSelected) = promiseResult;
    589            IPCIdentityCredential credential;
    590            credential.token() = Some(token);
    591            credential.id() = NS_ConvertUTF8toUTF16(accountId);
    592            credential.isAutoSelected() = isAutoSelected;
    593            credential.configURL() = aProvider.mConfigURL;
    594            // We always make sure accounts are linked after we successfully
    595            // fetch a token
    596            nsresult rv = LinkAccount(argumentPrincipal, accountId, aProvider);
    597            if (NS_FAILED(rv)) {
    598              return GetIPCIdentityCredentialPromise::CreateAndReject(rv,
    599                                                                      __func__);
    600            }
    601            return GetIPCIdentityCredentialPromise::CreateAndResolve(credential,
    602                                                                     __func__);
    603          },
    604          [browsingContext](nsresult error) {
    605            CloseUserInterface(browsingContext);
    606            return GetIPCIdentityCredentialPromise::CreateAndReject(error,
    607                                                                    __func__);
    608          });
    609 }
    610 
    611 // static
    612 RefPtr<GetRootManifestPromise> FetchRootManifest(
    613    nsIPrincipal* aPrincipal, const IdentityProviderConfig& aProvider) {
    614  MOZ_ASSERT(XRE_IsParentProcess());
    615  if (StaticPrefs::
    616          dom_security_credentialmanagement_identity_test_ignore_well_known()) {
    617    return GetRootManifestPromise::CreateAndResolve(Nothing(), __func__);
    618  }
    619 
    620  // Build the URL
    621  nsCString configLocation = aProvider.mConfigURL;
    622  nsCOMPtr<nsIURI> configURI;
    623  nsresult rv = NS_NewURI(getter_AddRefs(configURI), configLocation);
    624  if (NS_WARN_IF(NS_FAILED(rv))) {
    625    return GetRootManifestPromise::CreateAndReject(rv, __func__);
    626  }
    627  RefPtr<nsIEffectiveTLDService> etld =
    628      mozilla::components::EffectiveTLD::Service();
    629  if (!etld) {
    630    return GetRootManifestPromise::CreateAndReject(
    631        NS_ERROR_SERVICE_NOT_AVAILABLE, __func__);
    632  }
    633  nsCString manifestURIString;
    634  rv = etld->GetSite(configURI, manifestURIString);
    635  if (NS_FAILED(rv)) {
    636    return GetRootManifestPromise::CreateAndReject(NS_ERROR_INVALID_ARG,
    637                                                   __func__);
    638  }
    639  nsAutoCString wellKnownPathForTesting;
    640  rv = Preferences::GetCString(
    641      "dom.security.credentialmanagement.identity.test_well_known_path",
    642      wellKnownPathForTesting);
    643  if (NS_SUCCEEDED(rv) && !wellKnownPathForTesting.IsVoid() &&
    644      !wellKnownPathForTesting.IsEmpty()) {
    645    manifestURIString.Append(wellKnownPathForTesting);
    646  } else {
    647    manifestURIString.AppendLiteral("/.well-known/web-identity");
    648  }
    649  nsCOMPtr<nsIURI> manifestURI;
    650  rv = NS_NewURI(getter_AddRefs(manifestURI), manifestURIString, nullptr);
    651  if (NS_FAILED(rv)) {
    652    return GetRootManifestPromise::CreateAndReject(NS_ERROR_INVALID_ARG,
    653                                                   __func__);
    654  }
    655 
    656  // We actually don't need to do any of this well-known stuff if the
    657  // requesting principal is same-site to the manifest URI. There is no
    658  // privacy risk in that case, because the requests could be sent with
    659  // their unpartitioned cookies anyway.
    660  if (!aPrincipal->GetIsNullPrincipal()) {
    661    bool thirdParty = true;
    662    rv = aPrincipal->IsThirdPartyURI(manifestURI, &thirdParty);
    663    if (NS_SUCCEEDED(rv) && !thirdParty) {
    664      return GetRootManifestPromise::CreateAndResolve(Nothing(), __func__);
    665    }
    666  }
    667 
    668  return IdentityNetworkHelpers::FetchWellKnownHelper(manifestURI, aPrincipal)
    669      ->Then(
    670          GetCurrentSerialEventTarget(), __func__,
    671          [aProvider](const IdentityProviderWellKnown& manifest) {
    672            // Resolve whether or not the argument URL is found in
    673            // the well-known
    674            if (manifest.mProvider_urls.Contains(aProvider.mConfigURL)) {
    675              return GetRootManifestPromise::CreateAndResolve(Some(manifest),
    676                                                              __func__);
    677            }
    678            return GetRootManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
    679                                                           __func__);
    680          },
    681          [](nsresult error) {
    682            return GetRootManifestPromise::CreateAndReject(error, __func__);
    683          });
    684 }
    685 
    686 // static
    687 RefPtr<GetManifestPromise> FetchManifest(
    688    nsIPrincipal* aPrincipal, const IdentityProviderConfig& aProvider) {
    689  MOZ_ASSERT(XRE_IsParentProcess());
    690 
    691  nsCOMPtr<nsIPrincipal> requestingPrincipal(aPrincipal);
    692  return FetchRootManifest(aPrincipal, aProvider)
    693      ->Then(
    694          GetCurrentSerialEventTarget(), __func__,
    695          [aProvider,
    696           requestingPrincipal](Maybe<IdentityProviderWellKnown> rootManifest) {
    697            // Build the URL
    698            nsCString configLocation = aProvider.mConfigURL;
    699            nsCOMPtr<nsIURI> manifestURI;
    700            nsresult rv =
    701                NS_NewURI(getter_AddRefs(manifestURI), configLocation, nullptr);
    702            if (NS_FAILED(rv)) {
    703              return MozPromise<std::tuple<Maybe<IdentityProviderWellKnown>,
    704                                           IdentityProviderAPIConfig>,
    705                                nsresult,
    706                                true>::CreateAndReject(NS_ERROR_INVALID_ARG,
    707                                                       __func__);
    708            }
    709            return IdentityNetworkHelpers::FetchConfigHelper(
    710                manifestURI, requestingPrincipal, rootManifest);
    711          },
    712          [](nsresult error) {
    713            return MozPromise<std::tuple<Maybe<IdentityProviderWellKnown>,
    714                                         IdentityProviderAPIConfig>,
    715                              nsresult, true>::CreateAndReject(error, __func__);
    716          })
    717      ->Then(
    718          GetCurrentSerialEventTarget(), __func__,
    719          [aProvider](std::tuple<Maybe<IdentityProviderWellKnown>,
    720                                 IdentityProviderAPIConfig>
    721                          manifests) {
    722            IdentityProviderAPIConfig currentManifest;
    723            Maybe<IdentityProviderWellKnown> fetchedWellKnown;
    724            std::tie(fetchedWellKnown, currentManifest) = manifests;
    725            // If we have more than one provider URL, we need to make sure that
    726            // the accounts endpoint matches
    727            nsCString configLocation = aProvider.mConfigURL;
    728            if (fetchedWellKnown.isSome()) {
    729              IdentityProviderWellKnown wellKnown(fetchedWellKnown.extract());
    730              if (wellKnown.mProvider_urls.Length() == 1) {
    731                if (!wellKnown.mProvider_urls.Contains(configLocation)) {
    732                  return GetManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
    733                                                             __func__);
    734                }
    735              } else if (!wellKnown.mProvider_urls.Contains(configLocation) ||
    736                         !wellKnown.mAccounts_endpoint.WasPassed() ||
    737                         !wellKnown.mAccounts_endpoint.Value().Equals(
    738                             currentManifest.mAccounts_endpoint)) {
    739                return GetManifestPromise::CreateAndReject(NS_ERROR_FAILURE,
    740                                                           __func__);
    741              }
    742            }
    743            return GetManifestPromise::CreateAndResolve<
    744                mozilla::dom::IdentityProviderAPIConfig>(
    745                IdentityProviderAPIConfig(currentManifest), __func__);
    746          },
    747          [](nsresult error) {
    748            return GetManifestPromise::CreateAndReject(error, __func__);
    749          });
    750 }
    751 
    752 // static
    753 RefPtr<GetAccountListPromise> FetchAccountList(
    754    nsIPrincipal* aPrincipal, const IdentityProviderRequestOptions& aProvider,
    755    const IdentityProviderAPIConfig& aManifest) {
    756  MOZ_ASSERT(XRE_IsParentProcess());
    757  // Build the URL
    758  nsCOMPtr<nsIURI> baseURI;
    759  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), aProvider.mConfigURL);
    760  if (NS_WARN_IF(NS_FAILED(rv))) {
    761    return GetAccountListPromise::CreateAndReject(rv, __func__);
    762  }
    763  nsCOMPtr<nsIURI> idpURI;
    764  rv = NS_NewURI(getter_AddRefs(idpURI), aManifest.mAccounts_endpoint, nullptr,
    765                 baseURI);
    766  if (NS_WARN_IF(NS_FAILED(rv))) {
    767    return GetAccountListPromise::CreateAndReject(rv, __func__);
    768  }
    769  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
    770      idpURI, aPrincipal->OriginAttributesRef());
    771 
    772  return IdentityNetworkHelpers::FetchAccountsHelper(idpURI, idpPrincipal)
    773      ->Then(
    774          GetCurrentSerialEventTarget(), __func__,
    775          [aManifest](const IdentityProviderAccountList& accountList) {
    776            return GetAccountListPromise::CreateAndResolve(
    777                std::make_tuple(aManifest, accountList), __func__);
    778          },
    779          [](nsresult error) {
    780            return GetAccountListPromise::CreateAndReject(error, __func__);
    781          });
    782 }
    783 
    784 // static
    785 RefPtr<GetTokenPromise> FetchToken(
    786    nsIPrincipal* aPrincipal, WebIdentityParent* aRelyingParty,
    787    const IdentityProviderRequestOptions& aProvider,
    788    const IdentityProviderAPIConfig& aManifest,
    789    const IdentityProviderAccount& aAccount, const bool isAutoSelected) {
    790  MOZ_ASSERT(XRE_IsParentProcess());
    791  // Build the URL
    792  nsCOMPtr<nsIURI> baseURI;
    793  nsCString baseURIString = aProvider.mConfigURL;
    794  nsresult rv = NS_NewURI(getter_AddRefs(baseURI), baseURIString);
    795  if (NS_WARN_IF(NS_FAILED(rv))) {
    796    return GetTokenPromise::CreateAndReject(rv, __func__);
    797  }
    798  nsCOMPtr<nsIURI> idpURI;
    799  nsCString tokenSpec = aManifest.mId_assertion_endpoint;
    800  rv = NS_NewURI(getter_AddRefs(idpURI), tokenSpec.get(), baseURI);
    801  if (NS_WARN_IF(NS_FAILED(rv))) {
    802    return GetTokenPromise::CreateAndReject(rv, __func__);
    803  }
    804  nsCString tokenLocation;
    805  rv = idpURI->GetSpec(tokenLocation);
    806  if (NS_WARN_IF(NS_FAILED(rv))) {
    807    return GetTokenPromise::CreateAndReject(rv, __func__);
    808  }
    809 
    810  // Create a new request
    811  URLParams bodyValue;
    812  bodyValue.Set("account_id"_ns, NS_ConvertUTF16toUTF8(aAccount.mId));
    813  bodyValue.Set("client_id"_ns, aProvider.mClientId);
    814  if (aProvider.mNonce.WasPassed()) {
    815    bodyValue.Set("nonce"_ns, aProvider.mNonce.Value());
    816  }
    817  bodyValue.Set("disclosure_text_shown"_ns, "false"_ns);
    818  nsCString serializedIsAutoSelected = isAutoSelected ? "true"_ns : "false"_ns;
    819  bodyValue.Set("is_auto_selected"_ns, serializedIsAutoSelected);
    820  nsAutoCString bodyCString;
    821  bodyValue.Serialize(bodyCString, true);
    822 
    823  RefPtr<WebIdentityParent> relyingParty(aRelyingParty);
    824  return IdentityNetworkHelpers::FetchTokenHelper(idpURI, bodyCString,
    825                                                  aPrincipal)
    826      ->Then(
    827          GetCurrentSerialEventTarget(), __func__,
    828          [aAccount, idpURI, relyingParty,
    829           isAutoSelected](const IdentityAssertionResponse& response) {
    830            // If we were provided a token, resolve with it.
    831            if (response.mToken.WasPassed()) {
    832              return GetTokenPromise::CreateAndResolve(
    833                  std::make_tuple(response.mToken.Value(),
    834                                  NS_ConvertUTF16toUTF8(aAccount.mId),
    835                                  isAutoSelected),
    836                  __func__);
    837            }
    838            // If we don't have a continuation window to open at this stage,
    839            // reject with the appropriate error.
    840            if (!response.mContinue_on.WasPassed()) {
    841              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
    842                                                      __func__);
    843            }
    844            // Construct the URI we are going to open
    845            nsCOMPtr<nsIURI> continueURI;
    846            nsCString continueSpec = response.mContinue_on.Value();
    847            nsresult rv =
    848                NS_NewURI(getter_AddRefs(continueURI), continueSpec.get());
    849            if (NS_FAILED(rv)) {
    850              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
    851                                                      __func__);
    852            }
    853            // It must be same-origin to the URL used to fetch to identity
    854            // assertion
    855            if (!nsScriptSecurityManager::SecurityCompareURIs(continueURI,
    856                                                              idpURI)) {
    857              return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
    858                                                      __func__);
    859            }
    860            // Open the popup, and return the result of its interaction
    861            return AuthorizationPopupForToken(continueURI, relyingParty,
    862                                              aAccount, isAutoSelected);
    863          },
    864          [](nsresult error) {
    865            return GetTokenPromise::CreateAndReject(error, __func__);
    866          });
    867 }
    868 
    869 RefPtr<GetTokenPromise> AuthorizationPopupForToken(
    870    nsIURI* aContinueURI, WebIdentityParent* aRelyingParty,
    871    const IdentityProviderAccount& aAccount, const bool isAutoSelected) {
    872  MOZ_ASSERT(aContinueURI);
    873  IdentityCredentialRequestManager* requestManager =
    874      IdentityCredentialRequestManager::GetInstance();
    875  if (!requestManager) {
    876    return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR, __func__);
    877  }
    878  // Start the process of getting a token for the popup.
    879  // The request manager opens the popup and holds a ref to the promise
    880  // returned. This then gets settles depending on the popup page's behavior (or
    881  // rejects on close).
    882  return requestManager->GetTokenFromPopup(aRelyingParty, aContinueURI)
    883      ->Then(
    884          GetCurrentSerialEventTarget(), __func__,
    885          [aAccount, isAutoSelected](
    886              const std::tuple<nsCString, Maybe<nsCString>>& promiseResult) {
    887            // We will resolve either way with our token from here, but
    888            // We may have an account ID to override the user's selection in the
    889            // account chooser.
    890            nsCString token;
    891            Maybe<nsCString> overridingAccountId;
    892            std::tie(token, overridingAccountId) = promiseResult;
    893            if (overridingAccountId.isSome()) {
    894              return GetTokenPromise::CreateAndResolve(
    895                  std::make_tuple(token, overridingAccountId.value(),
    896                                  isAutoSelected),
    897                  __func__);
    898            }
    899            return GetTokenPromise::CreateAndResolve(
    900                std::make_tuple(token, NS_ConvertUTF16toUTF8(aAccount.mId),
    901                                isAutoSelected),
    902                __func__);
    903          },
    904          [](nsresult rv) {
    905            return GetTokenPromise::CreateAndReject(NS_ERROR_DOM_NETWORK_ERR,
    906                                                    __func__);
    907          });
    908 }
    909 
    910 // static
    911 RefPtr<MozPromise<bool, nsresult, true>> DisconnectInMainProcess(
    912    nsIPrincipal* aDocumentPrincipal,
    913    const IdentityCredentialDisconnectOptions& aOptions) {
    914  MOZ_ASSERT(XRE_IsParentProcess());
    915  nsresult rv;
    916  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
    917      mozilla::components::IdentityCredentialStorageService::Service(&rv);
    918  if (NS_WARN_IF(!icStorageService)) {
    919    return MozPromise<bool, nsresult, true>::CreateAndReject(rv, __func__);
    920  }
    921 
    922  RefPtr<MozPromise<bool, nsresult, true>::Private> resultPromise =
    923      new MozPromise<bool, nsresult, true>::Private(__func__);
    924 
    925  RefPtr<nsIURI> configURI;
    926  rv = NS_NewURI(getter_AddRefs(configURI), aOptions.mConfigURL);
    927  if (NS_FAILED(rv)) {
    928    resultPromise->Reject(NS_ERROR_DOM_MALFORMED_URI, __func__);
    929    return resultPromise;
    930  }
    931 
    932  nsCOMPtr<nsIPrincipal> principal(aDocumentPrincipal);
    933  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
    934      configURI, principal->OriginAttributesRef());
    935 
    936  FetchManifest(principal, aOptions)
    937      ->Then(
    938          GetCurrentSerialEventTarget(), __func__,
    939          [resultPromise, aOptions, icStorageService, configURI, idpPrincipal,
    940           principal](const IdentityProviderAPIConfig& aConfig) {
    941            if (!aConfig.mDisconnect_endpoint.WasPassed()) {
    942              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
    943              return MozPromise<DisconnectedAccount, nsresult,
    944                                true>::CreateAndReject(NS_OK, __func__);
    945            }
    946            RefPtr<nsIURI> disconnectURI;
    947            nsCString disconnectArgument = aConfig.mDisconnect_endpoint.Value();
    948            nsresult rv = NS_NewURI(getter_AddRefs(disconnectURI),
    949                                    disconnectArgument, nullptr, configURI);
    950            if (NS_FAILED(rv)) {
    951              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
    952              return MozPromise<DisconnectedAccount, nsresult,
    953                                true>::CreateAndReject(NS_OK, __func__);
    954            }
    955 
    956            bool connected = false;
    957            rv = icStorageService->Connected(principal, idpPrincipal,
    958                                             &connected);
    959            if (NS_WARN_IF(NS_FAILED(rv)) || !connected) {
    960              resultPromise->Reject(NS_ERROR_DOM_NETWORK_ERR, __func__);
    961              return MozPromise<DisconnectedAccount, nsresult,
    962                                true>::CreateAndReject(NS_OK, __func__);
    963            }
    964 
    965            // Create a new request
    966            URLParams bodyValue;
    967            bodyValue.Set("client_id"_ns, aOptions.mClientId);
    968            bodyValue.Set("account_hint"_ns, aOptions.mAccountHint);
    969            nsAutoCString bodyCString;
    970            bodyValue.Serialize(bodyCString, true);
    971            return IdentityNetworkHelpers::FetchDisconnectHelper(
    972                disconnectURI, bodyCString, principal);
    973          },
    974          [resultPromise](nsresult aError) {
    975            resultPromise->Reject(aError, __func__);
    976            // We reject with NS_OK, so that we don't disconnect accounts in the
    977            // reject callback here.
    978            return MozPromise<DisconnectedAccount, nsresult,
    979                              true>::CreateAndReject(NS_OK, __func__);
    980          })
    981      ->Then(
    982          GetCurrentSerialEventTarget(), __func__,
    983          [icStorageService, principal, idpPrincipal,
    984           resultPromise](const DisconnectedAccount& token) {
    985            bool registered = false, notUsed = false;
    986            nsresult rv = icStorageService->GetState(principal, idpPrincipal,
    987                                                     token.mAccount_id,
    988                                                     &registered, &notUsed);
    989            if (NS_WARN_IF(NS_FAILED(rv))) {
    990              resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
    991              return;
    992            }
    993            if (registered) {
    994              nsresult rv = icStorageService->Delete(principal, idpPrincipal,
    995                                                     token.mAccount_id);
    996              if (NS_WARN_IF(NS_FAILED(rv))) {
    997                resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
    998                return;
    999              }
   1000              resultPromise->Resolve(true, __func__);
   1001            } else {
   1002              nsresult rv =
   1003                  icStorageService->Disconnect(principal, idpPrincipal);
   1004              if (NS_WARN_IF(NS_FAILED(rv))) {
   1005                resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
   1006                return;
   1007              }
   1008              resultPromise->Resolve(true, __func__);
   1009            }
   1010            return;
   1011          },
   1012          [icStorageService, principal, idpPrincipal,
   1013           resultPromise](nsresult error) {
   1014            // Bail out if we already rejected the result above.
   1015            if (error == NS_OK) {
   1016              return;
   1017            }
   1018 
   1019            // If we issued the request and it failed, fall back
   1020            // to clearing all.
   1021            nsresult rv = icStorageService->Disconnect(principal, idpPrincipal);
   1022            if (NS_WARN_IF(NS_FAILED(rv))) {
   1023              resultPromise->Reject(NS_ERROR_UNEXPECTED, __func__);
   1024              return;
   1025            }
   1026            resultPromise->Resolve(true, __func__);
   1027            return;
   1028          });
   1029 
   1030  return resultPromise;
   1031 }
   1032 
   1033 // static
   1034 RefPtr<GetIdentityProviderRequestOptionsWithManifestPromise>
   1035 PromptUserToSelectProvider(
   1036    BrowsingContext* aBrowsingContext,
   1037    const Sequence<IdentityProviderRequestOptions>& aProviders,
   1038    const Sequence<GetManifestPromise::ResolveOrRejectValue>& aManifests) {
   1039  MOZ_ASSERT(aBrowsingContext);
   1040  RefPtr<GetIdentityProviderRequestOptionsWithManifestPromise::Private>
   1041      resultPromise =
   1042          new GetIdentityProviderRequestOptionsWithManifestPromise::Private(
   1043              __func__);
   1044 
   1045  if (NS_WARN_IF(!aBrowsingContext)) {
   1046    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1047    return resultPromise;
   1048  }
   1049 
   1050  nsresult error;
   1051  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
   1052      mozilla::components::IdentityCredentialPromptService::Service(&error);
   1053  if (NS_WARN_IF(!icPromptService)) {
   1054    resultPromise->Reject(error, __func__);
   1055    return resultPromise;
   1056  }
   1057 
   1058  nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(icPromptService);
   1059  AutoJSAPI jsapi;
   1060  if (NS_WARN_IF(!jsapi.Init(wrapped->GetJSObjectGlobal()))) {
   1061    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1062    return resultPromise;
   1063  }
   1064 
   1065  JS::Rooted<JS::Value> providersJS(jsapi.cx());
   1066  bool success = ToJSValue(jsapi.cx(), aProviders, &providersJS);
   1067  if (NS_WARN_IF(!success)) {
   1068    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1069    return resultPromise;
   1070  }
   1071 
   1072  // Convert each settled MozPromise into a Nullable<ResolveValue>
   1073  Sequence<Nullable<IdentityProviderAPIConfig>> manifests;
   1074  for (GetManifestPromise::ResolveOrRejectValue manifest : aManifests) {
   1075    if (manifest.IsResolve()) {
   1076      if (NS_WARN_IF(
   1077              !manifests.AppendElement(manifest.ResolveValue(), fallible))) {
   1078        resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1079        return resultPromise;
   1080      }
   1081    } else {
   1082      if (NS_WARN_IF(!manifests.AppendElement(
   1083              Nullable<IdentityProviderAPIConfig>(), fallible))) {
   1084        resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1085        return resultPromise;
   1086      }
   1087    }
   1088  }
   1089  JS::Rooted<JS::Value> manifestsJS(jsapi.cx());
   1090  success = ToJSValue(jsapi.cx(), manifests, &manifestsJS);
   1091  if (NS_WARN_IF(!success)) {
   1092    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1093    return resultPromise;
   1094  }
   1095 
   1096  RefPtr<Promise> showPromptPromise;
   1097  icPromptService->ShowProviderPrompt(aBrowsingContext, providersJS,
   1098                                      manifestsJS,
   1099                                      getter_AddRefs(showPromptPromise));
   1100 
   1101  showPromptPromise->AddCallbacksWithCycleCollectedArgs(
   1102      [aProviders, aManifests, resultPromise](
   1103          JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
   1104        int32_t result = aValue.toInt32();
   1105        if (result < 0 || (uint32_t)result > aProviders.Length() ||
   1106            (uint32_t)result > aManifests.Length()) {
   1107          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1108          return;
   1109        }
   1110        const IdentityProviderRequestOptions& resolvedProvider =
   1111            aProviders.ElementAt(result);
   1112        if (!aManifests.ElementAt(result).IsResolve()) {
   1113          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1114          return;
   1115        }
   1116        const IdentityProviderAPIConfig& resolvedManifest =
   1117            aManifests.ElementAt(result).ResolveValue();
   1118        resultPromise->Resolve(
   1119            std::make_tuple(resolvedProvider, resolvedManifest), __func__);
   1120      },
   1121      [resultPromise](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
   1122        resultPromise->Reject(
   1123            Promise::TryExtractNSResultFromRejectionValue(aValue), __func__);
   1124      });
   1125  // Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
   1126  showPromptPromise->AppendNativeHandler(
   1127      new MozPromiseRejectOnDestruction{resultPromise, __func__});
   1128 
   1129  return resultPromise;
   1130 }
   1131 
   1132 // static
   1133 RefPtr<GetAccountPromise> PromptUserToSelectAccount(
   1134    BrowsingContext* aBrowsingContext,
   1135    const IdentityProviderAccountList& aAccounts,
   1136    const IdentityProviderRequestOptions& aProvider,
   1137    const IdentityProviderAPIConfig& aManifest) {
   1138  MOZ_ASSERT(aBrowsingContext);
   1139  RefPtr<GetAccountPromise::Private> resultPromise =
   1140      new GetAccountPromise::Private(__func__);
   1141 
   1142  if (NS_WARN_IF(!aBrowsingContext)) {
   1143    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1144    return resultPromise;
   1145  }
   1146 
   1147  nsresult error;
   1148  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
   1149      mozilla::components::IdentityCredentialPromptService::Service(&error);
   1150  if (NS_WARN_IF(!icPromptService)) {
   1151    resultPromise->Reject(error, __func__);
   1152    return resultPromise;
   1153  }
   1154 
   1155  nsCOMPtr<nsIXPConnectWrappedJS> wrapped = do_QueryInterface(icPromptService);
   1156  AutoJSAPI jsapi;
   1157  if (NS_WARN_IF(!jsapi.Init(wrapped->GetJSObjectGlobal()))) {
   1158    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1159    return resultPromise;
   1160  }
   1161 
   1162  JS::Rooted<JS::Value> accountsJS(jsapi.cx());
   1163  bool success = ToJSValue(jsapi.cx(), aAccounts, &accountsJS);
   1164  if (NS_WARN_IF(!success)) {
   1165    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1166    return resultPromise;
   1167  }
   1168 
   1169  JS::Rooted<JS::Value> providerJS(jsapi.cx());
   1170  success = ToJSValue(jsapi.cx(), aProvider, &providerJS);
   1171  if (NS_WARN_IF(!success)) {
   1172    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1173    return resultPromise;
   1174  }
   1175 
   1176  JS::Rooted<JS::Value> manifestJS(jsapi.cx());
   1177  success = ToJSValue(jsapi.cx(), aManifest, &manifestJS);
   1178  if (NS_WARN_IF(!success)) {
   1179    resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1180    return resultPromise;
   1181  }
   1182 
   1183  RefPtr<Promise> showPromptPromise;
   1184  icPromptService->ShowAccountListPrompt(aBrowsingContext, accountsJS,
   1185                                         providerJS, manifestJS,
   1186                                         getter_AddRefs(showPromptPromise));
   1187 
   1188  showPromptPromise->AddCallbacksWithCycleCollectedArgs(
   1189      [aAccounts, resultPromise, aManifest](
   1190          JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
   1191        int32_t result = aValue.toInt32();
   1192        if (!aAccounts.mAccounts.WasPassed() || result < 0 ||
   1193            (uint32_t)result > aAccounts.mAccounts.Value().Length()) {
   1194          resultPromise->Reject(NS_ERROR_FAILURE, __func__);
   1195          return;
   1196        }
   1197        const IdentityProviderAccount& resolved =
   1198            aAccounts.mAccounts.Value().ElementAt(result);
   1199        resultPromise->Resolve(std::make_tuple(aManifest, resolved, false),
   1200                               __func__);
   1201      },
   1202      [resultPromise](JSContext*, JS::Handle<JS::Value> aValue, ErrorResult&) {
   1203        resultPromise->Reject(
   1204            Promise::TryExtractNSResultFromRejectionValue(aValue), __func__);
   1205      });
   1206  // Working around https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85883
   1207  showPromptPromise->AppendNativeHandler(
   1208      new MozPromiseRejectOnDestruction{resultPromise, __func__});
   1209 
   1210  return resultPromise;
   1211 }
   1212 
   1213 // static
   1214 nsresult LinkAccount(nsIPrincipal* aPrincipal, const nsCString& aAccountId,
   1215                     const IdentityProviderRequestOptions& aProvider) {
   1216  MOZ_ASSERT(aPrincipal);
   1217 
   1218  nsresult error;
   1219  nsCOMPtr<nsIIdentityCredentialStorageService> icStorageService =
   1220      mozilla::components::IdentityCredentialStorageService::Service(&error);
   1221  if (NS_WARN_IF(!icStorageService)) {
   1222    return NS_ERROR_NOT_AVAILABLE;
   1223  }
   1224 
   1225  // Check the storage bit
   1226  nsCString configLocation = aProvider.mConfigURL;
   1227  nsCOMPtr<nsIURI> idpURI;
   1228  error = NS_NewURI(getter_AddRefs(idpURI), configLocation);
   1229  if (NS_WARN_IF(NS_FAILED(error))) {
   1230    return error;
   1231  }
   1232  nsCOMPtr<nsIPrincipal> idpPrincipal = BasePrincipal::CreateContentPrincipal(
   1233      idpURI, aPrincipal->OriginAttributesRef());
   1234 
   1235  // Mark as logged in and return
   1236  icStorageService->SetState(aPrincipal, idpPrincipal, aAccountId, true, true);
   1237 
   1238  nsCOMPtr<nsIPermissionManager> permissionManager =
   1239      components::PermissionManager::Service();
   1240 
   1241  if (NS_WARN_IF(!permissionManager)) {
   1242    return NS_ERROR_NOT_AVAILABLE;
   1243  }
   1244 
   1245  nsCString idpOrigin;
   1246  error = idpPrincipal->GetOrigin(idpOrigin);
   1247  NS_ENSURE_SUCCESS(error, error);
   1248 
   1249  permissionManager->AddFromPrincipal(
   1250      aPrincipal, "credential-allow-silent-access^"_ns + idpOrigin,
   1251      nsIPermissionManager::ALLOW_ACTION, nsIPermissionManager::EXPIRE_SESSION,
   1252      0);
   1253  permissionManager->AddFromPrincipal(aPrincipal,
   1254                                      "credential-allow-silent-access"_ns,
   1255                                      nsIPermissionManager::ALLOW_ACTION,
   1256                                      nsIPermissionManager::EXPIRE_SESSION, 0);
   1257  return NS_OK;
   1258 }
   1259 
   1260 // static
   1261 void CloseUserInterface(BrowsingContext* aBrowsingContext) {
   1262  nsresult error;
   1263  nsCOMPtr<nsIIdentityCredentialPromptService> icPromptService =
   1264      mozilla::components::IdentityCredentialPromptService::Service(&error);
   1265  if (NS_WARN_IF(!icPromptService)) {
   1266    return;
   1267  }
   1268  icPromptService->Close(aBrowsingContext);
   1269 }
   1270 
   1271 }  // namespace identity
   1272 
   1273 }  // namespace mozilla::dom