blob: f7e17574a60e67b11d5cb9a078e7bb944a446295 [file] [log] [blame]
// Copyright 2026 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/webid/identity_credential_source_impl.h"
#include "base/functional/callback.h"
#include "content/browser/renderer_host/render_frame_host_impl.h"
#include "content/browser/webid/webid_utils.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/page.h"
#include "content/public/browser/render_frame_host.h"
#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
namespace content::webid {
DOCUMENT_USER_DATA_KEY_IMPL(IdentityCredentialSourceImpl);
IdentityCredentialSourceImpl::IdentityCredentialSourceImpl(RenderFrameHost* rfh)
: DocumentUserData(rfh),
network_manager_(IdpNetworkRequestManager::Create(
static_cast<RenderFrameHostImpl*>(rfh))),
api_permission_delegate_(
rfh->GetBrowserContext()->GetFederatedIdentityApiPermissionContext()),
permission_delegate_(
rfh->GetBrowserContext()->GetFederatedIdentityPermissionContext()) {}
IdentityCredentialSourceImpl::~IdentityCredentialSourceImpl() = default;
void IdentityCredentialSourceImpl::GetIdentityCredentialSuggestions(
const std::vector<GURL>& embedder_requested_idps,
GetIdentityCredentialSuggestionsCallback callback) {
// Cancel previous request if any.
weak_ptr_factory_.InvalidateWeakPtrs();
accounts_fetcher_.reset();
metrics_.reset();
callback_ = std::move(callback);
std::vector<ConfigFetcher::FetchRequest> fetch_requests;
base::flat_map<GURL, AccountsFetcher::IdentityProviderGetInfo>
token_request_get_infos;
// TODO(crbug.com/475510317): Instead of just fetching IdPs specified by the
// embedder, we should consider other sources such as IdPs who are using the
// passive mode etc. e.g. if there's a pending FedCM request from an IdP, we
// can skip fetching accounts for that IdP if its accounts are already
// fetched.
// TODO(crbug.com/475473059): Right now the caller must pass in the IdP's
// configURL instead of its eTLD+1. We should support the latter and fetch the
// accounts based on it.
for (const auto& idp : embedder_requested_idps) {
if (permission_delegate_->GetIdpSigninStatus(url::Origin::Create(idp))
.value_or(true) == false) {
continue;
}
fetch_requests.emplace_back(idp,
/*force_skip_well_known_enforcement=*/false);
blink::mojom::IdentityProviderRequestOptionsPtr options =
blink::mojom::IdentityProviderRequestOptions::New();
options->config = blink::mojom::IdentityProviderConfig::New();
options->config->config_url = idp;
// We don't have the client_id here, so we pass an empty string.
options->config->client_id = "";
token_request_get_infos.emplace(
idp, AccountsFetcher::IdentityProviderGetInfo(
std::move(options), blink::mojom::RpContext::kSignIn,
blink::mojom::RpMode::kPassive, /*format=*/std::nullopt));
}
if (fetch_requests.empty()) {
std::move(callback_).Run(
std::vector<scoped_refptr<IdentityRequestAccount>>());
return;
}
metrics_ =
std::make_unique<Metrics>(render_frame_host().GetPageUkmSourceId());
// TODO(crbug.com/475864620): Currently the main frame's RFH is passed to the
// fetcher but it's possible that the federated login lives in a cross-origin
// iframe who has different RFH. We should understand the impact using main
// RFH and find the right RFH if needed.
accounts_fetcher_ = std::make_unique<AccountsFetcher>(
render_frame_host(), network_manager_.get(), api_permission_delegate_,
permission_delegate_,
AccountsFetcher::FedCmFetchingParams(
blink::mojom::RpMode::kPassive, /*icon_ideal_size=*/0,
/*icon_minimum_size=*/0, MediationRequirement::kOptional),
base::BindOnce(&IdentityCredentialSourceImpl::OnAccountsFetchCompleted,
weak_ptr_factory_.GetWeakPtr()));
// Fetch accounts from eligible IdPs. The sign-in accounts filtering happens
// after the accounts fetch is completed.
accounts_fetcher_->FetchEndpointsForIdps(
fetch_requests, token_request_get_infos, metrics_.get(),
render_frame_host().GetLastCommittedOrigin(),
/*filter_accounts_callback=*/base::DoNothing());
}
void IdentityCredentialSourceImpl::SetNetworkManagerForTests(
std::unique_ptr<IdpNetworkRequestManager> network_manager) {
network_manager_ = std::move(network_manager);
}
void IdentityCredentialSourceImpl::SetPermissionDelegateForTests(
FederatedIdentityPermissionContextDelegate* permission_delegate) {
permission_delegate_ = permission_delegate;
}
void IdentityCredentialSourceImpl::OnAccountsFetchCompleted(
base::TimeTicks,
std::vector<AccountsFetcher::Result> results) {
std::vector<scoped_refptr<IdentityRequestAccount>> accounts;
for (const auto& result : results) {
if (result.accounts.has_value()) {
auto potentially_sign_in_accounts =
result.accounts->PotentialAccountsForOrigin(
render_frame_host().GetLastCommittedOrigin());
accounts.insert(accounts.end(), potentially_sign_in_accounts.begin(),
potentially_sign_in_accounts.end());
}
}
std::move(callback_).Run(accounts);
}
// static
IdentityCredentialSource* IdentityCredentialSource::FromPage(
content::Page& page) {
return IdentityCredentialSourceImpl::GetOrCreateForCurrentDocument(
&page.GetMainDocument());
}
} // namespace content::webid