blob: ffb505f4ba2a0f60921ff668742f9e8acd588edd [file] [log] [blame]
// Copyright 2021 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/webid_utils.h"
#include "base/strings/stringprintf.h"
#include "content/browser/webid/fedcm_metrics.h"
#include "content/browser/webid/flags.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/federated_identity_permission_context_delegate.h"
#include "content/public/common/web_identity.h"
#include "net/base/net_errors.h"
#include "third_party/blink/public/mojom/devtools/inspector_issue.mojom.h"
#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
#include "url/origin.h"
using blink::mojom::FederatedAuthRequestResult;
namespace content::webid {
void SetIdpSigninStatus(content::BrowserContext* context,
const url::Origin& origin,
blink::mojom::IdpSigninStatus status) {
auto* delegate = context->GetFederatedIdentityPermissionContext();
if (!delegate) {
// The embedder may not have a delegate (e.g. webview)
return;
}
delegate->SetIdpSigninStatus(
origin, status == blink::mojom::IdpSigninStatus::kSignedIn);
}
absl::optional<std::string> ComputeConsoleMessageForHttpResponseCode(
const char* endpoint_name,
int http_response_code) {
// Do not add error message for OK response status.
if (http_response_code >= 200 && http_response_code <= 299)
return absl::nullopt;
if (http_response_code < 0) {
// In this case, the |response_code| represents a NET_ERROR, so we should
// use a helper function to ensure we use a meaningful message.
return base::StringPrintf(
"The fetch of the %s resulted in a network error: %s", endpoint_name,
net::ErrorToShortString(http_response_code).c_str());
}
// In this case, the |response_code| represents an HTTP error code, which is
// standard and hence the number by itself should be understood.
return base::StringPrintf(
"When fetching the %s, a %d HTTP response code was received.",
endpoint_name, http_response_code);
}
bool IsEndpointUrlValid(const GURL& identity_provider_config_url,
const GURL& endpoint_url) {
return url::Origin::Create(identity_provider_config_url)
.IsSameOriginWith(endpoint_url);
}
bool ShouldFailAccountsEndpointRequestBecauseNotSignedInWithIdp(
const GURL& identity_provider_config_url,
FederatedIdentityPermissionContextDelegate* permission_delegate) {
if (GetFedCmIdpSigninStatusMode() == FedCmIdpSigninStatusMode::DISABLED) {
return false;
}
const url::Origin idp_origin =
url::Origin::Create(identity_provider_config_url);
const absl::optional<bool> idp_signin_status =
permission_delegate->GetIdpSigninStatus(idp_origin);
return !idp_signin_status.value_or(true);
}
void UpdateIdpSigninStatusForAccountsEndpointResponse(
const GURL& identity_provider_config_url,
IdpNetworkRequestManager::FetchStatus fetch_status,
bool does_idp_have_failing_signin_status,
FederatedIdentityPermissionContextDelegate* permission_delegate,
FedCmMetrics* metrics) {
if (GetFedCmIdpSigninStatusMode() == FedCmIdpSigninStatusMode::DISABLED) {
return;
}
url::Origin idp_origin = url::Origin::Create(identity_provider_config_url);
// Record metrics on effect of IDP sign-in status API.
const absl::optional<bool> idp_signin_status =
permission_delegate->GetIdpSigninStatus(idp_origin);
metrics->RecordIdpSigninMatchStatus(idp_signin_status,
fetch_status.parse_status);
if (fetch_status.parse_status ==
IdpNetworkRequestManager::ParseStatus::kSuccess) {
// `does_idp_have_failing_signin_status` fails the request prior to fetching
// the accounts endpoint for FedCmIdpSigninStatusMode::ENABLED mode but not
// FedCmIdpSigninStatusMode::METRICS_ONLY mode. Do not set the IdP sign-in
// status here if `does_idp_have_failing_signin_status` in
// FedCmIdpSigninStatusMode::METRICS_ONLY mode in order to better emulate
// FedCmIdpSigninStatusMode::ENABLED behavior.
if (!does_idp_have_failing_signin_status) {
permission_delegate->SetIdpSigninStatus(idp_origin, true);
}
} else {
// Ensures that we only fetch accounts unconditionally once.
permission_delegate->SetIdpSigninStatus(idp_origin, false);
}
}
std::string GetConsoleErrorMessageFromResult(
FederatedAuthRequestResult status) {
switch (status) {
case FederatedAuthRequestResult::kShouldEmbargo: {
return "User declined or dismissed prompt. API exponential cool down "
"triggered.";
}
case FederatedAuthRequestResult::kErrorDisabledInSettings: {
return "Third-party sign in was disabled in browser Site Settings.";
}
case FederatedAuthRequestResult::kErrorTooManyRequests: {
return "Only one navigator.credentials.get request may be outstanding at "
"one time.";
}
case FederatedAuthRequestResult::kErrorFetchingWellKnownHttpNotFound: {
return "The provider's FedCM well-known file cannot be found.";
}
case FederatedAuthRequestResult::kErrorFetchingWellKnownNoResponse: {
return "The provider's FedCM well-known file fetch resulted in an "
"error response code.";
}
case FederatedAuthRequestResult::kErrorFetchingWellKnownInvalidResponse: {
return "Provider's FedCM well-known file is invalid.";
}
case FederatedAuthRequestResult::kErrorFetchingWellKnownListEmpty: {
return "Provider's FedCM well-known file has no config URLs.";
}
case FederatedAuthRequestResult::kErrorConfigNotInWellKnown: {
return "Provider's FedCM config file not listed in its well-known file.";
}
case FederatedAuthRequestResult::kErrorWellKnownTooBig: {
return "Provider's FedCM well-known file contains too many config URLs.";
}
case FederatedAuthRequestResult::kErrorFetchingConfigHttpNotFound: {
return "The provider's FedCM config file cannot be found.";
}
case FederatedAuthRequestResult::kErrorFetchingConfigNoResponse: {
return "The provider's FedCM config file fetch resulted in an "
"error response code.";
}
case FederatedAuthRequestResult::kErrorFetchingConfigInvalidResponse: {
return "Provider's FedCM config file is invalid.";
}
case FederatedAuthRequestResult::kErrorFetchingClientMetadataHttpNotFound: {
return "The provider's client metadata endpoint cannot be found.";
}
case FederatedAuthRequestResult::kErrorFetchingClientMetadataNoResponse: {
return "The provider's client metadata fetch resulted in an error "
"response code.";
}
case FederatedAuthRequestResult::
kErrorFetchingClientMetadataInvalidResponse: {
return "Provider's client metadata is invalid.";
}
case FederatedAuthRequestResult::kErrorFetchingAccountsHttpNotFound: {
return "The provider's accounts list endpoint cannot be found.";
}
case FederatedAuthRequestResult::kErrorFetchingAccountsNoResponse: {
return "The provider's accounts list fetch resulted in an error response "
"code.";
}
case FederatedAuthRequestResult::kErrorFetchingAccountsInvalidResponse: {
return "Provider's accounts list is invalid. Should have received an "
"\"accounts\" list, where each account must have at least \"id\", "
"\"name\", and \"email\".";
}
case FederatedAuthRequestResult::kErrorFetchingAccountsListEmpty: {
return "Provider's accounts list is empty.";
}
case FederatedAuthRequestResult::kErrorFetchingIdTokenHttpNotFound: {
return "The provider's id token endpoint cannot be found.";
}
case FederatedAuthRequestResult::kErrorFetchingIdTokenNoResponse: {
return "The provider's token fetch resulted in an error response "
"code.";
}
case FederatedAuthRequestResult::kErrorFetchingIdTokenInvalidResponse: {
return "Provider's token is invalid.";
}
case FederatedAuthRequestResult::kErrorCanceled: {
return "The request has been aborted.";
}
case FederatedAuthRequestResult::kErrorRpPageNotVisible: {
return "RP page is not visible.";
}
case FederatedAuthRequestResult::kError: {
return "Error retrieving a token.";
}
case FederatedAuthRequestResult::kSuccess: {
// Should not be called with success, as we should not add a console
// message for success.
DCHECK(false);
return "";
}
}
}
} // namespace content::webid