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 ®istered, ¬Used); 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