blob: 4b0e11f58a9ad7ab1df26a4cc7b39d3ffa979e3f [file] [log] [blame]
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "content/browser/webid/idp_network_request_manager.h"
#include "base/base64.h"
#include "base/json/json_writer.h"
#include "base/rand_util.h"
#include "content/public/browser/identity_request_dialog_controller.h"
#include "content/public/browser/render_frame_host.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/common/color_parser.h"
#include "mojo/public/cpp/bindings/remote.h"
#include "net/base/escape.h"
#include "net/base/isolation_info.h"
#include "net/cookies/site_for_cookies.h"
#include "net/http/http_request_headers.h"
#include "net/http/http_status_code.h"
#include "services/network/public/cpp/is_potentially_trustworthy.h"
#include "services/network/public/cpp/resource_request.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/cpp/simple_url_loader.h"
#include "services/network/public/mojom/url_response_head.mojom.h"
#include "third_party/abseil-cpp/absl/types/optional.h"
#include "ui/gfx/color_utils.h"
#include "url/origin.h"
namespace content {
namespace {
// TODO(kenrb): These need to be defined in the explainer or draft spec and
// referenced here.
// Well-known configuration keys.
constexpr char kIdpEndpointKey[] = "idp_endpoint";
constexpr char kTokenEndpointKey[] = "idtoken_endpoint";
constexpr char kAccountsEndpointKey[] = "accounts_endpoint";
constexpr char kClientIdMetadataEndpointKey[] = "client_id_metadata_endpoint";
constexpr char kRevokeEndpoint[] = "revoke_endpoint";
// Client metadata keys.
constexpr char kPrivacyPolicyKey[] = "privacy_policy_url";
constexpr char kTermsOfServiceKey[] = "terms_of_service_url";
// Accounts endpoint response keys.
constexpr char kAccountsKey[] = "accounts";
constexpr char kIdpBrandingKey[] = "branding";
// Sign-in request response keys.
// TODO(majidvp): For consistency rename to signin_endpoint and move into
// `.well-known`.
constexpr char kSigninUrlKey[] = "signin_url";
constexpr char kIdTokenKey[] = "id_token";
// Token request body keys
constexpr char kAccountKey[] = "sub";
constexpr char kRequestKey[] = "request";
// Revoke request body keys.
constexpr char kClientIdKey[] = "client_id";
constexpr char kJSONMimeType[] = "application/json";
// 1 MiB is an arbitrary upper bound that should account for any reasonable
// response size that is a part of this protocol.
constexpr int maxResponseSizeInKiB = 1024;
net::NetworkTrafficAnnotationTag CreateTrafficAnnotation() {
return net::DefineNetworkTrafficAnnotation("webid", R"(
semantics {
sender: "WebID Backend"
description:
"The WebID API allows websites to initiate user account login "
"with identity providers which provide federated sign-in "
"capabilities using OpenID Connect. The API provides a "
"browser-mediated alternative to previously existing federated "
"sign-in implementations."
trigger:
"A website executes the navigator.id.get() JavaScript method to "
"initiate federated user sign-in to a designated provider."
data:
"An identity request contains a scope of claims specifying what "
"user information is being requested from the identity provider, "
"a label identifying the calling website application, and some "
"OpenID Connect protocol functional fields."
destination: WEBSITE
}
policy {
cookies_allowed: YES
cookies_store: "user"
setting: "Not user controlled. But the verification is a trusted "
"API that doesn't use user data."
policy_exception_justification:
"Not implemented, considered not useful as no content is being "
"uploaded; this request merely downloads the resources on the web."
})");
}
std::unique_ptr<network::ResourceRequest> CreateCredentialedResourceRequest(
GURL target_url,
url::Origin initiator) {
auto resource_request = std::make_unique<network::ResourceRequest>();
auto target_origin = url::Origin::Create(target_url);
auto site_for_cookies = net::SiteForCookies::FromOrigin(target_origin);
resource_request->request_initiator = initiator;
resource_request->url = target_url;
resource_request->site_for_cookies = site_for_cookies;
resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
kJSONMimeType);
// Using a random 64-bit header value. This is just to keep service
// implementations from assuming any particular static value.
const int kBytes = 64 / 8;
std::string webid_header_value;
base::Base64Encode(base::RandBytesAsString(kBytes), &webid_header_value);
resource_request->headers.SetHeader(kSecWebIdCsrfHeader, webid_header_value);
resource_request->credentials_mode =
network::mojom::CredentialsMode::kInclude;
resource_request->trusted_params = network::ResourceRequest::TrustedParams();
resource_request->trusted_params->isolation_info = net::IsolationInfo::Create(
net::IsolationInfo::RequestType::kOther, target_origin, target_origin,
site_for_cookies);
return resource_request;
}
absl::optional<content::IdentityRequestAccount> ParseAccount(
const base::Value& account) {
auto* sub = account.FindStringKey("sub");
auto* email = account.FindStringKey("email");
auto* name = account.FindStringKey("name");
auto* given_name = account.FindStringKey("given_name");
auto* picture = account.FindStringKey("picture");
// required fields
if (!(sub && email && name))
return absl::nullopt;
return content::IdentityRequestAccount(*sub, *email, *name,
given_name ? *given_name : "",
picture ? GURL(*picture) : GURL());
}
// Parses accounts from given Value. Returns true if parse is successful and
// adds parsed accounts to the |account_list|.
bool ParseAccounts(const base::Value* accounts,
IdpNetworkRequestManager::AccountList& account_list) {
if (!accounts->is_list())
return false;
for (auto& account : accounts->GetList()) {
if (!account.is_dict())
return false;
auto parsed_account = ParseAccount(account);
if (parsed_account)
account_list.push_back(parsed_account.value());
}
return true;
}
absl::optional<SkColor> ParseCssColor(const std::string* value) {
if (value == nullptr)
return absl::nullopt;
SkColor color;
if (!content::ParseCssColorString(*value, &color))
return absl::nullopt;
return SkColorSetA(color, 0xff);
}
// Parse IdentityProviderMetadata from given value. Overwrites |idp_metadata|
// with the parsed value.
void ParseIdentityProviderMetadata(const base::Value& idp_metadata_value,
IdentityProviderMetadata& idp_metadata) {
if (!idp_metadata_value.is_dict())
return;
idp_metadata.brand_background_color =
ParseCssColor(idp_metadata_value.FindStringKey("background_color"));
if (idp_metadata.brand_background_color) {
idp_metadata.brand_text_color =
ParseCssColor(idp_metadata_value.FindStringKey("foreground_color"));
if (idp_metadata.brand_text_color) {
float text_contrast_ratio = color_utils::GetContrastRatio(
*idp_metadata.brand_background_color, *idp_metadata.brand_text_color);
if (text_contrast_ratio < color_utils::kMinimumReadableContrastRatio)
idp_metadata.brand_text_color = absl::nullopt;
}
}
}
} // namespace
IdpNetworkRequestManager::Endpoints::Endpoints() = default;
IdpNetworkRequestManager::Endpoints::~Endpoints() = default;
IdpNetworkRequestManager::Endpoints::Endpoints(const Endpoints& other) =
default;
// static
constexpr char IdpNetworkRequestManager::kWellKnownFilePath[];
// static
std::unique_ptr<IdpNetworkRequestManager> IdpNetworkRequestManager::Create(
const GURL& provider,
RenderFrameHost* host) {
// WebID is restricted to secure contexts.
if (!network::IsOriginPotentiallyTrustworthy(url::Origin::Create(provider)))
return nullptr;
// Use the browser process URL loader factory because it has cross-origin
// read blocking disabled.
return std::make_unique<IdpNetworkRequestManager>(
provider, host->GetLastCommittedOrigin(),
host->GetStoragePartition()->GetURLLoaderFactoryForBrowserProcess());
}
IdpNetworkRequestManager::IdpNetworkRequestManager(
const GURL& provider,
const url::Origin& relying_party_origin,
scoped_refptr<network::SharedURLLoaderFactory> loader_factory)
: provider_(provider),
relying_party_origin_(relying_party_origin),
loader_factory_(loader_factory) {}
IdpNetworkRequestManager::~IdpNetworkRequestManager() = default;
void IdpNetworkRequestManager::FetchIdpWellKnown(
FetchWellKnownCallback callback) {
DCHECK(!url_loader_);
DCHECK(!idp_well_known_callback_);
idp_well_known_callback_ = std::move(callback);
const url::Origin& idp_origin = url::Origin::Create(provider_);
GURL target_url =
idp_origin.GetURL().Resolve(IdpNetworkRequestManager::kWellKnownFilePath);
url_loader_ = CreateUncredentialedUrlLoader(target_url);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnWellKnownLoaded,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
void IdpNetworkRequestManager::SendSigninRequest(
const GURL& signin_url,
const std::string& request,
SigninRequestCallback callback) {
DCHECK(!url_loader_);
DCHECK(!signin_request_callback_);
signin_request_callback_ = std::move(callback);
std::string escaped_request = net::EscapeUrlEncodedData(request, true);
GURL target_url = GURL(signin_url.spec() + "?" + escaped_request);
url_loader_ = CreateCredentialedUrlLoader(target_url);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnSigninRequestResponse,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
void IdpNetworkRequestManager::SendAccountsRequest(
const GURL& accounts_url,
AccountsRequestCallback callback) {
DCHECK(!url_loader_);
DCHECK(!accounts_request_callback_);
accounts_request_callback_ = std::move(callback);
// Use ReferrerPolicy::NO_REFERRER for this request so that relying party
// identity is not exposed to the Identity provider via referrer.
// TODO(cbiesinger): I don't think this does the right thing; per comments
// in referrer_policy.h this only applies to redirects.
net::ReferrerPolicy policy = net::ReferrerPolicy::NO_REFERRER;
url_loader_ =
CreateCredentialedUrlLoader(accounts_url, absl::nullopt, policy);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnAccountsRequestResponse,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
// TODO(majidvp): Should accept request in base::Value form instead of string.
std::string CreateTokenRequestBody(const std::string& account,
const std::string& request) {
// Given account and id_request creates the following JSON
// ```json
// {
// "sub": "1234",
// "request": "nonce=abc987987cba&client_id=89898"
// }
// }```
base::Value request_data(base::Value::Type::DICTIONARY);
request_data.SetStringKey(kAccountKey, account);
if (!request.empty())
request_data.SetStringKey(kRequestKey, request);
std::string request_body;
if (!base::JSONWriter::Write(request_data, &request_body)) {
LOG(ERROR) << "Not able to serialize token request body.";
return std::string();
}
return request_body;
}
void IdpNetworkRequestManager::SendTokenRequest(const GURL& token_url,
const std::string& account,
const std::string& request,
TokenRequestCallback callback) {
DCHECK(!url_loader_);
DCHECK(!token_request_callback_);
token_request_callback_ = std::move(callback);
std::string token_request_body = CreateTokenRequestBody(account, request);
if (token_request_body.empty()) {
std::move(token_request_callback_)
.Run(TokenResponse::kInvalidRequestError, std::string());
return;
}
url_loader_ = CreateCredentialedUrlLoader(token_url, token_request_body);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnTokenRequestResponse,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
std::string CreateRevokeRequestBody(const std::string& client_id,
const std::string& account) {
// Given account and id_request creates the following JSON
// ```json
// {
// "sub": "123",
// "request": {
// "client_id": "client1234"
// }
// }```
base::Value request_dict(base::Value::Type::DICTIONARY);
request_dict.SetStringKey(kClientIdKey, client_id);
base::Value request_data(base::Value::Type::DICTIONARY);
request_data.SetStringKey(kAccountKey, account);
request_data.SetKey(kRequestKey, std::move(request_dict));
std::string request_body;
if (!base::JSONWriter::Write(request_data, &request_body)) {
LOG(ERROR) << "Not able to serialize token request body.";
return std::string();
}
return request_body;
}
void IdpNetworkRequestManager::SendRevokeRequest(const GURL& revoke_url,
const std::string& client_id,
const std::string& account_id,
RevokeCallback callback) {
DCHECK(!url_loader_);
DCHECK(!token_request_callback_);
revoke_callback_ = std::move(callback);
std::string revoke_request_body =
CreateRevokeRequestBody(client_id, account_id);
if (revoke_request_body.empty()) {
std::move(revoke_callback_).Run(RevokeResponse::kError);
return;
}
url_loader_ = CreateCredentialedUrlLoader(revoke_url, revoke_request_body);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnRevokeResponse,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
void IdpNetworkRequestManager::SendLogout(const GURL& logout_url,
LogoutCallback callback) {
// TODO(kenrb): Add browser test verifying that the response to this can
// clear cookies. https://crbug.com/1155312.
DCHECK(!url_loader_);
DCHECK(!logout_callback_);
logout_callback_ = std::move(callback);
auto resource_request =
CreateCredentialedResourceRequest(logout_url, relying_party_origin_);
resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept, "*/*");
auto traffic_annotation = CreateTrafficAnnotation();
url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnLogoutCompleted,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
void IdpNetworkRequestManager::OnWellKnownLoaded(
std::unique_ptr<std::string> response_body) {
int response_code = -1;
auto* response_info = url_loader_->ResponseInfo();
if (response_info && response_info->headers)
response_code = response_info->headers->response_code();
url_loader_.reset();
if (response_code == net::HTTP_NOT_FOUND) {
std::move(idp_well_known_callback_)
.Run(FetchStatus::kWebIdNotSupported, Endpoints());
return;
}
if (!response_body) {
std::move(idp_well_known_callback_)
.Run(FetchStatus::kFetchError, Endpoints());
return;
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response_body,
base::BindOnce(&IdpNetworkRequestManager::OnWellKnownParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void IdpNetworkRequestManager::OnWellKnownParsed(
data_decoder::DataDecoder::ValueOrError result) {
auto Fail = [&]() {
std::move(idp_well_known_callback_)
.Run(FetchStatus::kInvalidResponseError, Endpoints());
};
if (!result.value) {
Fail();
return;
}
auto& response = *result.value;
if (!response.is_dict()) {
Fail();
return;
}
auto ExtractEndpoint = [&](const char* key) {
const base::Value* endpoint = response.FindKey(key);
if (!endpoint || !endpoint->is_string()) {
return std::string();
}
return endpoint->GetString();
};
Endpoints endpoints;
endpoints.idp = ExtractEndpoint(kIdpEndpointKey);
endpoints.token = ExtractEndpoint(kTokenEndpointKey);
endpoints.accounts = ExtractEndpoint(kAccountsEndpointKey);
endpoints.client_id_metadata = ExtractEndpoint(kClientIdMetadataEndpointKey);
endpoints.revoke = ExtractEndpoint(kRevokeEndpoint);
std::move(idp_well_known_callback_).Run(FetchStatus::kSuccess, endpoints);
}
void IdpNetworkRequestManager::OnSigninRequestResponse(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
if (!response_body) {
std::move(signin_request_callback_)
.Run(SigninResponse::kSigninError, std::string());
return;
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response_body,
base::BindOnce(&IdpNetworkRequestManager::OnSigninRequestParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void IdpNetworkRequestManager::OnSigninRequestParsed(
data_decoder::DataDecoder::ValueOrError result) {
if (!result.value) {
std::move(signin_request_callback_)
.Run(SigninResponse::kInvalidResponseError, std::string());
return;
}
auto& response = *result.value;
if (!response.is_dict()) {
std::move(signin_request_callback_)
.Run(SigninResponse::kInvalidResponseError, std::string());
return;
}
// TODO(kenrb): This possibly should be part of the well-known file, unless
// IDPs ever have a reason to serve different URLs for sign-in pages.
// https://crbug.com/1141125.
const base::Value* signin_url = response.FindKey(kSigninUrlKey);
const base::Value* id_token = response.FindKey(kIdTokenKey);
// Only one of the fields should be present.
bool signin_url_present = signin_url && signin_url->is_string();
bool token_present = id_token && id_token->is_string();
bool all_present = signin_url_present && token_present;
if (!(signin_url_present || token_present) || all_present) {
std::move(signin_request_callback_)
.Run(SigninResponse::kInvalidResponseError, std::string());
return;
}
if (signin_url) {
std::move(signin_request_callback_)
.Run(SigninResponse::kLoadIdp, signin_url->GetString());
return;
}
std::move(signin_request_callback_)
.Run(SigninResponse::kTokenGranted, id_token->GetString());
}
void IdpNetworkRequestManager::OnAccountsRequestResponse(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
if (!response_body) {
std::move(accounts_request_callback_)
.Run(AccountsResponse::kNetError, AccountList(),
IdentityProviderMetadata());
return;
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response_body,
base::BindOnce(&IdpNetworkRequestManager::OnAccountsRequestParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void IdpNetworkRequestManager::OnAccountsRequestParsed(
data_decoder::DataDecoder::ValueOrError result) {
auto Fail = [&]() {
std::move(accounts_request_callback_)
.Run(AccountsResponse::kInvalidResponseError, AccountList(),
IdentityProviderMetadata());
};
if (!result.value) {
Fail();
return;
}
auto& response = *result.value;
if (!response.is_dict()) {
Fail();
return;
}
AccountList account_list;
const base::Value* accounts = response.FindKey(kAccountsKey);
bool accounts_present = accounts && ParseAccounts(accounts, account_list);
if (!accounts_present) {
Fail();
return;
}
IdentityProviderMetadata idp_metadata;
const base::Value* idp_metadata_value = response.FindKey(kIdpBrandingKey);
if (idp_metadata_value)
ParseIdentityProviderMetadata(*idp_metadata_value, idp_metadata);
std::move(accounts_request_callback_)
.Run(AccountsResponse::kSuccess, std::move(account_list),
std::move(idp_metadata));
}
void IdpNetworkRequestManager::OnTokenRequestResponse(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
if (!response_body) {
std::move(token_request_callback_)
.Run(TokenResponse::kNetError, std::string());
return;
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response_body,
base::BindOnce(&IdpNetworkRequestManager::OnTokenRequestParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void IdpNetworkRequestManager::OnTokenRequestParsed(
data_decoder::DataDecoder::ValueOrError result) {
auto Fail = [&]() {
std::move(token_request_callback_)
.Run(TokenResponse::kInvalidResponseError, std::string());
};
if (!result.value) {
Fail();
return;
}
auto& response = *result.value;
if (!response.is_dict()) {
Fail();
return;
}
const base::Value* id_token = response.FindKey(kIdTokenKey);
bool token_present = id_token && id_token->is_string();
if (!token_present) {
Fail();
return;
}
std::move(token_request_callback_)
.Run(TokenResponse::kSuccess, id_token->GetString());
}
void IdpNetworkRequestManager::OnRevokeResponse(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
RevokeResponse status =
response_body ? RevokeResponse::kSuccess : RevokeResponse::kError;
std::move(revoke_callback_).Run(status);
}
void IdpNetworkRequestManager::OnLogoutCompleted(
std::unique_ptr<std::string> response_body) {
url_loader_.reset();
std::move(logout_callback_).Run();
}
void IdpNetworkRequestManager::FetchClientIdMetadata(
const GURL& endpoint,
const std::string& client_id,
FetchClientIdMetadataCallback callback) {
DCHECK(!url_loader_);
DCHECK(!client_metadata_callback_);
client_metadata_callback_ = std::move(callback);
GURL target_url = endpoint.Resolve(
"?client_id=" + net::EscapeQueryParamValue(client_id, true));
url_loader_ = CreateUncredentialedUrlLoader(target_url);
url_loader_->DownloadToString(
loader_factory_.get(),
base::BindOnce(&IdpNetworkRequestManager::OnClientIdMetadataLoaded,
weak_ptr_factory_.GetWeakPtr()),
maxResponseSizeInKiB * 1024);
}
void IdpNetworkRequestManager::OnClientIdMetadataLoaded(
std::unique_ptr<std::string> response_body) {
int response_code = -1;
auto* response_info = url_loader_->ResponseInfo();
if (response_info && response_info->headers)
response_code = response_info->headers->response_code();
url_loader_.reset();
if (response_code == net::HTTP_NOT_FOUND) {
std::move(client_metadata_callback_)
.Run(FetchStatus::kFetchError, ClientIdMetadata());
return;
}
if (!response_body) {
std::move(client_metadata_callback_)
.Run(FetchStatus::kFetchError, ClientIdMetadata());
return;
}
data_decoder::DataDecoder::ParseJsonIsolated(
*response_body,
base::BindOnce(&IdpNetworkRequestManager::OnClientIdMetadataParsed,
weak_ptr_factory_.GetWeakPtr()));
}
void IdpNetworkRequestManager::OnClientIdMetadataParsed(
data_decoder::DataDecoder::ValueOrError result) {
auto Fail = [&]() {
std::move(client_metadata_callback_)
.Run(FetchStatus::kInvalidResponseError, ClientIdMetadata());
};
if (!result.value) {
Fail();
return;
}
auto& response = *result.value;
if (!response.is_dict()) {
Fail();
return;
}
auto ExtractUrl = [&](const char* key) {
const base::Value* endpoint = response.FindKey(key);
if (!endpoint || !endpoint->is_string()) {
return std::string();
}
return endpoint->GetString();
};
ClientIdMetadata data;
data.privacy_policy_url = ExtractUrl(kPrivacyPolicyKey);
data.terms_of_service_url = ExtractUrl(kTermsOfServiceKey);
std::move(client_metadata_callback_).Run(FetchStatus::kSuccess, data);
}
std::unique_ptr<network::SimpleURLLoader>
IdpNetworkRequestManager::CreateUncredentialedUrlLoader(
const GURL& target_url) const {
net::NetworkTrafficAnnotationTag traffic_annotation =
CreateTrafficAnnotation();
auto resource_request = std::make_unique<network::ResourceRequest>();
const url::Origin& idp_origin = url::Origin::Create(provider_);
resource_request->url = target_url;
// TODO(kenrb): credentials_mode should be kOmit, but for prototyping
// purposes it is useful to be able to run test IdPs on services that always
// require cookies. This needs to be changed back when a better solution is
// found or those test IdPs are no longer required.
// See https://crbug.com/1159177.
resource_request->credentials_mode =
network::mojom::CredentialsMode::kInclude;
resource_request->headers.SetHeader(net::HttpRequestHeaders::kAccept,
kJSONMimeType);
// TODO(kenrb): Not following redirects is important for security because
// this bypasses CORB. Ensure there is a test added.
// https://crbug.com/1155312.
resource_request->redirect_mode = network::mojom::RedirectMode::kError;
resource_request->request_initiator = relying_party_origin_;
resource_request->trusted_params = network::ResourceRequest::TrustedParams();
resource_request->trusted_params->isolation_info =
net::IsolationInfo::Create(net::IsolationInfo::RequestType::kOther,
idp_origin, idp_origin, net::SiteForCookies());
return network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
}
std::unique_ptr<network::SimpleURLLoader>
IdpNetworkRequestManager::CreateCredentialedUrlLoader(
const GURL& target_url,
absl::optional<std::string> request_body,
absl::optional<net::ReferrerPolicy> policy) const {
auto resource_request =
CreateCredentialedResourceRequest(target_url, relying_party_origin_);
if (policy)
resource_request->referrer_policy = *policy;
if (request_body) {
resource_request->method = net::HttpRequestHeaders::kPostMethod;
resource_request->headers.SetHeader(net::HttpRequestHeaders::kContentType,
kJSONMimeType);
}
auto traffic_annotation = CreateTrafficAnnotation();
std::unique_ptr<network::SimpleURLLoader> loader =
network::SimpleURLLoader::Create(std::move(resource_request),
traffic_annotation);
if (request_body)
loader->AttachStringForUpload(*request_body, kJSONMimeType);
return loader;
}
} // namespace content