0

[FedCM] Rename revoke to disconnect

The feature has not shipped yet so it is OK to rename metrics.

DanglingUntriaged-notes: this CL is just a rename...

Bug: 1473134
Change-Id: I8616fd8b68ab2255897f0cb8b1486d9e17eac5cf
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5049262
Reviewed-by: John Abd-El-Malek <jam@chromium.org>
Reviewed-by: Robert Kaplow <rkaplow@chromium.org>
Reviewed-by: Brendon Tiszka <tiszka@chromium.org>
Reviewed-by: Steve Kobes <skobes@chromium.org>
Commit-Queue: Nicolás Peña <npm@chromium.org>
Reviewed-by: Yi Gu <yigu@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1228089}
This commit is contained in:
Nicolás Peña
2023-11-22 18:10:39 +00:00
committed by Chromium LUCI CQ
parent 60c5e02c30
commit 87f8ab1b7a
48 changed files with 459 additions and 428 deletions

@@ -9171,6 +9171,10 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kFedCmAutoSelectedFlagDescription, kOsAll, flag_descriptions::kFedCmAutoSelectedFlagDescription, kOsAll,
FEATURE_VALUE_TYPE(features::kFedCmAutoSelectedFlag)}, FEATURE_VALUE_TYPE(features::kFedCmAutoSelectedFlag)},
{"fedcm-disconnect", flag_descriptions::kFedCmDisconnectName,
flag_descriptions::kFedCmDisconnectDescription, kOsAll,
FEATURE_VALUE_TYPE(features::kFedCmDisconnect)},
{"fedcm-domain-hint", flag_descriptions::kFedCmDomainHintName, {"fedcm-domain-hint", flag_descriptions::kFedCmDomainHintName,
flag_descriptions::kFedCmDomainHintDescription, kOsAll, flag_descriptions::kFedCmDomainHintDescription, kOsAll,
FEATURE_VALUE_TYPE(features::kFedCmDomainHint)}, FEATURE_VALUE_TYPE(features::kFedCmDomainHint)},
@@ -9201,10 +9205,6 @@ const FeatureEntry kFeatureEntries[] = {
flag_descriptions::kFedCmMultiIdpDescription, kOsDesktop, flag_descriptions::kFedCmMultiIdpDescription, kOsDesktop,
FEATURE_VALUE_TYPE(features::kFedCmMultipleIdentityProviders)}, FEATURE_VALUE_TYPE(features::kFedCmMultipleIdentityProviders)},
{"fedcm-revoke", flag_descriptions::kFedCmRevokeName,
flag_descriptions::kFedCmRevokeDescription, kOsAll,
FEATURE_VALUE_TYPE(features::kFedCmRevoke)},
{"fedcm-selective-disclosure", {"fedcm-selective-disclosure",
flag_descriptions::kFedCmSelectiveDisclosureName, flag_descriptions::kFedCmSelectiveDisclosureName,
flag_descriptions::kFedCmSelectiveDisclosureDescription, kOsAll, flag_descriptions::kFedCmSelectiveDisclosureDescription, kOsAll,

@@ -4084,6 +4084,11 @@
"owners": [ "yigu@chromium.org", "web-identity-eng@google.com"], "owners": [ "yigu@chromium.org", "web-identity-eng@google.com"],
"expiry_milestone": 125 "expiry_milestone": 125
}, },
{
"name": "fedcm-disconnect",
"owners": ["npm@chromium.org", "web-identity-eng@google.com"],
"expiry_milestone": 125
},
{ {
"name": "fedcm-domain-hint", "name": "fedcm-domain-hint",
"owners": [ "npm@chromium.org", "web-identity-eng@google.com"], "owners": [ "npm@chromium.org", "web-identity-eng@google.com"],
@@ -4124,11 +4129,6 @@
"owners": ["npm@chromium.org", "web-identity-eng@google.com"], "owners": ["npm@chromium.org", "web-identity-eng@google.com"],
"expiry_milestone": 125 "expiry_milestone": 125
}, },
{
"name": "fedcm-revoke",
"owners": ["npm@chromium.org", "web-identity-eng@google.com"],
"expiry_milestone": 125
},
{ {
"name": "fedcm-selective-disclosure", "name": "fedcm-selective-disclosure",
"owners": ["goto@chromium.org", "web-identity-eng@google.com"], "owners": ["goto@chromium.org", "web-identity-eng@google.com"],

@@ -1652,6 +1652,11 @@ const char kFedCmAutoSelectedFlagDescription[] =
"auto-selected with developers post user permission to continue with the " "auto-selected with developers post user permission to continue with the "
"IdP."; "IdP.";
const char kFedCmDisconnectName[] = "FedCmDisconnect";
const char kFedCmDisconnectDescription[] =
"Enables the IdentityCredential.disconnect() API which allows "
"disconnecting accounts created via federated login through FedCM.";
const char kFedCmDomainHintName[] = "FedCmDomainHint"; const char kFedCmDomainHintName[] = "FedCmDomainHint";
const char kFedCmDomainHintDescription[] = const char kFedCmDomainHintDescription[] =
"Enables RPs to request only FedCM invocations to only show accounts " "Enables RPs to request only FedCM invocations to only show accounts "
@@ -1683,11 +1688,6 @@ const char kFedCmMultiIdpDescription[] =
"Allows the FedCM API to request multiple identity providers " "Allows the FedCM API to request multiple identity providers "
"simultaneously. Requires FedCM to be enabled as well."; "simultaneously. Requires FedCM to be enabled as well.";
const char kFedCmRevokeName[] = "FedCmRevoke";
const char kFedCmRevokeDescription[] =
"Enables the IdentityCredential.revoke() API which allows revoking "
"accounts created via federated login through FedCM.";
const char kFedCmSelectiveDisclosureName[] = "FedCmSelectiveDisclosure"; const char kFedCmSelectiveDisclosureName[] = "FedCmSelectiveDisclosure";
const char kFedCmSelectiveDisclosureDescription[] = const char kFedCmSelectiveDisclosureDescription[] =
"Allows a relying party to selectively request a set of identity " "Allows a relying party to selectively request a set of identity "

@@ -931,6 +931,9 @@ extern const char kFedCmAuthzDescription[];
extern const char kFedCmAutoSelectedFlagName[]; extern const char kFedCmAutoSelectedFlagName[];
extern const char kFedCmAutoSelectedFlagDescription[]; extern const char kFedCmAutoSelectedFlagDescription[];
extern const char kFedCmDisconnectName[];
extern const char kFedCmDisconnectDescription[];
extern const char kFedCmDomainHintName[]; extern const char kFedCmDomainHintName[];
extern const char kFedCmDomainHintDescription[]; extern const char kFedCmDomainHintDescription[];
@@ -949,9 +952,6 @@ extern const char kFedCmMetricsEndpointDescription[];
extern const char kFedCmMultiIdpName[]; extern const char kFedCmMultiIdpName[];
extern const char kFedCmMultiIdpDescription[]; extern const char kFedCmMultiIdpDescription[];
extern const char kFedCmRevokeName[];
extern const char kFedCmRevokeDescription[];
extern const char kFedCmSelectiveDisclosureName[]; extern const char kFedCmSelectiveDisclosureName[];
extern const char kFedCmSelectiveDisclosureDescription[]; extern const char kFedCmSelectiveDisclosureDescription[];

@@ -2314,12 +2314,12 @@ source_set("browser") {
"webid/fake_identity_request_dialog_controller.h", "webid/fake_identity_request_dialog_controller.h",
"webid/fedcm_metrics.cc", "webid/fedcm_metrics.cc",
"webid/fedcm_metrics.h", "webid/fedcm_metrics.h",
"webid/federated_auth_disconnect_request.cc",
"webid/federated_auth_disconnect_request.h",
"webid/federated_auth_request_impl.cc", "webid/federated_auth_request_impl.cc",
"webid/federated_auth_request_impl.h", "webid/federated_auth_request_impl.h",
"webid/federated_auth_request_page_data.cc", "webid/federated_auth_request_page_data.cc",
"webid/federated_auth_request_page_data.h", "webid/federated_auth_request_page_data.h",
"webid/federated_auth_revoke_request.cc",
"webid/federated_auth_revoke_request.h",
"webid/federated_auth_user_info_request.cc", "webid/federated_auth_user_info_request.cc",
"webid/federated_auth_user_info_request.h", "webid/federated_auth_user_info_request.h",
"webid/federated_provider_fetcher.cc", "webid/federated_provider_fetcher.cc",

@@ -395,12 +395,12 @@ void FedCmMetrics::RecordNumRequestsPerDocument(const int num_requests) {
num_requests); num_requests);
} }
void FedCmMetrics::RecordRevokeStatus(FedCmRevokeStatus status) { void FedCmMetrics::RecordDisconnectStatus(FedCmDisconnectStatus status) {
if (is_disabled_) { if (is_disabled_) {
return; return;
} }
auto RecordUkm = [&](auto& ukm_builder) { auto RecordUkm = [&](auto& ukm_builder) {
ukm_builder.SetStatus_Revoke2(static_cast<int>(status)); ukm_builder.SetStatus_Disconnect(static_cast<int>(status));
ukm_builder.SetFedCmSessionID(session_id_); ukm_builder.SetFedCmSessionID(session_id_);
ukm_builder.Record(ukm::UkmRecorder::Get()); ukm_builder.Record(ukm::UkmRecorder::Get());
}; };
@@ -410,7 +410,7 @@ void FedCmMetrics::RecordRevokeStatus(FedCmRevokeStatus status) {
ukm::builders::Blink_FedCmIdp fedcm_idp_builder(provider_source_id_); ukm::builders::Blink_FedCmIdp fedcm_idp_builder(provider_source_id_);
RecordUkm(fedcm_idp_builder); RecordUkm(fedcm_idp_builder);
base::UmaHistogramEnumeration("Blink.FedCm.Status.Revoke2", status); base::UmaHistogramEnumeration("Blink.FedCm.Status.Disconnect", status);
} }
void FedCmMetrics::RecordErrorDialogResult(FedCmErrorDialogResult result) { void FedCmMetrics::RecordErrorDialogResult(FedCmErrorDialogResult result) {

@@ -111,16 +111,16 @@ enum class PreventSilentAccessFrameType {
kMaxValue = kCrossSiteIframe kMaxValue = kCrossSiteIframe
}; };
// This enum describes the status of a revocation call to the FedCM API. // This enum describes the status of a disconnect call to the FedCM API.
enum class FedCmRevokeStatus { enum class FedCmDisconnectStatus {
// Don't change the meaning or the order of these values because they are // Don't change the meaning or the order of these values because they are
// being recorded in metrics and in sync with the counterpart in enums.xml. // being recorded in metrics and in sync with the counterpart in enums.xml.
kSuccess, kSuccess,
kTooManyRequests, kTooManyRequests,
kUnhandledRequest, kUnhandledRequest,
kNoAccountToRevoke, kNoAccountToDisconnect,
kRevokeUrlIsCrossOrigin, kDisconnectUrlIsCrossOrigin,
kRevocationFailedOnServer, kDisconnectFailedOnServer,
kConfigHttpNotFound, kConfigHttpNotFound,
kConfigNoResponse, kConfigNoResponse,
kConfigInvalidResponse, kConfigInvalidResponse,
@@ -264,8 +264,8 @@ class CONTENT_EXPORT FedCmMetrics {
// FedCM request or for the purpose of MDocs or multi-IDP are not counted. // FedCM request or for the purpose of MDocs or multi-IDP are not counted.
void RecordNumRequestsPerDocument(const int num_requests); void RecordNumRequestsPerDocument(const int num_requests);
// Records the status of the |Revoke| call. // Records the status of the disconnect call.
void RecordRevokeStatus(FedCmRevokeStatus status); void RecordDisconnectStatus(FedCmDisconnectStatus status);
// Records the type of error dialog shown. // Records the type of error dialog shown.
void RecordErrorDialogType( void RecordErrorDialogType(

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "content/browser/webid/federated_auth_revoke_request.h" #include "content/browser/webid/federated_auth_disconnect_request.h"
#include "content/browser/webid/webid_utils.h" #include "content/browser/webid/webid_utils.h"
#include "content/public/browser/federated_identity_api_permission_context_delegate.h" #include "content/public/browser/federated_identity_api_permission_context_delegate.h"
@@ -26,38 +26,39 @@ base::TimeDelta GetRandomRejectionTime() {
using FederatedApiPermissionStatus = using FederatedApiPermissionStatus =
FederatedIdentityApiPermissionContextDelegate::PermissionStatus; FederatedIdentityApiPermissionContextDelegate::PermissionStatus;
using LoginState = IdentityRequestAccount::LoginState; using LoginState = IdentityRequestAccount::LoginState;
using RevokeStatusForMetrics = content::FedCmRevokeStatus; using DisconnectStatusForMetrics = content::FedCmDisconnectStatus;
using blink::mojom::DisconnectStatus;
using blink::mojom::FederatedAuthRequestResult; using blink::mojom::FederatedAuthRequestResult;
using blink::mojom::RevokeStatus;
// static // static
std::unique_ptr<FederatedAuthRevokeRequest> FederatedAuthRevokeRequest::Create( std::unique_ptr<FederatedAuthDisconnectRequest>
FederatedAuthDisconnectRequest::Create(
std::unique_ptr<IdpNetworkRequestManager> network_manager, std::unique_ptr<IdpNetworkRequestManager> network_manager,
FederatedIdentityPermissionContextDelegate* permission_delegate, FederatedIdentityPermissionContextDelegate* permission_delegate,
RenderFrameHost* render_frame_host, RenderFrameHost* render_frame_host,
FedCmMetrics* metrics, FedCmMetrics* metrics,
blink::mojom::IdentityCredentialRevokeOptionsPtr options, blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
bool should_complete_request_immediately) { bool should_complete_request_immediately) {
std::unique_ptr<FederatedAuthRevokeRequest> request = std::unique_ptr<FederatedAuthDisconnectRequest> request =
base::WrapUnique<FederatedAuthRevokeRequest>( base::WrapUnique<FederatedAuthDisconnectRequest>(
new FederatedAuthRevokeRequest(std::move(network_manager), new FederatedAuthDisconnectRequest(
permission_delegate, render_frame_host, std::move(network_manager), permission_delegate,
metrics, std::move(options), render_frame_host, metrics, std::move(options),
should_complete_request_immediately)); should_complete_request_immediately));
return request; return request;
} }
FederatedAuthRevokeRequest::~FederatedAuthRevokeRequest() { FederatedAuthDisconnectRequest::~FederatedAuthDisconnectRequest() {
Complete(RevokeStatus::kError, FedCmRevokeStatus::kUnhandledRequest, Complete(DisconnectStatus::kError, FedCmDisconnectStatus::kUnhandledRequest,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
} }
FederatedAuthRevokeRequest::FederatedAuthRevokeRequest( FederatedAuthDisconnectRequest::FederatedAuthDisconnectRequest(
std::unique_ptr<IdpNetworkRequestManager> network_manager, std::unique_ptr<IdpNetworkRequestManager> network_manager,
FederatedIdentityPermissionContextDelegate* permission_delegate, FederatedIdentityPermissionContextDelegate* permission_delegate,
RenderFrameHost* render_frame_host, RenderFrameHost* render_frame_host,
FedCmMetrics* metrics, FedCmMetrics* metrics,
blink::mojom::IdentityCredentialRevokeOptionsPtr options, blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
bool should_complete_request_immediately) bool should_complete_request_immediately)
: network_manager_(std::move(network_manager)), : network_manager_(std::move(network_manager)),
permission_delegate_(permission_delegate), permission_delegate_(permission_delegate),
@@ -71,15 +72,15 @@ FederatedAuthRevokeRequest::FederatedAuthRevokeRequest(
embedding_origin_ = main_frame->GetLastCommittedOrigin(); embedding_origin_ = main_frame->GetLastCommittedOrigin();
} }
void FederatedAuthRevokeRequest::SetCallbackAndStart( void FederatedAuthDisconnectRequest::SetCallbackAndStart(
blink::mojom::FederatedAuthRequest::RevokeCallback callback, blink::mojom::FederatedAuthRequest::DisconnectCallback callback,
FederatedIdentityApiPermissionContextDelegate* api_permission_delegate) { FederatedIdentityApiPermissionContextDelegate* api_permission_delegate) {
callback_ = std::move(callback); callback_ = std::move(callback);
url::Origin config_origin = url::Origin::Create(options_->config->config_url); url::Origin config_origin = url::Origin::Create(options_->config->config_url);
if (!network::IsOriginPotentiallyTrustworthy(config_origin)) { if (!network::IsOriginPotentiallyTrustworthy(config_origin)) {
Complete(RevokeStatus::kError, Complete(DisconnectStatus::kError,
RevokeStatusForMetrics::kIdpNotPotentiallyTrustworthy, DisconnectStatusForMetrics::kIdpNotPotentiallyTrustworthy,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
return; return;
} }
@@ -87,15 +88,15 @@ void FederatedAuthRevokeRequest::SetCallbackAndStart(
FederatedApiPermissionStatus permission_status = FederatedApiPermissionStatus permission_status =
api_permission_delegate->GetApiPermissionStatus(embedding_origin_); api_permission_delegate->GetApiPermissionStatus(embedding_origin_);
absl::optional<RevokeStatusForMetrics> error_revoke_status; absl::optional<DisconnectStatusForMetrics> error_disconnect_status;
switch (permission_status) { switch (permission_status) {
case FederatedApiPermissionStatus::BLOCKED_VARIATIONS: case FederatedApiPermissionStatus::BLOCKED_VARIATIONS:
error_revoke_status = RevokeStatusForMetrics::kDisabledInFlags; error_disconnect_status = DisconnectStatusForMetrics::kDisabledInFlags;
break; break;
case FederatedApiPermissionStatus::BLOCKED_SETTINGS: case FederatedApiPermissionStatus::BLOCKED_SETTINGS:
error_revoke_status = RevokeStatusForMetrics::kDisabledInSettings; error_disconnect_status = DisconnectStatusForMetrics::kDisabledInSettings;
break; break;
// We do not block revocation on FedCM cooldown. // We do not block disconnect on FedCM cooldown.
case FederatedApiPermissionStatus::BLOCKED_EMBARGO: case FederatedApiPermissionStatus::BLOCKED_EMBARGO:
case FederatedApiPermissionStatus::GRANTED: case FederatedApiPermissionStatus::GRANTED:
// Intentional fall-through. // Intentional fall-through.
@@ -104,8 +105,8 @@ void FederatedAuthRevokeRequest::SetCallbackAndStart(
NOTREACHED(); NOTREACHED();
break; break;
} }
if (error_revoke_status) { if (error_disconnect_status) {
Complete(RevokeStatus::kError, *error_revoke_status, Complete(DisconnectStatus::kError, *error_disconnect_status,
/*should_delay_callback=*/true); /*should_delay_callback=*/true);
return; return;
} }
@@ -115,7 +116,8 @@ void FederatedAuthRevokeRequest::SetCallbackAndStart(
*render_frame_host_, options_->config->config_url, embedding_origin_, *render_frame_host_, options_->config->config_url, embedding_origin_,
origin_, /*account_id=*/absl::nullopt, permission_delegate_, origin_, /*account_id=*/absl::nullopt, permission_delegate_,
api_permission_delegate)) { api_permission_delegate)) {
Complete(RevokeStatus::kError, RevokeStatusForMetrics::kNoAccountToRevoke, Complete(DisconnectStatus::kError,
DisconnectStatusForMetrics::kNoAccountToDisconnect,
/*should_delay_callback=*/true); /*should_delay_callback=*/true);
return; return;
} }
@@ -127,11 +129,11 @@ void FederatedAuthRevokeRequest::SetCallbackAndStart(
{GURL(config_url)}, /*icon_ideal_size=*/0, {GURL(config_url)}, /*icon_ideal_size=*/0,
/*icon_minimum_size=*/0, /*icon_minimum_size=*/0,
base::BindOnce( base::BindOnce(
&FederatedAuthRevokeRequest::OnAllConfigAndWellKnownFetched, &FederatedAuthDisconnectRequest::OnAllConfigAndWellKnownFetched,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void FederatedAuthRevokeRequest::OnAllConfigAndWellKnownFetched( void FederatedAuthDisconnectRequest::OnAllConfigAndWellKnownFetched(
std::vector<FederatedProviderFetcher::FetchResult> fetch_results) { std::vector<FederatedProviderFetcher::FetchResult> fetch_results) {
provider_fetcher_.reset(); provider_fetcher_.reset();
DCHECK(fetch_results.size() == 1u); DCHECK(fetch_results.size() == 1u);
@@ -147,85 +149,85 @@ void FederatedAuthRevokeRequest::OnAllConfigAndWellKnownFetched(
// TODO (crbug.com/1473134): add devtools issues and console errors. // TODO (crbug.com/1473134): add devtools issues and console errors.
FedCmRevokeStatus status; FedCmDisconnectStatus status;
switch (fetch_error.result) { switch (fetch_error.result) {
case FederatedAuthRequestResult::kErrorFetchingWellKnownHttpNotFound: { case FederatedAuthRequestResult::kErrorFetchingWellKnownHttpNotFound: {
status = FedCmRevokeStatus::kWellKnownHttpNotFound; status = FedCmDisconnectStatus::kWellKnownHttpNotFound;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingWellKnownNoResponse: { case FederatedAuthRequestResult::kErrorFetchingWellKnownNoResponse: {
status = FedCmRevokeStatus::kWellKnownNoResponse; status = FedCmDisconnectStatus::kWellKnownNoResponse;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingWellKnownInvalidResponse: { case FederatedAuthRequestResult::kErrorFetchingWellKnownInvalidResponse: {
status = FedCmRevokeStatus::kWellKnownInvalidResponse; status = FedCmDisconnectStatus::kWellKnownInvalidResponse;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingWellKnownListEmpty: { case FederatedAuthRequestResult::kErrorFetchingWellKnownListEmpty: {
status = FedCmRevokeStatus::kWellKnownListEmpty; status = FedCmDisconnectStatus::kWellKnownListEmpty;
break; break;
} }
case FederatedAuthRequestResult:: case FederatedAuthRequestResult::
kErrorFetchingWellKnownInvalidContentType: { kErrorFetchingWellKnownInvalidContentType: {
status = FedCmRevokeStatus::kWellKnownInvalidContentType; status = FedCmDisconnectStatus::kWellKnownInvalidContentType;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingConfigHttpNotFound: { case FederatedAuthRequestResult::kErrorFetchingConfigHttpNotFound: {
status = FedCmRevokeStatus::kConfigHttpNotFound; status = FedCmDisconnectStatus::kConfigHttpNotFound;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingConfigNoResponse: { case FederatedAuthRequestResult::kErrorFetchingConfigNoResponse: {
status = FedCmRevokeStatus::kConfigNoResponse; status = FedCmDisconnectStatus::kConfigNoResponse;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingConfigInvalidResponse: { case FederatedAuthRequestResult::kErrorFetchingConfigInvalidResponse: {
status = FedCmRevokeStatus::kConfigInvalidResponse; status = FedCmDisconnectStatus::kConfigInvalidResponse;
break; break;
} }
case FederatedAuthRequestResult::kErrorFetchingConfigInvalidContentType: { case FederatedAuthRequestResult::kErrorFetchingConfigInvalidContentType: {
status = FedCmRevokeStatus::kConfigInvalidContentType; status = FedCmDisconnectStatus::kConfigInvalidContentType;
break; break;
} }
case FederatedAuthRequestResult::kErrorWellKnownTooBig: { case FederatedAuthRequestResult::kErrorWellKnownTooBig: {
status = FedCmRevokeStatus::kWellKnownTooBig; status = FedCmDisconnectStatus::kWellKnownTooBig;
break; break;
} }
case FederatedAuthRequestResult::kErrorConfigNotInWellKnown: { case FederatedAuthRequestResult::kErrorConfigNotInWellKnown: {
status = FedCmRevokeStatus::kConfigNotInWellKnown; status = FedCmDisconnectStatus::kConfigNotInWellKnown;
break; break;
} }
default: { default: {
status = FedCmRevokeStatus::kUnhandledRequest; status = FedCmDisconnectStatus::kUnhandledRequest;
// The FederatedProviderFetcher does not return any other type of // The FederatedProviderFetcher does not return any other type of
// result. // result.
CHECK(false); CHECK(false);
break; break;
} }
} }
Complete(RevokeStatus::kError, status, Complete(DisconnectStatus::kError, status,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
return; return;
} }
if (!webid::IsEndpointSameOrigin(options_->config->config_url, if (!webid::IsEndpointSameOrigin(options_->config->config_url,
fetch_result.endpoints.revoke)) { fetch_result.endpoints.disconnect)) {
render_frame_host_->AddMessageToConsole( render_frame_host_->AddMessageToConsole(
blink::mojom::ConsoleMessageLevel::kError, blink::mojom::ConsoleMessageLevel::kError,
"Config is missing or has an invalid or cross-origin " "Config is missing or has an invalid or cross-origin "
"\"revocation_endpoint\" URL."); "\"disconnect_endpoint\" URL.");
Complete(RevokeStatus::kError, Complete(DisconnectStatus::kError,
RevokeStatusForMetrics::kRevokeUrlIsCrossOrigin, DisconnectStatusForMetrics::kDisconnectUrlIsCrossOrigin,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
return; return;
} }
network_manager_->SendRevokeRequest( network_manager_->SendDisconnectRequest(
fetch_result.endpoints.revoke, options_->account_hint, fetch_result.endpoints.disconnect, options_->account_hint,
options_->config->client_id, options_->config->client_id,
base::BindOnce(&FederatedAuthRevokeRequest::OnRevokeResponse, base::BindOnce(&FederatedAuthDisconnectRequest::OnDisconnectResponse,
weak_ptr_factory_.GetWeakPtr())); weak_ptr_factory_.GetWeakPtr()));
} }
void FederatedAuthRevokeRequest::OnRevokeResponse( void FederatedAuthDisconnectRequest::OnDisconnectResponse(
IdpNetworkRequestManager::FetchStatus fetch_status, IdpNetworkRequestManager::FetchStatus fetch_status,
const std::string& account_id) { const std::string& account_id) {
CHECK(callback_); CHECK(callback_);
@@ -237,31 +239,32 @@ void FederatedAuthRevokeRequest::OnRevokeResponse(
if (fetch_status.parse_status != if (fetch_status.parse_status !=
IdpNetworkRequestManager::ParseStatus::kSuccess) { IdpNetworkRequestManager::ParseStatus::kSuccess) {
// Even though the response was unsuccessful, the credentialed fetch was // Even though the response was unsuccessful, the credentialed fetch was
// sent to the IDP, so revoke all permissions associated with the triple // sent to the IDP, so disconnect all permissions associated with the triple
// (`origin_`, `embedding_origin`, `idp_origin`). // (`origin_`, `embedding_origin`, `idp_origin`).
permission_delegate_->RevokeSharingPermission( permission_delegate_->RevokeSharingPermission(
origin_, embedding_origin_, idp_origin, /*account_id=*/""); origin_, embedding_origin_, idp_origin, /*account_id=*/"");
Complete(RevokeStatus::kError, Complete(DisconnectStatus::kError,
RevokeStatusForMetrics::kRevocationFailedOnServer, DisconnectStatusForMetrics::kDisconnectFailedOnServer,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
return; return;
} }
permission_delegate_->RevokeSharingPermission(origin_, embedding_origin_, permission_delegate_->RevokeSharingPermission(origin_, embedding_origin_,
idp_origin, account_id); idp_origin, account_id);
Complete(RevokeStatus::kSuccess, RevokeStatusForMetrics::kSuccess, Complete(DisconnectStatus::kSuccess, DisconnectStatusForMetrics::kSuccess,
/*should_delay_callback=*/false); /*should_delay_callback=*/false);
} }
void FederatedAuthRevokeRequest::Complete( void FederatedAuthDisconnectRequest::Complete(
blink::mojom::RevokeStatus status, blink::mojom::DisconnectStatus status,
absl::optional<content::FedCmRevokeStatus> revoke_status_for_metrics, absl::optional<content::FedCmDisconnectStatus>
disconnect_status_for_metrics,
bool should_delay_callback) { bool should_delay_callback) {
if (!callback_) { if (!callback_) {
return; return;
} }
if (revoke_status_for_metrics) { if (disconnect_status_for_metrics) {
metrics_->RecordRevokeStatus(*revoke_status_for_metrics); metrics_->RecordDisconnectStatus(*disconnect_status_for_metrics);
} }
if (!should_delay_callback || should_complete_request_immediately_) { if (!should_delay_callback || should_complete_request_immediately_) {
@@ -270,7 +273,7 @@ void FederatedAuthRevokeRequest::Complete(
} }
base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask( base::SequencedTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE, FROM_HERE,
base::BindOnce(&FederatedAuthRevokeRequest::Complete, base::BindOnce(&FederatedAuthDisconnectRequest::Complete,
weak_ptr_factory_.GetWeakPtr(), status, absl::nullopt, weak_ptr_factory_.GetWeakPtr(), status, absl::nullopt,
/*should_delay_callback=*/false), /*should_delay_callback=*/false),
GetRandomRejectionTime()); GetRandomRejectionTime());

@@ -2,8 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#ifndef CONTENT_BROWSER_WEBID_FEDERATED_AUTH_REVOKE_REQUEST_H_ #ifndef CONTENT_BROWSER_WEBID_FEDERATED_AUTH_DISCONNECT_REQUEST_H_
#define CONTENT_BROWSER_WEBID_FEDERATED_AUTH_REVOKE_REQUEST_H_ #define CONTENT_BROWSER_WEBID_FEDERATED_AUTH_DISCONNECT_REQUEST_H_
#include <memory> #include <memory>
#include <vector> #include <vector>
@@ -23,53 +23,54 @@ class FederatedIdentityPermissionContextDelegate;
class FederatedProviderFetcher; class FederatedProviderFetcher;
class RenderFrameHost; class RenderFrameHost;
// Fetches data for a FedCM revoke request. // Fetches data for a FedCM disconnect request.
class CONTENT_EXPORT FederatedAuthRevokeRequest { class CONTENT_EXPORT FederatedAuthDisconnectRequest {
public: public:
// Returns an object which fetches data for revoke request. // Returns an object which fetches data for disconnect request.
static std::unique_ptr<FederatedAuthRevokeRequest> Create( static std::unique_ptr<FederatedAuthDisconnectRequest> Create(
std::unique_ptr<IdpNetworkRequestManager> network_manager, std::unique_ptr<IdpNetworkRequestManager> network_manager,
FederatedIdentityPermissionContextDelegate* permission_delegate, FederatedIdentityPermissionContextDelegate* permission_delegate,
RenderFrameHost* render_frame_host, RenderFrameHost* render_frame_host,
FedCmMetrics* metrics, FedCmMetrics* metrics,
blink::mojom::IdentityCredentialRevokeOptionsPtr options, blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
bool should_complete_request_immediately); bool should_complete_request_immediately);
FederatedAuthRevokeRequest(const FederatedAuthRevokeRequest&) = delete; FederatedAuthDisconnectRequest(const FederatedAuthDisconnectRequest&) =
FederatedAuthRevokeRequest& operator=(const FederatedAuthRevokeRequest&) =
delete; delete;
~FederatedAuthRevokeRequest(); FederatedAuthDisconnectRequest& operator=(
const FederatedAuthDisconnectRequest&) = delete;
~FederatedAuthDisconnectRequest();
// There is a separate method to set the callback because the callback relies // There is a separate method to set the callback because the callback relies
// on having a pointer to this object, hence cannot be passed in the // on having a pointer to this object, hence cannot be passed in the
// constructor. Once the callback is set, start fetching. // constructor. Once the callback is set, start fetching.
void SetCallbackAndStart( void SetCallbackAndStart(
blink::mojom::FederatedAuthRequest::RevokeCallback callback, blink::mojom::FederatedAuthRequest::DisconnectCallback callback,
FederatedIdentityApiPermissionContextDelegate* api_permission_delegate); FederatedIdentityApiPermissionContextDelegate* api_permission_delegate);
private: private:
FederatedAuthRevokeRequest( FederatedAuthDisconnectRequest(
std::unique_ptr<IdpNetworkRequestManager> network_manager, std::unique_ptr<IdpNetworkRequestManager> network_manager,
FederatedIdentityPermissionContextDelegate* permission_delegate, FederatedIdentityPermissionContextDelegate* permission_delegate,
RenderFrameHost* render_frame_host, RenderFrameHost* render_frame_host,
FedCmMetrics* metrics, FedCmMetrics* metrics,
blink::mojom::IdentityCredentialRevokeOptionsPtr options, blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
bool should_complete_request_immediately); bool should_complete_request_immediately);
void OnAllConfigAndWellKnownFetched( void OnAllConfigAndWellKnownFetched(
std::vector<FederatedProviderFetcher::FetchResult> fetch_results); std::vector<FederatedProviderFetcher::FetchResult> fetch_results);
void OnRevokeResponse(IdpNetworkRequestManager::FetchStatus fetch_status, void OnDisconnectResponse(IdpNetworkRequestManager::FetchStatus fetch_status,
const std::string& account_id); const std::string& account_id);
// `should_delay_callback` represents whether we should call the callback // `should_delay_callback` represents whether we should call the callback
// with some delay or immediately. For some failures we choose to reject // with some delay or immediately. For some failures we choose to reject
// with some delay for privacy reasons. `revoke_status_for_metrics` is // with some delay for privacy reasons. `disconnect_status_for_metrics` is
// non-nullopt if metrics have not yet been recorded for this request. // non-nullopt if metrics have not yet been recorded for this request.
void Complete( void Complete(blink::mojom::DisconnectStatus status,
blink::mojom::RevokeStatus status, absl::optional<content::FedCmDisconnectStatus>
absl::optional<content::FedCmRevokeStatus> revoke_status_for_metrics, disconnect_status_for_metrics,
bool should_delay_callback); bool should_delay_callback);
std::unique_ptr<IdpNetworkRequestManager> network_manager_; std::unique_ptr<IdpNetworkRequestManager> network_manager_;
// Owned by |BrowserContext| // Owned by |BrowserContext|
@@ -80,17 +81,17 @@ class CONTENT_EXPORT FederatedAuthRevokeRequest {
raw_ptr<RenderFrameHost, DanglingUntriaged> render_frame_host_; raw_ptr<RenderFrameHost, DanglingUntriaged> render_frame_host_;
std::unique_ptr<FederatedProviderFetcher> provider_fetcher_; std::unique_ptr<FederatedProviderFetcher> provider_fetcher_;
blink::mojom::IdentityCredentialRevokeOptionsPtr options_; blink::mojom::IdentityCredentialDisconnectOptionsPtr options_;
bool should_complete_request_immediately_{false}; bool should_complete_request_immediately_{false};
url::Origin origin_; url::Origin origin_;
url::Origin embedding_origin_; url::Origin embedding_origin_;
blink::mojom::FederatedAuthRequest::RevokeCallback callback_; blink::mojom::FederatedAuthRequest::DisconnectCallback callback_;
base::WeakPtrFactory<FederatedAuthRevokeRequest> weak_ptr_factory_{this}; base::WeakPtrFactory<FederatedAuthDisconnectRequest> weak_ptr_factory_{this};
}; };
} // namespace content } // namespace content
#endif // CONTENT_BROWSER_WEBID_FEDERATED_AUTH_REVOKE_REQUEST_H_ #endif // CONTENT_BROWSER_WEBID_FEDERATED_AUTH_DISCONNECT_REQUEST_H_

@@ -2,7 +2,7 @@
// Use of this source code is governed by a BSD-style license that can be // Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file. // found in the LICENSE file.
#include "content/browser/webid/federated_auth_revoke_request.h" #include "content/browser/webid/federated_auth_disconnect_request.h"
#include <memory> #include <memory>
#include <set> #include <set>
@@ -36,11 +36,12 @@ using ::testing::_;
using ::testing::NiceMock; using ::testing::NiceMock;
using ::testing::Return; using ::testing::Return;
using DisconnectResponse =
content::IdpNetworkRequestManager::DisconnectResponse;
using DisconnectStatusForMetrics = content::FedCmDisconnectStatus;
using FedCmEntry = ukm::builders::Blink_FedCm; using FedCmEntry = ukm::builders::Blink_FedCm;
using LoginState = content::IdentityRequestAccount::LoginState; using LoginState = content::IdentityRequestAccount::LoginState;
using RevokeResponse = content::IdpNetworkRequestManager::RevokeResponse; using blink::mojom::DisconnectStatus;
using RevokeStatusForMetrics = content::FedCmRevokeStatus;
using blink::mojom::RevokeStatus;
namespace content { namespace content {
namespace { namespace {
@@ -48,13 +49,13 @@ namespace {
constexpr char kRpUrl[] = "https://rp.example"; constexpr char kRpUrl[] = "https://rp.example";
constexpr char kProviderUrl[] = "https://idp.example/fedcm.json"; constexpr char kProviderUrl[] = "https://idp.example/fedcm.json";
constexpr char kAccountsEndpoint[] = "https://idp.example/accounts"; constexpr char kAccountsEndpoint[] = "https://idp.example/accounts";
constexpr char kRevokeEndpoint[] = "https://idp.example/revoke"; constexpr char kDisconnectEndpoint[] = "https://idp.example/disconnect";
constexpr char kTokenEndpoint[] = "https://idp.example/token"; constexpr char kTokenEndpoint[] = "https://idp.example/token";
constexpr char kLoginUrl[] = "https://idp.example/login"; constexpr char kLoginUrl[] = "https://idp.example/login";
constexpr char kClientId[] = "client_id_123"; constexpr char kClientId[] = "client_id_123";
// Not used? // Not used?
// constexpr char kIdpRevokeUrl[] = "https://idp.example/revoke"; // constexpr char kIdpDisconnectUrl[] = "https://idp.example/disconnect";
struct AccountConfig { struct AccountConfig {
std::string id; std::string id;
@@ -65,7 +66,7 @@ struct AccountConfig {
struct Config { struct Config {
std::vector<AccountConfig> accounts; std::vector<AccountConfig> accounts;
FetchStatus config_fetch_status; FetchStatus config_fetch_status;
FetchStatus revoke_fetch_status; FetchStatus disconnect_fetch_status;
std::string config_url; std::string config_url;
}; };
@@ -74,24 +75,25 @@ Config kValidConfig = {
{{"account1", /*login_state=*/absl::nullopt, {{"account1", /*login_state=*/absl::nullopt,
/*was_granted_sharing_permission=*/true}}, /*was_granted_sharing_permission=*/true}},
/*config_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK}, /*config_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK},
/*revoke_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK}, /*disconnect_fetch_status=*/{ParseStatus::kSuccess, net::HTTP_OK},
kProviderUrl}; kProviderUrl};
// Helper class for receiving the Revoke method callback. // Helper class for receiving the Disconnect method callback.
class RevokeRequestCallbackHelper { class DisconnectRequestCallbackHelper {
public: public:
RevokeRequestCallbackHelper() = default; DisconnectRequestCallbackHelper() = default;
~RevokeRequestCallbackHelper() = default; ~DisconnectRequestCallbackHelper() = default;
RevokeRequestCallbackHelper(const RevokeRequestCallbackHelper&) = delete; DisconnectRequestCallbackHelper(const DisconnectRequestCallbackHelper&) =
RevokeRequestCallbackHelper& operator=(const RevokeRequestCallbackHelper&) =
delete; delete;
DisconnectRequestCallbackHelper& operator=(
const DisconnectRequestCallbackHelper&) = delete;
RevokeStatus status() const { return status_; } DisconnectStatus status() const { return status_; }
// This can only be called once per lifetime of this object. // This can only be called once per lifetime of this object.
base::OnceCallback<void(RevokeStatus)> callback() { base::OnceCallback<void(DisconnectStatus)> callback() {
return base::BindOnce(&RevokeRequestCallbackHelper::ReceiverMethod, return base::BindOnce(&DisconnectRequestCallbackHelper::ReceiverMethod,
base::Unretained(this)); base::Unretained(this));
} }
@@ -105,7 +107,7 @@ class RevokeRequestCallbackHelper {
} }
private: private:
void ReceiverMethod(RevokeStatus status) { void ReceiverMethod(DisconnectStatus status) {
status_ = status; status_ = status;
was_called_ = true; was_called_ = true;
wait_for_callback_loop_.Quit(); wait_for_callback_loop_.Quit();
@@ -113,7 +115,7 @@ class RevokeRequestCallbackHelper {
bool was_called_ = false; bool was_called_ = false;
base::RunLoop wait_for_callback_loop_; base::RunLoop wait_for_callback_loop_;
RevokeStatus status_; DisconnectStatus status_;
}; };
class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager { class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
@@ -143,7 +145,7 @@ class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
IdpNetworkRequestManager::Endpoints endpoints; IdpNetworkRequestManager::Endpoints endpoints;
endpoints.accounts = GURL(kAccountsEndpoint); endpoints.accounts = GURL(kAccountsEndpoint);
endpoints.token = GURL(kTokenEndpoint); endpoints.token = GURL(kTokenEndpoint);
endpoints.revoke = GURL(kRevokeEndpoint); endpoints.disconnect = GURL(kDisconnectEndpoint);
IdentityProviderMetadata idp_metadata; IdentityProviderMetadata idp_metadata;
idp_metadata.config_url = GURL(config_.config_url); idp_metadata.config_url = GURL(config_.config_url);
@@ -154,19 +156,20 @@ class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
endpoints, idp_metadata)); endpoints, idp_metadata));
} }
void SendRevokeRequest(const GURL& revoke_url, void SendDisconnectRequest(const GURL& disconnect_url,
const std::string& account_hint, const std::string& account_hint,
const std::string& client_id, const std::string& client_id,
RevokeCallback callback) override { DisconnectCallback callback) override {
has_fetched_revoke_ = true; has_fetched_disconnect_ = true;
base::SequencedTaskRunner::GetCurrentDefault()->PostTask( base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE, base::BindOnce(std::move(callback), FROM_HERE,
config_.revoke_fetch_status, account_hint)); base::BindOnce(std::move(callback), config_.disconnect_fetch_status,
account_hint));
} }
bool has_fetched_well_known_{false}; bool has_fetched_well_known_{false};
bool has_fetched_config_{false}; bool has_fetched_config_{false};
bool has_fetched_revoke_{false}; bool has_fetched_disconnect_{false};
private: private:
const Config config_; const Config config_;
@@ -219,16 +222,17 @@ class TestPermissionDelegate : public MockPermissionDelegate {
} // namespace } // namespace
class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness { class FederatedAuthDisconnectRequestTest
: public RenderViewHostImplTestHarness {
public: public:
FederatedAuthRevokeRequestTest() { FederatedAuthDisconnectRequestTest() {
ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>(); ukm_recorder_ = std::make_unique<ukm::TestAutoSetUkmRecorder>();
} }
~FederatedAuthRevokeRequestTest() override = default; ~FederatedAuthDisconnectRequestTest() override = default;
void SetUp() override { void SetUp() override {
RenderViewHostImplTestHarness::SetUp(); RenderViewHostImplTestHarness::SetUp();
scoped_feature_list_.InitAndEnableFeature(features::kFedCmRevoke); scoped_feature_list_.InitAndEnableFeature(features::kFedCmDisconnect);
api_permission_delegate_ = std::make_unique<TestApiPermissionDelegate>(); api_permission_delegate_ = std::make_unique<TestApiPermissionDelegate>();
permission_delegate_ = std::make_unique<TestPermissionDelegate>(); permission_delegate_ = std::make_unique<TestPermissionDelegate>();
@@ -242,8 +246,8 @@ class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness {
RenderViewHostImplTestHarness::TearDown(); RenderViewHostImplTestHarness::TearDown();
} }
void RunRevokeTest(const Config& config, void RunDisconnectTest(const Config& config,
RevokeStatus expected_revoke_status) { DisconnectStatus expected_disconnect_status) {
permission_delegate_->SetConfig(config); permission_delegate_->SetConfig(config);
auto network_manager = auto network_manager =
@@ -254,15 +258,15 @@ class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness {
GURL(config.config_url), main_test_rfh()->GetPageUkmSourceId(), GURL(config.config_url), main_test_rfh()->GetPageUkmSourceId(),
/*session_id=*/1, /*is_disabled=*/false); /*session_id=*/1, /*is_disabled=*/false);
blink::mojom::IdentityCredentialRevokeOptionsPtr options = blink::mojom::IdentityCredentialDisconnectOptionsPtr options =
blink::mojom::IdentityCredentialRevokeOptions::New(); blink::mojom::IdentityCredentialDisconnectOptions::New();
options->config = blink::mojom::IdentityProviderConfig::New(); options->config = blink::mojom::IdentityProviderConfig::New();
options->config->config_url = GURL(config.config_url); options->config->config_url = GURL(config.config_url);
options->config->client_id = kClientId; options->config->client_id = kClientId;
options->account_hint = "accountHint"; options->account_hint = "accountHint";
RevokeRequestCallbackHelper callback_helper; DisconnectRequestCallbackHelper callback_helper;
request_ = FederatedAuthRevokeRequest::Create( request_ = FederatedAuthDisconnectRequest::Create(
std::move(network_manager), permission_delegate_.get(), main_rfh(), std::move(network_manager), permission_delegate_.get(), main_rfh(),
metrics_.get(), std::move(options), metrics_.get(), std::move(options),
/*should_complete_request_immediately=*/true); /*should_complete_request_immediately=*/true);
@@ -270,11 +274,11 @@ class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness {
api_permission_delegate_.get()); api_permission_delegate_.get());
callback_helper.WaitForCallback(); callback_helper.WaitForCallback();
EXPECT_EQ(expected_revoke_status, callback_helper.status()); EXPECT_EQ(expected_disconnect_status, callback_helper.status());
} }
void ExpectRevokeStatusUKM(RevokeStatusForMetrics status, void ExpectDisconnectStatusUKM(DisconnectStatusForMetrics status,
const char* entry_name) { const char* entry_name) {
auto entries = ukm_recorder()->GetEntriesByName(entry_name); auto entries = ukm_recorder()->GetEntriesByName(entry_name);
ASSERT_FALSE(entries.empty()) ASSERT_FALSE(entries.empty())
@@ -285,24 +289,25 @@ class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness {
bool metric_found = false; bool metric_found = false;
for (const auto* const entry : entries) { for (const auto* const entry : entries) {
const int64_t* metric = const int64_t* metric =
ukm_recorder()->GetEntryMetric(entry, "Status.Revoke2"); ukm_recorder()->GetEntryMetric(entry, "Status.Disconnect");
if (!metric) { if (!metric) {
continue; continue;
} }
EXPECT_FALSE(metric_found) EXPECT_FALSE(metric_found)
<< "Found more than one entry with Status.Revoke2 in " << entry_name; << "Found more than one entry with Status.Disconnect in "
<< entry_name;
metric_found = true; metric_found = true;
EXPECT_EQ(static_cast<int>(status), *metric) EXPECT_EQ(static_cast<int>(status), *metric)
<< "Unexpected status recorded in " << entry_name; << "Unexpected status recorded in " << entry_name;
} }
EXPECT_TRUE(metric_found) EXPECT_TRUE(metric_found)
<< "No Status.Revoke2 entry was found in " << entry_name; << "No Status.Disconnect entry was found in " << entry_name;
} }
bool DidFetchAnyEndpoint() { bool DidFetchAnyEndpoint() {
return network_manager_->has_fetched_well_known_ || return network_manager_->has_fetched_well_known_ ||
network_manager_->has_fetched_config_ || network_manager_->has_fetched_config_ ||
network_manager_->has_fetched_revoke_; network_manager_->has_fetched_disconnect_;
} }
ukm::TestAutoSetUkmRecorder* ukm_recorder() { return ukm_recorder_.get(); } ukm::TestAutoSetUkmRecorder* ukm_recorder() { return ukm_recorder_.get(); }
@@ -313,38 +318,39 @@ class FederatedAuthRevokeRequestTest : public RenderViewHostImplTestHarness {
std::unique_ptr<TestApiPermissionDelegate> api_permission_delegate_; std::unique_ptr<TestApiPermissionDelegate> api_permission_delegate_;
std::unique_ptr<TestPermissionDelegate> permission_delegate_; std::unique_ptr<TestPermissionDelegate> permission_delegate_;
std::unique_ptr<FedCmMetrics> metrics_; std::unique_ptr<FedCmMetrics> metrics_;
std::unique_ptr<FederatedAuthRevokeRequest> request_; std::unique_ptr<FederatedAuthDisconnectRequest> request_;
base::HistogramTester histogram_tester_; base::HistogramTester histogram_tester_;
std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_; std::unique_ptr<ukm::TestAutoSetUkmRecorder> ukm_recorder_;
}; };
TEST_F(FederatedAuthRevokeRequestTest, Success) { TEST_F(FederatedAuthDisconnectRequestTest, Success) {
Config config = kValidConfig; Config config = kValidConfig;
RunRevokeTest(config, RevokeStatus::kSuccess); RunDisconnectTest(config, DisconnectStatus::kSuccess);
EXPECT_TRUE(network_manager_->has_fetched_well_known_); EXPECT_TRUE(network_manager_->has_fetched_well_known_);
EXPECT_TRUE(network_manager_->has_fetched_config_); EXPECT_TRUE(network_manager_->has_fetched_config_);
EXPECT_TRUE(network_manager_->has_fetched_revoke_); EXPECT_TRUE(network_manager_->has_fetched_disconnect_);
histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.Revoke2", histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.Disconnect",
RevokeStatusForMetrics::kSuccess, 1); DisconnectStatusForMetrics::kSuccess, 1);
ExpectRevokeStatusUKM(RevokeStatusForMetrics::kSuccess, ExpectDisconnectStatusUKM(DisconnectStatusForMetrics::kSuccess,
FedCmEntry::kEntryName); FedCmEntry::kEntryName);
} }
TEST_F(FederatedAuthRevokeRequestTest, NotTrustworthyIdP) { TEST_F(FederatedAuthDisconnectRequestTest, NotTrustworthyIdP) {
Config config = kValidConfig; Config config = kValidConfig;
config.config_url = "http://idp.example/fedcm.json"; config.config_url = "http://idp.example/fedcm.json";
RunRevokeTest(config, RevokeStatus::kError); RunDisconnectTest(config, DisconnectStatus::kError);
EXPECT_FALSE(DidFetchAnyEndpoint()); EXPECT_FALSE(DidFetchAnyEndpoint());
histogram_tester_.ExpectUniqueSample( histogram_tester_.ExpectUniqueSample(
"Blink.FedCm.Status.Revoke2", "Blink.FedCm.Status.Disconnect",
RevokeStatusForMetrics::kIdpNotPotentiallyTrustworthy, 1); DisconnectStatusForMetrics::kIdpNotPotentiallyTrustworthy, 1);
ExpectRevokeStatusUKM(RevokeStatusForMetrics::kIdpNotPotentiallyTrustworthy, ExpectDisconnectStatusUKM(
FedCmEntry::kEntryName); DisconnectStatusForMetrics::kIdpNotPotentiallyTrustworthy,
FedCmEntry::kEntryName);
} }
TEST_F(FederatedAuthRevokeRequestTest, TEST_F(FederatedAuthDisconnectRequestTest,
NoSharingPermissionButIdpHasThirdPartyCookiesAccessAndClaimsSignin) { NoSharingPermissionButIdpHasThirdPartyCookiesAccessAndClaimsSignin) {
base::test::ScopedFeatureList list; base::test::ScopedFeatureList list;
list.InitAndEnableFeature(features::kFedCmExemptIdpWithThirdPartyCookies); list.InitAndEnableFeature(features::kFedCmExemptIdpWithThirdPartyCookies);
@@ -361,15 +367,15 @@ TEST_F(FederatedAuthRevokeRequestTest,
url::Origin::Create(GURL(kRpUrl)))) url::Origin::Create(GURL(kRpUrl))))
.WillOnce(Return(true)); .WillOnce(Return(true));
RunRevokeTest(config, RevokeStatus::kSuccess); RunDisconnectTest(config, DisconnectStatus::kSuccess);
EXPECT_TRUE(network_manager_->has_fetched_well_known_); EXPECT_TRUE(network_manager_->has_fetched_well_known_);
EXPECT_TRUE(network_manager_->has_fetched_config_); EXPECT_TRUE(network_manager_->has_fetched_config_);
EXPECT_TRUE(network_manager_->has_fetched_revoke_); EXPECT_TRUE(network_manager_->has_fetched_disconnect_);
histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.Revoke2", histogram_tester_.ExpectUniqueSample("Blink.FedCm.Status.Disconnect",
RevokeStatusForMetrics::kSuccess, 1); DisconnectStatusForMetrics::kSuccess, 1);
ExpectRevokeStatusUKM(RevokeStatusForMetrics::kSuccess, ExpectDisconnectStatusUKM(DisconnectStatusForMetrics::kSuccess,
FedCmEntry::kEntryName); FedCmEntry::kEntryName);
} }
} // namespace content } // namespace content

@@ -25,8 +25,8 @@
#include "content/browser/web_contents/web_contents_impl.h" #include "content/browser/web_contents/web_contents_impl.h"
#include "content/browser/webid/digital_credentials/digital_credential_provider.h" #include "content/browser/webid/digital_credentials/digital_credential_provider.h"
#include "content/browser/webid/fake_identity_request_dialog_controller.h" #include "content/browser/webid/fake_identity_request_dialog_controller.h"
#include "content/browser/webid/federated_auth_disconnect_request.h"
#include "content/browser/webid/federated_auth_request_page_data.h" #include "content/browser/webid/federated_auth_request_page_data.h"
#include "content/browser/webid/federated_auth_revoke_request.h"
#include "content/browser/webid/federated_auth_user_info_request.h" #include "content/browser/webid/federated_auth_user_info_request.h"
#include "content/browser/webid/flags.h" #include "content/browser/webid/flags.h"
#include "content/browser/webid/identity_registry.h" #include "content/browser/webid/identity_registry.h"
@@ -46,6 +46,7 @@
#include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h" #include "third_party/blink/public/mojom/webid/federated_auth_request.mojom.h"
using base::Value; using base::Value;
using blink::mojom::DisconnectStatus;
using blink::mojom::FederatedAuthRequestResult; using blink::mojom::FederatedAuthRequestResult;
using blink::mojom::IdentityProviderConfig; using blink::mojom::IdentityProviderConfig;
using blink::mojom::IdentityProviderConfigPtr; using blink::mojom::IdentityProviderConfigPtr;
@@ -54,10 +55,9 @@ using blink::mojom::IdentityProviderRequestOptions;
using blink::mojom::IdentityProviderRequestOptionsPtr; using blink::mojom::IdentityProviderRequestOptionsPtr;
using blink::mojom::RequestTokenStatus; using blink::mojom::RequestTokenStatus;
using blink::mojom::RequestUserInfoStatus; using blink::mojom::RequestUserInfoStatus;
using blink::mojom::RevokeStatus;
using FederatedApiPermissionStatus = using FederatedApiPermissionStatus =
content::FederatedIdentityApiPermissionContextDelegate::PermissionStatus; content::FederatedIdentityApiPermissionContextDelegate::PermissionStatus;
using RevokeStatusForMetrics = content::FedCmRevokeStatus; using DisconnectStatusForMetrics = content::FedCmDisconnectStatus;
using TokenStatus = content::FedCmRequestIdTokenStatus; using TokenStatus = content::FedCmRequestIdTokenStatus;
using SignInStateMatchStatus = content::FedCmSignInStateMatchStatus; using SignInStateMatchStatus = content::FedCmSignInStateMatchStatus;
using TokenResponseType = using TokenResponseType =
@@ -499,11 +499,11 @@ FederatedAuthRequestImpl::~FederatedAuthRequestImpl() {
// naturally. // naturally.
user_info_requests_.clear(); user_info_requests_.clear();
// Calls |FederatedAuthRevokeRequest|'s destructor to complete the revocation // Calls |FederatedAuthDisconnectRequest|'s destructor to complete the
// request. This is needed because otherwise some resources like // revocation request. This is needed because otherwise some resources like
// `fedcm_metrics_` may no longer be usable when the destructor get invoked // `fedcm_metrics_` may no longer be usable when the destructor get invoked
// naturally. // naturally.
revoke_request_.reset(); disconnect_request_.reset();
// Since FederatedAuthRequestImpl is a subclass of // Since FederatedAuthRequestImpl is a subclass of
// DocumentService<blink::mojom::FederatedAuthRequest>, it only lives as long // DocumentService<blink::mojom::FederatedAuthRequest>, it only lives as long
@@ -1192,16 +1192,16 @@ void FederatedAuthRequestImpl::OnAllConfigAndWellKnownFetched(
} }
} }
void FederatedAuthRequestImpl::CompleteRevokeRequest( void FederatedAuthRequestImpl::CompleteDisconnectRequest(
RevokeCallback callback, DisconnectCallback callback,
blink::mojom::RevokeStatus status) { blink::mojom::DisconnectStatus status) {
if (!revoke_request_) { if (!disconnect_request_) {
NOTREACHED() << "The completed revocation request is nowhere to be found"; NOTREACHED() << "The completed disconnect request is nowhere to be found";
return; return;
} }
std::move(callback).Run(status); std::move(callback).Run(status);
revoke_request_.reset(); disconnect_request_.reset();
} }
void FederatedAuthRequestImpl::OnClientMetadataResponseReceived( void FederatedAuthRequestImpl::OnClientMetadataResponseReceived(
@@ -2622,13 +2622,13 @@ void FederatedAuthRequestImpl::PreventSilentAccess(
std::move(callback).Run(); std::move(callback).Run();
} }
void FederatedAuthRequestImpl::Revoke( void FederatedAuthRequestImpl::Disconnect(
blink::mojom::IdentityCredentialRevokeOptionsPtr options, blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
RevokeCallback callback) { DisconnectCallback callback) {
if (!IsFedCmRevokeEnabled()) { if (!IsFedCmDisconnectEnabled()) {
// This should only happen when the request comes from a compromised // This should only happen when the request comes from a compromised
// renderer. // renderer.
std::move(callback).Run(RevokeStatus::kError); std::move(callback).Run(DisconnectStatus::kError);
return; return;
} }
if (!fedcm_metrics_) { if (!fedcm_metrics_) {
@@ -2642,9 +2642,10 @@ void FederatedAuthRequestImpl::Revoke(
options->config->config_url, render_frame_host().GetPageUkmSourceId(), options->config->config_url, render_frame_host().GetPageUkmSourceId(),
/*is_disabled=*/false); /*is_disabled=*/false);
} }
if (revoke_request_) { if (disconnect_request_) {
fedcm_metrics_->RecordRevokeStatus(FedCmRevokeStatus::kTooManyRequests); fedcm_metrics_->RecordDisconnectStatus(
std::move(callback).Run(RevokeStatus::kErrorTooManyRequests); FedCmDisconnectStatus::kTooManyRequests);
std::move(callback).Run(DisconnectStatus::kErrorTooManyRequests);
return; return;
} }
@@ -2658,14 +2659,15 @@ void FederatedAuthRequestImpl::Revoke(
auto network_manager = CreateNetworkManager(); auto network_manager = CreateNetworkManager();
revoke_request_ = FederatedAuthRevokeRequest::Create( disconnect_request_ = FederatedAuthDisconnectRequest::Create(
std::move(network_manager), permission_delegate_, &render_frame_host(), std::move(network_manager), permission_delegate_, &render_frame_host(),
fedcm_metrics_.get(), std::move(options), fedcm_metrics_.get(), std::move(options),
should_complete_request_immediately_); should_complete_request_immediately_);
FederatedAuthRevokeRequest* revoke_request_ptr = revoke_request_.get(); FederatedAuthDisconnectRequest* disconnect_request_ptr =
disconnect_request_.get();
revoke_request_ptr->SetCallbackAndStart( disconnect_request_ptr->SetCallbackAndStart(
base::BindOnce(&FederatedAuthRequestImpl::CompleteRevokeRequest, base::BindOnce(&FederatedAuthRequestImpl::CompleteDisconnectRequest,
weak_ptr_factory_.GetWeakPtr(), std::move(callback)), weak_ptr_factory_.GetWeakPtr(), std::move(callback)),
api_permission_delegate_); api_permission_delegate_);
} }

@@ -31,7 +31,7 @@
namespace content { namespace content {
class FederatedAuthRevokeRequest; class FederatedAuthDisconnectRequest;
class FederatedAuthUserInfoRequest; class FederatedAuthUserInfoRequest;
class FederatedIdentityApiPermissionContextDelegate; class FederatedIdentityApiPermissionContextDelegate;
class FederatedIdentityAutoReauthnPermissionContextDelegate; class FederatedIdentityAutoReauthnPermissionContextDelegate;
@@ -89,8 +89,8 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
void UnregisterIdP(const ::GURL& idp, UnregisterIdPCallback) override; void UnregisterIdP(const ::GURL& idp, UnregisterIdPCallback) override;
void CloseModalDialogView() override; void CloseModalDialogView() override;
void PreventSilentAccess(PreventSilentAccessCallback callback) override; void PreventSilentAccess(PreventSilentAccessCallback callback) override;
void Revoke(blink::mojom::IdentityCredentialRevokeOptionsPtr options, void Disconnect(blink::mojom::IdentityCredentialDisconnectOptionsPtr options,
RevokeCallback) override; DisconnectCallback) override;
// FederatedIdentityPermissionContextDelegate::IdpSigninStatusObserver: // FederatedIdentityPermissionContextDelegate::IdpSigninStatusObserver:
void OnIdpSigninStatusReceived(const url::Origin& idp_config_origin, void OnIdpSigninStatusReceived(const url::Origin& idp_config_origin,
@@ -348,8 +348,8 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
void SignInToIdP(GURL signin_url); void SignInToIdP(GURL signin_url);
void CompleteRevokeRequest(RevokeCallback callback, void CompleteDisconnectRequest(DisconnectCallback callback,
blink::mojom::RevokeStatus status); blink::mojom::DisconnectStatus status);
void RecordErrorMetrics( void RecordErrorMetrics(
blink::mojom::IdentityProviderRequestOptionsPtr idp, blink::mojom::IdentityProviderRequestOptionsPtr idp,
@@ -418,8 +418,8 @@ class CONTENT_EXPORT FederatedAuthRequestImpl
base::flat_set<std::unique_ptr<FederatedAuthUserInfoRequest>> base::flat_set<std::unique_ptr<FederatedAuthUserInfoRequest>>
user_info_requests_; user_info_requests_;
// Pending revoke request. // Pending disconnect request.
std::unique_ptr<FederatedAuthRevokeRequest> revoke_request_; std::unique_ptr<FederatedAuthDisconnectRequest> disconnect_request_;
// TODO(crbug.com/1361649): Refactor these member variables introduced through // TODO(crbug.com/1361649): Refactor these member variables introduced through
// the multi IDP prototype implementation to make them less confusing. // the multi IDP prototype implementation to make them less confusing.

@@ -89,7 +89,7 @@ constexpr char kClientMetadataEndpoint[] =
"https://idp.example/client_metadata"; "https://idp.example/client_metadata";
constexpr char kMetricsEndpoint[] = "https://idp.example/metrics"; constexpr char kMetricsEndpoint[] = "https://idp.example/metrics";
constexpr char kIdpLoginUrl[] = "https://idp.example/login_url"; constexpr char kIdpLoginUrl[] = "https://idp.example/login_url";
constexpr char kIdpRevokeUrl[] = "https://idp.example/revoke"; constexpr char kIdpDisconnectUrl[] = "https://idp.example/disconnect";
constexpr char kPrivacyPolicyUrl[] = "https://rp.example/pp"; constexpr char kPrivacyPolicyUrl[] = "https://rp.example/pp";
constexpr char kTermsOfServiceUrl[] = "https://rp.example/tos"; constexpr char kTermsOfServiceUrl[] = "https://rp.example/tos";
constexpr char kClientId[] = "client_id_123"; constexpr char kClientId[] = "client_id_123";
@@ -261,7 +261,7 @@ struct MockConfig {
std::string client_metadata_endpoint; std::string client_metadata_endpoint;
std::string metrics_endpoint; std::string metrics_endpoint;
std::string idp_login_url; std::string idp_login_url;
std::string revoke_endpoint; std::string disconnect_endpoint;
}; };
struct MockIdpInfo { struct MockIdpInfo {
@@ -338,7 +338,7 @@ static const MockIdpInfo kDefaultIdentityProviderInfo{
kClientMetadataEndpoint, kClientMetadataEndpoint,
kMetricsEndpoint, kMetricsEndpoint,
kIdpLoginUrl, kIdpLoginUrl,
kIdpRevokeUrl, kIdpDisconnectUrl,
}, },
kDefaultClientMetadata, kDefaultClientMetadata,
{ParseStatus::kSuccess, net::HTTP_OK}, {ParseStatus::kSuccess, net::HTTP_OK},
@@ -358,7 +358,7 @@ static const MockIdpInfo kProviderTwoInfo{
"https://idp2.example/client_metadata", "https://idp2.example/client_metadata",
"https://idp2.example/metrics", "https://idp2.example/metrics",
"https://idp2.example/login_url", "https://idp2.example/login_url",
"https://idp2.example/revoke", "https://idp2.example/disconnect",
}, },
kDefaultClientMetadata, kDefaultClientMetadata,
{ParseStatus::kSuccess, net::HTTP_OK}, {ParseStatus::kSuccess, net::HTTP_OK},
@@ -455,8 +455,8 @@ class TestIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
GURL(config_.idp_info[provider_key].config.client_metadata_endpoint); GURL(config_.idp_info[provider_key].config.client_metadata_endpoint);
endpoints.metrics = endpoints.metrics =
GURL(config_.idp_info[provider_key].config.metrics_endpoint); GURL(config_.idp_info[provider_key].config.metrics_endpoint);
endpoints.revoke = endpoints.disconnect =
GURL(config_.idp_info[provider_key].config.revoke_endpoint); GURL(config_.idp_info[provider_key].config.disconnect_endpoint);
IdentityProviderMetadata idp_metadata; IdentityProviderMetadata idp_metadata;
idp_metadata.config_url = provider; idp_metadata.config_url = provider;

@@ -64,8 +64,8 @@ bool IsFedCmErrorEnabled() {
return base::FeatureList::IsEnabled(features::kFedCmError); return base::FeatureList::IsEnabled(features::kFedCmError);
} }
bool IsFedCmRevokeEnabled() { bool IsFedCmDisconnectEnabled() {
return base::FeatureList::IsEnabled(features::kFedCmRevoke); return base::FeatureList::IsEnabled(features::kFedCmDisconnect);
} }
bool IsFedCmAddAccountEnabled() { bool IsFedCmAddAccountEnabled() {

@@ -47,8 +47,8 @@ bool IsFedCmDomainHintEnabled();
// Whether the Error API is enabled. // Whether the Error API is enabled.
bool IsFedCmErrorEnabled(); bool IsFedCmErrorEnabled();
// Whether the revoke API is enabled. // Whether the disconnect API is enabled.
bool IsFedCmRevokeEnabled(); bool IsFedCmDisconnectEnabled();
// Whether "Add Account" is enabled. // Whether "Add Account" is enabled.
bool IsFedCmAddAccountEnabled(); bool IsFedCmAddAccountEnabled();

@@ -68,7 +68,7 @@ constexpr char kProviderUrlListKey[] = "provider_urls";
constexpr char kIdAssertionEndpoint[] = "id_assertion_endpoint"; constexpr char kIdAssertionEndpoint[] = "id_assertion_endpoint";
constexpr char kClientMetadataEndpointKey[] = "client_metadata_endpoint"; constexpr char kClientMetadataEndpointKey[] = "client_metadata_endpoint";
constexpr char kMetricsEndpoint[] = "metrics_endpoint"; constexpr char kMetricsEndpoint[] = "metrics_endpoint";
constexpr char kRevocationEndpoint[] = "revocation_endpoint"; constexpr char kDisconnectEndpoint[] = "disconnect_endpoint";
constexpr char kSupportsAddAccountKey[] = "supports_add_account"; constexpr char kSupportsAddAccountKey[] = "supports_add_account";
// Shared between the well-known files and config files // Shared between the well-known files and config files
@@ -128,8 +128,8 @@ constexpr char kAccessDenied[] = "access_denied";
constexpr char kTemporarilyUnavailable[] = "temporarily_unavailable"; constexpr char kTemporarilyUnavailable[] = "temporarily_unavailable";
constexpr char kServerError[] = "server_error"; constexpr char kServerError[] = "server_error";
// Revoke response keys. // Disconnect response keys.
constexpr char kRevokeAccountId[] = "account_id"; constexpr char kDisconnectAccountId[] = "account_id";
// 1 MiB is an arbitrary upper bound that should account for any reasonable // 1 MiB is an arbitrary upper bound that should account for any reasonable
// response size that is a part of this protocol. // response size that is a part of this protocol.
@@ -520,7 +520,8 @@ void OnConfigParsed(const GURL& provider,
endpoints.client_metadata = endpoints.client_metadata =
ExtractEndpoint(provider, response, kClientMetadataEndpointKey); ExtractEndpoint(provider, response, kClientMetadataEndpointKey);
endpoints.metrics = ExtractEndpoint(provider, response, kMetricsEndpoint); endpoints.metrics = ExtractEndpoint(provider, response, kMetricsEndpoint);
endpoints.revoke = ExtractEndpoint(provider, response, kRevocationEndpoint); endpoints.disconnect =
ExtractEndpoint(provider, response, kDisconnectEndpoint);
const base::Value::Dict* idp_metadata_value = const base::Value::Dict* idp_metadata_value =
response.FindDict(kIdpBrandingKey); response.FindDict(kIdpBrandingKey);
@@ -773,16 +774,17 @@ void OnLogoutCompleted(IdpNetworkRequestManager::LogoutCallback callback,
std::move(callback).Run(); std::move(callback).Run();
} }
void OnRevokeResponseParsed(IdpNetworkRequestManager::RevokeCallback callback, void OnDisconnectResponseParsed(
FetchStatus fetch_status, IdpNetworkRequestManager::DisconnectCallback callback,
data_decoder::DataDecoder::ValueOrError result) { FetchStatus fetch_status,
data_decoder::DataDecoder::ValueOrError result) {
if (fetch_status.parse_status != ParseStatus::kSuccess) { if (fetch_status.parse_status != ParseStatus::kSuccess) {
std::move(callback).Run(fetch_status, /*account_id=*/""); std::move(callback).Run(fetch_status, /*account_id=*/"");
return; return;
} }
const base::Value::Dict& response = result->GetDict(); const base::Value::Dict& response = result->GetDict();
const std::string* account_id = response.FindString(kRevokeAccountId); const std::string* account_id = response.FindString(kDisconnectAccountId);
if (account_id && !account_id->empty()) { if (account_id && !account_id->empty()) {
std::move(callback).Run(fetch_status, *account_id); std::move(callback).Run(fetch_status, *account_id);
@@ -1002,18 +1004,18 @@ void IdpNetworkRequestManager::SendLogout(const GURL& logout_url,
maxResponseSizeInKiB * 1024); maxResponseSizeInKiB * 1024);
} }
void IdpNetworkRequestManager::SendRevokeRequest( void IdpNetworkRequestManager::SendDisconnectRequest(
const GURL& revoke_url, const GURL& disconnect_url,
const std::string& account_hint, const std::string& account_hint,
const std::string& client_id, const std::string& client_id,
RevokeCallback callback) { DisconnectCallback callback) {
auto resource_request = CreateCredentialedResourceRequest( auto resource_request = CreateCredentialedResourceRequest(
revoke_url, CredentialedResourceRequestType::kOriginWithCORS); disconnect_url, CredentialedResourceRequestType::kOriginWithCORS);
std::string url_encoded_post_data = std::string url_encoded_post_data =
"client_id=" + client_id + "&account_hint=" + account_hint; "client_id=" + client_id + "&account_hint=" + account_hint;
DownloadJsonAndParse( DownloadJsonAndParse(
std::move(resource_request), url_encoded_post_data, std::move(resource_request), url_encoded_post_data,
base::BindOnce(&OnRevokeResponseParsed, std::move(callback)), base::BindOnce(&OnDisconnectResponseParsed, std::move(callback)),
maxResponseSizeInKiB * 1024); maxResponseSizeInKiB * 1024);
} }

@@ -110,7 +110,7 @@ class CONTENT_EXPORT IdpNetworkRequestManager {
GURL accounts; GURL accounts;
GURL client_metadata; GURL client_metadata;
GURL metrics; GURL metrics;
GURL revoke; GURL disconnect;
}; };
struct CONTENT_EXPORT WellKnown { struct CONTENT_EXPORT WellKnown {
@@ -156,7 +156,7 @@ class CONTENT_EXPORT IdpNetworkRequestManager {
kTokenEndpointInvalidResponse = 402, kTokenEndpointInvalidResponse = 402,
}; };
enum class RevokeResponse { enum class DisconnectResponse {
kSuccess, kSuccess,
kError, kError,
}; };
@@ -223,7 +223,7 @@ class CONTENT_EXPORT IdpNetworkRequestManager {
using ParseJsonCallback = using ParseJsonCallback =
base::OnceCallback<void(FetchStatus, base::OnceCallback<void(FetchStatus,
data_decoder::DataDecoder::ValueOrError)>; data_decoder::DataDecoder::ValueOrError)>;
using RevokeCallback = using DisconnectCallback =
base::OnceCallback<void(FetchStatus, const std::string&)>; base::OnceCallback<void(FetchStatus, const std::string&)>;
using TokenRequestCallback = using TokenRequestCallback =
base::OnceCallback<void(FetchStatus, TokenResult)>; base::OnceCallback<void(FetchStatus, TokenResult)>;
@@ -294,11 +294,11 @@ class CONTENT_EXPORT IdpNetworkRequestManager {
// Send logout request to a single target. // Send logout request to a single target.
virtual void SendLogout(const GURL& logout_url, LogoutCallback); virtual void SendLogout(const GURL& logout_url, LogoutCallback);
// Send a revoke request to the IDP. // Send a disconnect request to the IDP.
virtual void SendRevokeRequest(const GURL& revoke_url, virtual void SendDisconnectRequest(const GURL& disconnect_url,
const std::string& account_hint, const std::string& account_hint,
const std::string& client_id, const std::string& client_id,
RevokeCallback callback); DisconnectCallback callback);
private: private:
// Starts download request using `url_loader`. Calls `parse_json_callback` // Starts download request using `url_loader`. Calls `parse_json_callback`

@@ -66,7 +66,8 @@ constexpr char kTestAccountsEndpoint[] = "https://idp.test/accounts_endpoint";
constexpr char kTestTokenEndpoint[] = "https://idp.test/token_endpoint"; constexpr char kTestTokenEndpoint[] = "https://idp.test/token_endpoint";
constexpr char kTestClientMetadataEndpoint[] = constexpr char kTestClientMetadataEndpoint[] =
"https://idp.test/client_metadata_endpoint"; "https://idp.test/client_metadata_endpoint";
constexpr char kTestRevokeEndpoint[] = "https://idp.test/revocation_endpoint"; constexpr char kTestDisconnectEndpoint[] =
"https://idp.test/revocation_endpoint";
constexpr char kSingleAccountEndpointValidJson[] = R"({ constexpr char kSingleAccountEndpointValidJson[] = R"({
"accounts" : [ "accounts" : [
@@ -1568,15 +1569,15 @@ TEST_F(IdpNetworkRequestManagerTest, IdAssertionResponseWithTokenAndHttpError) {
EXPECT_FALSE(error_url_type()); EXPECT_FALSE(error_url_type());
} }
TEST_F(IdpNetworkRequestManagerTest, RevokeRequest) { TEST_F(IdpNetworkRequestManagerTest, DisconnectRequest) {
base::test::ScopedFeatureList list; base::test::ScopedFeatureList list;
list.InitAndEnableFeature(features::kFedCmRevoke); list.InitAndEnableFeature(features::kFedCmDisconnect);
bool called = false; bool called = false;
auto interceptor = auto interceptor =
base::BindLambdaForTesting([&](const network::ResourceRequest& request) { base::BindLambdaForTesting([&](const network::ResourceRequest& request) {
called = true; called = true;
EXPECT_EQ(GURL(kTestRevokeEndpoint), request.url); EXPECT_EQ(GURL(kTestDisconnectEndpoint), request.url);
EXPECT_FALSE(request.referrer.is_valid()); EXPECT_FALSE(request.referrer.is_valid());
url::Origin rpOrigin = url::Origin::Create(GURL(kTestRpUrl)); url::Origin rpOrigin = url::Origin::Create(GURL(kTestRpUrl));
EXPECT_EQ(GetOriginHeader(request), rpOrigin); EXPECT_EQ(GetOriginHeader(request), rpOrigin);
@@ -1596,34 +1597,34 @@ TEST_F(IdpNetworkRequestManagerTest, RevokeRequest) {
}); });
test_url_loader_factory().SetInterceptor(interceptor); test_url_loader_factory().SetInterceptor(interceptor);
const char test_revoke_json[] = R"({ const char test_disconnect_json[] = R"({
"account_id" : "accountId" "account_id" : "accountId"
})"; })";
GURL revoke_endpoint(kTestRevokeEndpoint); GURL disconnect_endpoint(kTestDisconnectEndpoint);
AddResponse(revoke_endpoint, net::HTTP_OK, "application/json", AddResponse(disconnect_endpoint, net::HTTP_OK, "application/json",
test_revoke_json); test_disconnect_json);
base::RunLoop run_loop; base::RunLoop run_loop;
FetchStatus revoke_response; FetchStatus disconnect_response;
absl::optional<std::string> revoke_account_id; absl::optional<std::string> disconnect_account_id;
auto callback = base::BindLambdaForTesting( auto callback = base::BindLambdaForTesting(
[&](FetchStatus response, const std::string& account_id) { [&](FetchStatus response, const std::string& account_id) {
revoke_response = response; disconnect_response = response;
revoke_account_id = account_id; disconnect_account_id = account_id;
run_loop.Quit(); run_loop.Quit();
}); });
std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager(); std::unique_ptr<IdpNetworkRequestManager> manager = CreateTestManager();
manager->SendRevokeRequest(revoke_endpoint, "hint", "clientId", manager->SendDisconnectRequest(disconnect_endpoint, "hint", "clientId",
std::move(callback)); std::move(callback));
run_loop.Run(); run_loop.Run();
EXPECT_TRUE(called); EXPECT_TRUE(called);
EXPECT_EQ(ParseStatus::kSuccess, revoke_response.parse_status); EXPECT_EQ(ParseStatus::kSuccess, disconnect_response.parse_status);
EXPECT_EQ(net::HTTP_OK, revoke_response.response_code); EXPECT_EQ(net::HTTP_OK, disconnect_response.response_code);
ASSERT_TRUE(revoke_account_id.has_value()); ASSERT_TRUE(disconnect_account_id.has_value());
EXPECT_EQ(*revoke_account_id, "accountId"); EXPECT_EQ(*disconnect_account_id, "accountId");
} }
} // namespace } // namespace

@@ -79,13 +79,13 @@ void DelegatedIdpNetworkRequestManager::SendLogout(const GURL& logout_url,
delegate_->SendLogout(logout_url, std::move(callback)); delegate_->SendLogout(logout_url, std::move(callback));
} }
void DelegatedIdpNetworkRequestManager::SendRevokeRequest( void DelegatedIdpNetworkRequestManager::SendDisconnectRequest(
const GURL& revoke_url, const GURL& disconnect_url,
const std::string& account_hint, const std::string& account_hint,
const std::string& client_id, const std::string& client_id,
RevokeCallback callback) { DisconnectCallback callback) {
delegate_->SendRevokeRequest(revoke_url, account_hint, client_id, delegate_->SendDisconnectRequest(disconnect_url, account_hint, client_id,
std::move(callback)); std::move(callback));
} }
} // namespace content } // namespace content

@@ -53,10 +53,10 @@ class DelegatedIdpNetworkRequestManager : public MockIdpNetworkRequestManager {
const GURL& metrics_endpoint_url, const GURL& metrics_endpoint_url,
MetricsEndpointErrorCode error_code) override; MetricsEndpointErrorCode error_code) override;
void SendLogout(const GURL& logout_url, LogoutCallback callback) override; void SendLogout(const GURL& logout_url, LogoutCallback callback) override;
void SendRevokeRequest(const GURL& revoke_url, void SendDisconnectRequest(const GURL& disconnect_url,
const std::string& account_hint, const std::string& account_hint,
const std::string& client_id, const std::string& client_id,
RevokeCallback callback) override; DisconnectCallback callback) override;
private: private:
raw_ptr<IdpNetworkRequestManager, DanglingUntriaged> delegate_; raw_ptr<IdpNetworkRequestManager, DanglingUntriaged> delegate_;

@@ -239,7 +239,7 @@ void SetRuntimeFeaturesFromChromiumFeatures() {
raw_ref(blink::features::kSharedStorageAPIM118), kSetOnlyIfOverridden}, raw_ref(blink::features::kSharedStorageAPIM118), kSetOnlyIfOverridden},
{wf::EnableFedCmMultipleIdentityProviders, {wf::EnableFedCmMultipleIdentityProviders,
raw_ref(features::kFedCmMultipleIdentityProviders), kDefault}, raw_ref(features::kFedCmMultipleIdentityProviders), kDefault},
{wf::EnableFedCmRevoke, raw_ref(features::kFedCmRevoke), {wf::EnableFedCmDisconnect, raw_ref(features::kFedCmDisconnect),
kSetOnlyIfOverridden}, kSetOnlyIfOverridden},
{wf::EnableFedCmSelectiveDisclosure, {wf::EnableFedCmSelectiveDisclosure,
raw_ref(features::kFedCmSelectiveDisclosure), kDefault}, raw_ref(features::kFedCmSelectiveDisclosure), kDefault},

@@ -406,8 +406,10 @@ BASE_FEATURE(kFedCmMultipleIdentityProviders,
"FedCmMultipleIdentityProviders", "FedCmMultipleIdentityProviders",
base::FEATURE_DISABLED_BY_DEFAULT); base::FEATURE_DISABLED_BY_DEFAULT);
// Enables the revoke method within the FedCM API. // Enables the disconnect method within the FedCM API.
BASE_FEATURE(kFedCmRevoke, "FedCmRevoke", base::FEATURE_ENABLED_BY_DEFAULT); BASE_FEATURE(kFedCmDisconnect,
"FedCmDisconnect",
base::FEATURE_ENABLED_BY_DEFAULT);
// Enables usage of the FedCM API with the Selective Disclosure API at the same // Enables usage of the FedCM API with the Selective Disclosure API at the same
// time. // time.

@@ -101,7 +101,7 @@ CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIdPRegistration);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIdpSigninStatusEnabled); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmIdpSigninStatusEnabled);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMetricsEndpoint); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMetricsEndpoint);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMultipleIdentityProviders); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmMultipleIdentityProviders);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmRevoke); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmDisconnect);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmSelectiveDisclosure); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmSelectiveDisclosure);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmWithoutWellKnownEnforcement); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFedCmWithoutWellKnownEnforcement);
CONTENT_EXPORT BASE_DECLARE_FEATURE(kFencedFramesEnforceFocus); CONTENT_EXPORT BASE_DECLARE_FEATURE(kFencedFramesEnforceFocus);

@@ -2719,10 +2719,10 @@ test("content_unittests") {
"../browser/web_package/signed_exchange_signature_header_field_unittest.cc", "../browser/web_package/signed_exchange_signature_header_field_unittest.cc",
"../browser/web_package/signed_exchange_signature_verifier_unittest.cc", "../browser/web_package/signed_exchange_signature_verifier_unittest.cc",
"../browser/web_package/signed_exchange_utils_unittest.cc", "../browser/web_package/signed_exchange_utils_unittest.cc",
"../browser/webid/federated_auth_disconnect_request_unittest.cc",
"../browser/webid/federated_auth_request_impl_multiple_frames_unittest.cc", "../browser/webid/federated_auth_request_impl_multiple_frames_unittest.cc",
"../browser/webid/federated_auth_request_impl_registry_unittest.cc", "../browser/webid/federated_auth_request_impl_registry_unittest.cc",
"../browser/webid/federated_auth_request_impl_unittest.cc", "../browser/webid/federated_auth_request_impl_unittest.cc",
"../browser/webid/federated_auth_revoke_request_unittest.cc",
"../browser/webid/federated_auth_user_info_request_unittest.cc", "../browser/webid/federated_auth_user_info_request_unittest.cc",
"../browser/webid/federated_provider_fetcher_unittest.cc", "../browser/webid/federated_provider_fetcher_unittest.cc",
"../browser/webid/identity_registry_unittest.cc", "../browser/webid/identity_registry_unittest.cc",

@@ -3547,7 +3547,7 @@ enum WebFeature {
kOBSOLETE_GestureScrollUpdate = 4221, kOBSOLETE_GestureScrollUpdate = 4221,
kOBSOLETE_GestureScrollEnd = 4222, kOBSOLETE_GestureScrollEnd = 4222,
kArrayBufferTooBigForWebAPI = 4223, kArrayBufferTooBigForWebAPI = 4223,
kFedCmRevoke = 4224, kFedCmDisconnect = 4224,
kOBSOLETE_FedCmLogout = 4225, kOBSOLETE_FedCmLogout = 4225,
kOBSOLETE_FedCmLogoutRps = 4226, kOBSOLETE_FedCmLogoutRps = 4226,
kV8Navigator_DeprecatedReplaceInURN_Method = 4227, kV8Navigator_DeprecatedReplaceInURN_Method = 4227,

@@ -33,10 +33,10 @@ enum RequestUserInfoStatus {
kError, kError,
}; };
// Represents the fetch result from a IdentityProvider.revoke() request. // Represents the fetch result from a IdentityProvider.disconnect() request.
// It is used to determine whether a JavaScript exception should be // It is used to determine whether a JavaScript exception should be
// thrown, and what the error message of such exception should say. // thrown, and what the error message of such exception should say.
enum RevokeStatus { enum DisconnectStatus {
kSuccess, kSuccess,
kErrorTooManyRequests, kErrorTooManyRequests,
kError kError
@@ -135,11 +135,11 @@ struct IdentityProviderRequestOptions {
map<string, string> params; map<string, string> params;
}; };
// The information passed in an IdentityProvider.revoke() call. // The information passed in an IdentityProvider.disconnect() call.
struct IdentityCredentialRevokeOptions { struct IdentityCredentialDisconnectOptions {
IdentityProviderConfig config; IdentityProviderConfig config;
// The account hint for which the revocation ought to happen. // The account hint for which the disconnect ought to happen.
string account_hint; string account_hint;
}; };
@@ -228,7 +228,8 @@ interface FederatedAuthRequest {
// CredentialTypes and avoid the duplication eventually. // CredentialTypes and avoid the duplication eventually.
PreventSilentAccess() => (); PreventSilentAccess() => ();
// Revokes the sharing permission for the specified |account_id| from // Disconnects the sharing permission for the specified |account_id| from
// |provider| for the RP identified by |client_id|. // |provider| for the RP identified by |client_id|.
Revoke(IdentityCredentialRevokeOptions options) => (RevokeStatus status); Disconnect(IdentityCredentialDisconnectOptions options) =>
(DisconnectStatus status);
}; };

@@ -612,8 +612,8 @@ generated_dictionary_sources_in_modules = [
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_logout_r_ps_request.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_logout_r_ps_request.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_revoke_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_disconnect_options.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_revoke_options.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_disconnect_options.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.cc",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.h", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.h",
"$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_request_options.cc", "$root_gen_dir/third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_request_options.cc",

@@ -28,9 +28,9 @@
#include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_field_requirement.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_field_requirement.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_provider.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_provider.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_selector.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_digital_credential_selector.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_disconnect_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options_context.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options_context.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options_mode.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_request_options_mode.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_revoke_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_config.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_request_options.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_provider_request_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_user_info.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_user_info.h"
@@ -74,8 +74,8 @@ using blink::mojom::blink::DigitalCredentialProvider;
using blink::mojom::blink::DigitalCredentialProviderPtr; using blink::mojom::blink::DigitalCredentialProviderPtr;
using blink::mojom::blink::DigitalCredentialSelector; using blink::mojom::blink::DigitalCredentialSelector;
using blink::mojom::blink::DigitalCredentialSelectorPtr; using blink::mojom::blink::DigitalCredentialSelectorPtr;
using blink::mojom::blink::IdentityCredentialRevokeOptions; using blink::mojom::blink::IdentityCredentialDisconnectOptions;
using blink::mojom::blink::IdentityCredentialRevokeOptionsPtr; using blink::mojom::blink::IdentityCredentialDisconnectOptionsPtr;
using blink::mojom::blink::IdentityProvider; using blink::mojom::blink::IdentityProvider;
using blink::mojom::blink::IdentityProviderConfig; using blink::mojom::blink::IdentityProviderConfig;
using blink::mojom::blink::IdentityProviderConfigPtr; using blink::mojom::blink::IdentityProviderConfigPtr;
@@ -1015,18 +1015,19 @@ TypeConverter<Vector<PRFValuesPtr>, blink::AuthenticationExtensionsPRFInputs>::
} }
// static // static
IdentityCredentialRevokeOptionsPtr IdentityCredentialDisconnectOptionsPtr
TypeConverter<IdentityCredentialRevokeOptionsPtr, TypeConverter<IdentityCredentialDisconnectOptionsPtr,
blink::IdentityCredentialRevokeOptions>:: blink::IdentityCredentialDisconnectOptions>::
Convert(const blink::IdentityCredentialRevokeOptions& options) { Convert(const blink::IdentityCredentialDisconnectOptions& options) {
auto mojo_revoke_options = IdentityCredentialRevokeOptions::New(); auto mojo_disconnect_options = IdentityCredentialDisconnectOptions::New();
mojo_revoke_options->config = IdentityProviderConfig::New(); mojo_disconnect_options->config = IdentityProviderConfig::New();
mojo_revoke_options->config->config_url = blink::KURL(options.configURL()); mojo_disconnect_options->config->config_url =
mojo_revoke_options->config->client_id = options.clientId(); blink::KURL(options.configURL());
mojo_disconnect_options->config->client_id = options.clientId();
mojo_revoke_options->account_hint = options.accountHint(); mojo_disconnect_options->account_hint = options.accountHint();
return mojo_revoke_options; return mojo_disconnect_options;
} }
} // namespace mojo } // namespace mojo

@@ -24,7 +24,7 @@ class AuthenticationExtensionsPRFValues;
class AuthenticatorSelectionCriteria; class AuthenticatorSelectionCriteria;
class CableAuthenticationData; class CableAuthenticationData;
class Credential; class Credential;
class IdentityCredentialRevokeOptions; class IdentityCredentialDisconnectOptions;
class IdentityProviderConfig; class IdentityProviderConfig;
class IdentityProviderRequestOptions; class IdentityProviderRequestOptions;
class IdentityUserInfo; class IdentityUserInfo;
@@ -269,10 +269,11 @@ struct TypeConverter<Vector<blink::mojom::blink::PRFValuesPtr>,
}; };
template <> template <>
struct TypeConverter<blink::mojom::blink::IdentityCredentialRevokeOptionsPtr, struct TypeConverter<
blink::IdentityCredentialRevokeOptions> { blink::mojom::blink::IdentityCredentialDisconnectOptionsPtr,
static blink::mojom::blink::IdentityCredentialRevokeOptionsPtr Convert( blink::IdentityCredentialDisconnectOptions> {
const blink::IdentityCredentialRevokeOptions&); static blink::mojom::blink::IdentityCredentialDisconnectOptionsPtr Convert(
const blink::IdentityCredentialDisconnectOptions&);
}; };
} // namespace mojo } // namespace mojo

@@ -17,8 +17,8 @@
namespace blink { namespace blink {
namespace { namespace {
using mojom::blink::DisconnectStatus;
using mojom::blink::RequestTokenStatus; using mojom::blink::RequestTokenStatus;
using mojom::blink::RevokeStatus;
constexpr char kIdentityCredentialType[] = "identity"; constexpr char kIdentityCredentialType[] = "identity";
@@ -31,10 +31,10 @@ enum class FedCmCspStatus {
kMaxValue = kFailedOrigin kMaxValue = kFailedOrigin
}; };
void OnRevoke(ScriptPromiseResolver* resolver, RevokeStatus status) { void OnDisconnect(ScriptPromiseResolver* resolver, DisconnectStatus status) {
if (status != RevokeStatus::kSuccess) { if (status != DisconnectStatus::kSuccess) {
resolver->Reject(MakeGarbageCollected<DOMException>( resolver->Reject(MakeGarbageCollected<DOMException>(
DOMExceptionCode::kNetworkError, "Error revoking account.")); DOMExceptionCode::kNetworkError, "Error disconnecting account."));
return; return;
} }
resolver->Resolve(); resolver->Resolve();
@@ -96,9 +96,9 @@ bool IdentityCredential::IsIdentityCredential() const {
} }
// static // static
ScriptPromise IdentityCredential::revoke( ScriptPromise IdentityCredential::disconnect(
ScriptState* script_state, ScriptState* script_state,
const blink::IdentityCredentialRevokeOptions* options, const blink::IdentityCredentialDisconnectOptions* options,
ExceptionState& exception_state) { ExceptionState& exception_state) {
auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state); auto* resolver = MakeGarbageCollected<ScriptPromiseResolver>(script_state);
ScriptPromise promise = resolver->Promise(); ScriptPromise promise = resolver->Promise();
@@ -141,10 +141,11 @@ ScriptPromise IdentityCredential::revoke(
return promise; return promise;
} }
mojom::blink::IdentityCredentialRevokeOptionsPtr revoke_options = mojom::blink::IdentityCredentialDisconnectOptionsPtr disconnect_options =
blink::mojom::blink::IdentityCredentialRevokeOptions::From(*options); blink::mojom::blink::IdentityCredentialDisconnectOptions::From(*options);
auth_request->Revoke(std::move(revoke_options), auth_request->Disconnect(
WTF::BindOnce(&OnRevoke, WrapPersistent(resolver))); std::move(disconnect_options),
WTF::BindOnce(&OnDisconnect, WrapPersistent(resolver)));
return promise; return promise;
} }

@@ -6,8 +6,8 @@
#define THIRD_PARTY_BLINK_RENDERER_MODULES_CREDENTIALMANAGEMENT_IDENTITY_CREDENTIAL_H_ #define THIRD_PARTY_BLINK_RENDERER_MODULES_CREDENTIALMANAGEMENT_IDENTITY_CREDENTIAL_H_
#include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h" #include "third_party/blink/renderer/bindings/core/v8/script_promise_resolver.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_disconnect_options.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_logout_r_ps_request.h" #include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_logout_r_ps_request.h"
#include "third_party/blink/renderer/bindings/modules/v8/v8_identity_credential_revoke_options.h"
#include "third_party/blink/renderer/modules/credentialmanagement/credential.h" #include "third_party/blink/renderer/modules/credentialmanagement/credential.h"
#include "third_party/blink/renderer/modules/modules_export.h" #include "third_party/blink/renderer/modules/modules_export.h"
#include "third_party/blink/renderer/platform/bindings/script_wrappable.h" #include "third_party/blink/renderer/platform/bindings/script_wrappable.h"
@@ -36,9 +36,10 @@ class MODULES_EXPORT IdentityCredential final : public Credential {
const String& token() const { return token_; } const String& token() const { return token_; }
const bool& isAutoSelected() const { return is_auto_selected_; } const bool& isAutoSelected() const { return is_auto_selected_; }
static ScriptPromise revoke(ScriptState*, static ScriptPromise disconnect(
const IdentityCredentialRevokeOptions* options, ScriptState*,
ExceptionState&); const IdentityCredentialDisconnectOptions* options,
ExceptionState&);
private: private:
const String token_; const String token_;

@@ -4,7 +4,7 @@
// https://fedidcg.github.io/FedCM/#browser-api-identity-credential-interface // https://fedidcg.github.io/FedCM/#browser-api-identity-credential-interface
dictionary IdentityCredentialRevokeOptions : IdentityProviderConfig { dictionary IdentityCredentialDisconnectOptions : IdentityProviderConfig {
USVString accountHint; USVString accountHint;
}; };
@@ -20,7 +20,7 @@ dictionary IdentityCredentialRevokeOptions : IdentityProviderConfig {
[RuntimeEnabled=FedCmAutoSelectedFlag] [RuntimeEnabled=FedCmAutoSelectedFlag]
readonly attribute boolean isAutoSelected; readonly attribute boolean isAutoSelected;
// https://fedidcg.github.io/FedCM/#browser-api-revocation // https://github.com/fedidcg/FedCM/pull/515
[RuntimeEnabled=FedCmRevoke, CallWith=ScriptState, RaisesException, MeasureAs=FedCmRevoke] static Promise<void> revoke(optional IdentityCredentialRevokeOptions options = {}); [RuntimeEnabled=FedCmDisconnect, CallWith=ScriptState, RaisesException, MeasureAs=FedCmDisconnect] static Promise<void> disconnect(optional IdentityCredentialDisconnectOptions options = {});
}; };

@@ -1593,6 +1593,13 @@
status: "stable", status: "stable",
base_feature: "none", base_feature: "none",
}, },
{
name: "FedCmDisconnect",
depends_on: ["FedCm"],
base_feature: "none",
status: "test",
public: true,
},
{ {
name: "FedCmDomainHint", name: "FedCmDomainHint",
depends_on: ["FedCm"], depends_on: ["FedCm"],
@@ -1628,13 +1635,6 @@
base_feature: "none", base_feature: "none",
public: true, public: true,
}, },
{
name: "FedCmRevoke",
depends_on: ["FedCm"],
base_feature: "none",
status: "test",
public: true,
},
{ {
name: "FedCmSelectiveDisclosure", name: "FedCmSelectiveDisclosure",
depends_on: ["FedCm"], depends_on: ["FedCm"],

@@ -97,8 +97,8 @@ crbug.com/1499775 external/wpt/credential-management/fedcm-nonce-is-optional.htt
crbug.com/1499775 external/wpt/credential-management/fedcm-not-observed-by-service-worker.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-not-observed-by-service-worker.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-pending-call-rejected.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-pending-call-rejected.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-returning-account-auto-reauthn.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-returning-account-auto-reauthn.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-revoke.sub.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-disconnect.sub.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-revoke-iframe.sub.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-disconnect-iframe.sub.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-store.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-store.https.html [ Timeout ]
crbug.com/1499775 external/wpt/credential-management/fedcm-token-returned-with-http-error.https.html [ Timeout ] crbug.com/1499775 external/wpt/credential-management/fedcm-token-returned-with-http-error.https.html [ Timeout ]
crbug.com/1499775 external/wpt/css/CSS2/floats/float-nowrap-2.html [ Failure ] # Reftest image failure crbug.com/1499775 external/wpt/css/CSS2/floats/float-nowrap-2.html [ Failure ] # Reftest image failure

@@ -7103,7 +7103,7 @@ crbug.com/1502336 [ Linux ] external/wpt/idle-detection/idle-detection-allowed-b
crbug.com/1502794 external/wpt/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html [ Failure Pass Timeout ] crbug.com/1502794 external/wpt/html/semantics/invokers/invoketarget-on-video-behavior.tentative.html [ Failure Pass Timeout ]
# Gerdener 2023-11-17 # Gerdener 2023-11-17
crbug.com/1503108 [ Mac ] external/wpt/credential-management/fedcm-revoke.sub.https.html [ Timeout ] crbug.com/1503108 [ Mac ] external/wpt/credential-management/fedcm-disconnect.sub.https.html [ Timeout ]
crbug.com/1503051 [ Mac ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/audioworklet.https.sub.html [ Failure Pass ] crbug.com/1503051 [ Mac ] virtual/keepalive-in-browser-migration/external/wpt/fetch/metadata/generated/audioworklet.https.sub.html [ Failure Pass ]
crbug.com/1503067 [ Linux ] virtual/plz-dedicated-worker-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window.html [ Failure Pass ] crbug.com/1503067 [ Linux ] virtual/plz-dedicated-worker-disabled/external/wpt/fetch/private-network-access/window-open.tentative.https.window.html [ Failure Pass ]
crbug.com/1503067 [ Linux ] virtual/pna-iframes-warning/external/wpt/fetch/private-network-access/window-open.tentative.https.window.html [ Failure Pass ] crbug.com/1503067 [ Linux ] virtual/pna-iframes-warning/external/wpt/fetch/private-network-access/window-open.tentative.https.window.html [ Failure Pass ]

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<title>Federated Credential Management API revoke() tests in iframes.</title> <title>Federated Credential Management API disconnect() tests in iframes.</title>
<link rel="help" href="https://fedidcg.github.io/FedCM"> <link rel="help" href="https://fedidcg.github.io/FedCM">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
@@ -27,21 +27,21 @@ async function createIframeAndWaitForMessage(test, iframeUrl, allow = false) {
} }
fedcm_test(async t => { fedcm_test(async t => {
const message = await createIframeAndWaitForMessage(t, 'support/fedcm/revoke-iframe.html'); const message = await createIframeAndWaitForMessage(t, 'support/fedcm/disconnect-iframe.html');
assert_equals(message.result, "Pass"); assert_equals(message.result, "Pass");
}, 'Same-origin iframe does not need explicit identity-credentials-get'); }, 'Same-origin iframe does not need explicit identity-credentials-get');
fedcm_test(async t => { fedcm_test(async t => {
const message = await createIframeAndWaitForMessage(t, const message = await createIframeAndWaitForMessage(t,
'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/revoke-iframe.html?skip_get'); 'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/disconnect-iframe.html?skip_get');
assert_equals(message.result, "Failed revoke"); assert_equals(message.result, "Failed disconnect");
assert_equals(message.errorType, "NotAllowedError"); assert_equals(message.errorType, "NotAllowedError");
}, 'Cross-origin iframe fails revoke() without explicit identity-credentials-get'); }, 'Cross-origin iframe fails disconnect() without explicit identity-credentials-get');
fedcm_test(async t => { fedcm_test(async t => {
const message = await createIframeAndWaitForMessage(t, const message = await createIframeAndWaitForMessage(t,
'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/revoke-iframe.html', 'https://{{hosts[alt][]}}:{{ports[https][0]}}/credential-management/support/fedcm/disconnect-iframe.html',
/*allow=*/true); /*allow=*/true);
assert_equals(message.result, "Pass"); assert_equals(message.result, "Pass");
}, 'Cross-origin iframe can revoke with explicit identity-credentials-get'); }, 'Cross-origin iframe can disconnect with explicit identity-credentials-get');
</script> </script>

@@ -1,5 +1,5 @@
<!DOCTYPE html> <!DOCTYPE html>
<title>Federated Credential Management API revoke() tests.</title> <title>Federated Credential Management API disconnect() tests.</title>
<link rel="help" href="https://fedidcg.github.io/FedCM"> <link rel="help" href="https://fedidcg.github.io/FedCM">
<script src="/resources/testharness.js"></script> <script src="/resources/testharness.js"></script>
<script src="/resources/testharnessreport.js"></script> <script src="/resources/testharnessreport.js"></script>
@@ -12,18 +12,18 @@
import {fedcm_test, import {fedcm_test,
mark_signed_in, mark_signed_in,
set_fedcm_cookie, set_fedcm_cookie,
revoke_options, disconnect_options,
fedcm_get_and_select_first_account, fedcm_get_and_select_first_account,
request_options_with_mediation_required, request_options_with_mediation_required,
alt_manifest_origin, alt_manifest_origin,
alt_request_options_with_mediation_required, alt_request_options_with_mediation_required,
alt_revoke_options, alt_disconnect_options,
set_alt_fedcm_cookie} from './support/fedcm-helper.sub.js'; set_alt_fedcm_cookie} from './support/fedcm-helper.sub.js';
fedcm_test(async t => { fedcm_test(async t => {
await mark_signed_in(); await mark_signed_in();
await set_fedcm_cookie(); await set_fedcm_cookie();
// Get at least one connected account that can be revoked. // Get at least one connected account that can be disconnected.
const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required()); const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required());
// The IDP implementation will accept any account hint, so this is really testing that the user // The IDP implementation will accept any account hint, so this is really testing that the user
// agent eventually stops sending the requests to the IDP. // agent eventually stops sending the requests to the IDP.
@@ -32,50 +32,53 @@ fedcm_test(async t => {
return new Promise(async resolve => { return new Promise(async resolve => {
while (true) { while (true) {
try { try {
await IdentityCredential.revoke(revoke_options("1234")); await IdentityCredential.disconnect(disconnect_options("1234"));
} catch(e) { } catch(e) {
resolve(); resolve();
break; break;
} }
} }
}); });
}, "Repeatedly calling revoke should eventually fail"); }, "Repeatedly calling disconnect should eventually fail");
fedcm_test(async t => { fedcm_test(async t => {
const revoke = IdentityCredential.revoke(revoke_options("nonExistent")); const disconnect = IdentityCredential.disconnect(
return promise_rejects_dom(t, 'NetworkError', revoke); disconnect_options("nonExistent"));
}, 'Test that revoke fails when there is no account to revoke'); return promise_rejects_dom(t, 'NetworkError', disconnect);
}, 'Test that disconnect fails when there is no account to disconnect');
fedcm_test(async t => { fedcm_test(async t => {
const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required()); const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required());
return IdentityCredential.revoke(revoke_options("1234")); return IdentityCredential.disconnect(disconnect_options("1234"));
}, 'Test that revoke succeeds when there is an account to revoke'); }, 'Test that disconnect succeeds when there is an account to disconnect');
fedcm_test(async t => { fedcm_test(async t => {
const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required()); const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required());
await IdentityCredential.revoke(revoke_options("1234")); await IdentityCredential.disconnect(disconnect_options("1234"));
const revoke = IdentityCredential.revoke(revoke_options("1234")); const disconnect = IdentityCredential.disconnect(disconnect_options("1234"));
return promise_rejects_dom(t, 'NetworkError', revoke); return promise_rejects_dom(t, 'NetworkError', disconnect);
}, 'Test that revoking the same account twice results in failure.'); }, 'Test that disconnecting the same account twice results in failure.');
fedcm_test(async t => { fedcm_test(async t => {
const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required()); const cred = await fedcm_get_and_select_first_account(t, request_options_with_mediation_required());
// A connected account is guaranteed by the above, and IDP accepts any account hint, so this tests // A connected account is guaranteed by the above, and IDP accepts any account hint, so this tests
// that the user agent allows the request to go through to the IDP. // that the user agent allows the request to go through to the IDP.
return IdentityCredential.revoke(revoke_options("noMatch")); return IdentityCredential.disconnect(disconnect_options("noMatch"));
}, 'Revoke passing an incorrect ID can still succeed'); }, 'Disconnect passing an incorrect ID can still succeed');
fedcm_test(async t => { fedcm_test(async t => {
await set_alt_fedcm_cookie(); await set_alt_fedcm_cookie();
await mark_signed_in(alt_manifest_origin); await mark_signed_in(alt_manifest_origin);
await fedcm_get_and_select_first_account(t, alt_request_options_with_mediation_required()); await fedcm_get_and_select_first_account(t, alt_request_options_with_mediation_required());
await fedcm_get_and_select_first_account(t, request_options_with_mediation_required()); await fedcm_get_and_select_first_account(t,
request_options_with_mediation_required());
// Await the first revocation since they cannot happen in parallel. Both should succeed. // Await the first disconnect since they cannot happen in parallel. Both
await IdentityCredential.revoke(revoke_options("1")); // should succeed.
return IdentityCredential.revoke(alt_revoke_options("2")); await IdentityCredential.disconnect(disconnect_options("1"));
}, 'Revocation is bound to each IDP'); return IdentityCredential.disconnect(alt_disconnect_options("2"));
}, 'Disconnect is bound to each IDP');
</script> </script>

@@ -213,7 +213,7 @@ export function fedcm_get_and_select_first_account(t, options) {
return credentialPromise; return credentialPromise;
} }
export function revoke_options(accountHint, manifest_filename) { export function disconnect_options(accountHint, manifest_filename) {
if (manifest_filename === undefined) { if (manifest_filename === undefined) {
manifest_filename = "manifest.py"; manifest_filename = "manifest.py";
} }
@@ -226,7 +226,7 @@ credential-management/support/fedcm/${manifest_filename}`;
}; };
} }
export function alt_revoke_options(accountHint, manifest_filename) { export function alt_disconnect_options(accountHint, manifest_filename) {
if (manifest_filename === undefined) { if (manifest_filename === undefined) {
manifest_filename = "manifest.py"; manifest_filename = "manifest.py";
} }

@@ -1,4 +1,4 @@
import { RequestTokenStatus, LogoutRpsStatus, RevokeStatus, FederatedAuthRequest, FederatedAuthRequestReceiver } from '/gen/third_party/blink/public/mojom/webid/federated_auth_request.mojom.m.js'; import { RequestTokenStatus, LogoutRpsStatus, DisconnectStatus, FederatedAuthRequest, FederatedAuthRequestReceiver } from '/gen/third_party/blink/public/mojom/webid/federated_auth_request.mojom.m.js';
function toMojoTokenStatus(status) { function toMojoTokenStatus(status) {
return RequestTokenStatus["k" + status]; return RequestTokenStatus["k" + status];
@@ -17,7 +17,7 @@ export class MockFederatedAuthRequest {
this.selected_identity_provider_config_url_ = null; this.selected_identity_provider_config_url_ = null;
this.status_ = RequestTokenStatus.kError; this.status_ = RequestTokenStatus.kError;
this.logoutRpsStatus_ = LogoutRpsStatus.kError; this.logoutRpsStatus_ = LogoutRpsStatus.kError;
this.revokeStatus_ = RevokeStatus.kError; this.disconnectStatus_ = DisconnectStatus.kError;
this.returnPending_ = false; this.returnPending_ = false;
this.pendingPromiseResolve_ = null; this.pendingPromiseResolve_ = null;
} }
@@ -53,13 +53,13 @@ export class MockFederatedAuthRequest {
this.logoutRpsStatus_ = validated; this.logoutRpsStatus_ = validated;
} }
// Causes the subsequent `FederatedCredential.revoke` to reject with this // Causes the subsequent `FederatedCredential.disconnect` to reject with this
// status. // status.
revokeReturn(status) { disconnectReturn(status) {
let validated = RevokeStatus[status]; let validated = DisconnectStatus[status];
if (validated === undefined) if (validated === undefined)
throw new Error("Invalid status: " + status); throw new Error("Invalid status: " + status);
this.revokeStatus_ = validated; this.disconnectStatus_ = validated;
} }
// Implements // Implements
@@ -106,9 +106,9 @@ export class MockFederatedAuthRequest {
}); });
} }
async revoke(provider, client_id, account_id) { async disconnect(provider, client_id, account_id) {
return Promise.resolve({ return Promise.resolve({
status: this.revokeStatus_ status: this.disconnectStatus_
}); });
} }
@@ -135,7 +135,7 @@ export class MockFederatedAuthRequest {
this.selected_identity_provider_config_url_ = null; this.selected_identity_provider_config_url_ = null;
this.status_ = RequestTokenStatus.kError; this.status_ = RequestTokenStatus.kError;
this.logoutRpsStatus_ = LogoutRpsStatus.kError; this.logoutRpsStatus_ = LogoutRpsStatus.kError;
this.revokeStatus_ = RevokeStatus.kError; this.disconnectStatus_ = DisconnectStatus.kError;
this.receiver_.$.close(); this.receiver_.$.close();
this.interceptor_.stop(); this.interceptor_.stop();

@@ -2,34 +2,36 @@
<script src="/resources/testdriver.js"></script> <script src="/resources/testdriver.js"></script>
<script src="/resources/testdriver-vendor.js"></script> <script src="/resources/testdriver-vendor.js"></script>
<script type="module"> <script type="module">
import {revoke_options, import {disconnect_options,
request_options_with_mediation_required, request_options_with_mediation_required,
set_fedcm_cookie, manifest_origin} from './../fedcm-helper.sub.js'; set_fedcm_cookie, manifest_origin} from './../fedcm-helper.sub.js';
// Loading this iframe in the test will make a FedCM call on load, and // Loading this iframe in the test will make a FedCM call on load, and
// trigger a postMessage upon completion. // trigger a postMessage upon completion.
// //
// message { // message {
// string result: "Pass" | "Failed get" | "Failed revoke" // string result: "Pass" | "Failed get" | "Failed disconnect"
// string errorType: error.name // string errorType: error.name
// } // }
async function attemptRevoke() { async function attemptDisconnect() {
try { try {
await IdentityCredential.revoke(revoke_options("1234")); await IdentityCredential.disconnect(disconnect_options("1234"));
window.top.postMessage({result: "Pass"}, "*"); window.top.postMessage({result: "Pass"}, "*");
} catch (error) { } catch (error) {
window.top.postMessage({result: "Failed revoke", errorType: error.name}, "*"); window.top.postMessage({result: "Failed disconnect", errorType: error.name},
"*");
} }
} }
window.onload = async () => { window.onload = async () => {
const params = new URLSearchParams(document.location.search); const params = new URLSearchParams(document.location.search);
if (params.has("skip_get")) { if (params.has("skip_get")) {
attemptRevoke(); attemptDisconnect();
return; return;
} }
// Use this variable to stop trying to select an account once the get() promise is resolved. // Use this variable to stop trying to select an account once the get()
// promise is resolved.
let cancelHelper = false; let cancelHelper = false;
try { try {
const credentialPromise = navigator.credentials.get(request_options_with_mediation_required()); const credentialPromise = navigator.credentials.get(request_options_with_mediation_required());
@@ -46,8 +48,8 @@ window.onload = async () => {
helper(); helper();
const cred = await credentialPromise; const cred = await credentialPromise;
await set_fedcm_cookie(manifest_origin); await set_fedcm_cookie(manifest_origin);
// Now that we have a get(), attempt to revoke permission. // Now that we have a get(), attempt to disconnect permission.
attemptRevoke(); attemptDisconnect();
} catch (error) { } catch (error) {
window.top.postMessage({result: "Failed get", errorType: error.name}, '*'); window.top.postMessage({result: "Failed get", errorType: error.name}, '*');
} }

@@ -13,7 +13,7 @@ def main(request, response):
"accounts_endpoint": "accounts.py", "accounts_endpoint": "accounts.py",
"client_metadata_endpoint": "client_metadata.py", "client_metadata_endpoint": "client_metadata.py",
"id_assertion_endpoint": "token.py", "id_assertion_endpoint": "token.py",
"revocation_endpoint": "revoke.py", "disconnect_endpoint": "disconnect.py",
"login_url": "login.html" "login_url": "login.html"
} }
""" """

@@ -2,6 +2,6 @@
"accounts_endpoint": "accounts.py", "accounts_endpoint": "accounts.py",
"client_metadata_endpoint": "client_metadata.py", "client_metadata_endpoint": "client_metadata.py",
"id_assertion_endpoint": "/common/redirect.py?location=/credential-management/support/fedcm/token.py&status=308", "id_assertion_endpoint": "/common/redirect.py?location=/credential-management/support/fedcm/token.py&status=308",
"revocation_endpoint": "revoke.py", "disconnect_endpoint": "disconnect.py",
"login_url": "login.html" "login_url": "login.html"
} }

@@ -294,7 +294,7 @@ SET TIMEOUT: xhr/resources/init.htm
SET TIMEOUT: xhr/resources/xmlhttprequest-timeout.js SET TIMEOUT: xhr/resources/xmlhttprequest-timeout.js
SET TIMEOUT: fenced-frame/resolve-to-config-promise.https.html SET TIMEOUT: fenced-frame/resolve-to-config-promise.https.html
SET TIMEOUT: credential-management/support/fedcm-iframe.html SET TIMEOUT: credential-management/support/fedcm-iframe.html
SET TIMEOUT: credential-management/support/fedcm/revoke-iframe.html SET TIMEOUT: credential-management/support/fedcm/disconnect-iframe.html
# generate_tests implementation and sample usage # generate_tests implementation and sample usage
GENERATE_TESTS: resources/test/tests/functional/generate-callback.html GENERATE_TESTS: resources/test/tests/functional/generate-callback.html

@@ -5256,7 +5256,7 @@ interface IIRFilterNode : AudioNode
method constructor method constructor
method getFrequencyResponse method getFrequencyResponse
interface IdentityCredential : Credential interface IdentityCredential : Credential
static method revoke static method disconnect
attribute @@toStringTag attribute @@toStringTag
getter isAutoSelected getter isAutoSelected
getter token getter token

@@ -24685,7 +24685,7 @@ Called by update_use_counter_feature_enum.py.-->
<int value="4221" label="OBSOLETE_GestureScrollUpdate"/> <int value="4221" label="OBSOLETE_GestureScrollUpdate"/>
<int value="4222" label="OBSOLETE_GestureScrollEnd"/> <int value="4222" label="OBSOLETE_GestureScrollEnd"/>
<int value="4223" label="ArrayBufferTooBigForWebAPI"/> <int value="4223" label="ArrayBufferTooBigForWebAPI"/>
<int value="4224" label="FedCmRevoke"/> <int value="4224" label="FedCmDisconnect"/>
<int value="4225" label="OBSOLETE_FedCmLogout"/> <int value="4225" label="OBSOLETE_FedCmLogout"/>
<int value="4226" label="OBSOLETE_FedCmLogoutRps"/> <int value="4226" label="OBSOLETE_FedCmLogoutRps"/>
<int value="4227" label="V8Navigator_DeprecatedReplaceInURN_Method"/> <int value="4227" label="V8Navigator_DeprecatedReplaceInURN_Method"/>
@@ -25408,6 +25408,29 @@ Called by update_permissions_policy_enum.py.-->
<int value="2" label="FailedOrigin"/> <int value="2" label="FailedOrigin"/>
</enum> </enum>
<enum name="FedCmDisconnectStatus">
<int value="0" label="Success"/>
<int value="1" label="TooManyRequests"/>
<int value="2" label="UnhandledRequest"/>
<int value="3" label="NoAccountToDisconnect"/>
<int value="4" label="DisconnectUrlIsCrossOrigin"/>
<int value="5" label="DisconnectFailedOnServer"/>
<int value="6" label="ConfigHttpNotFound"/>
<int value="7" label="ConfigNoResponse"/>
<int value="8" label="ConfigInvalidResponse"/>
<int value="9" label="DisabledInSettings"/>
<int value="10" label="DisabledInFlags"/>
<int value="11" label="WellKnownHttpNotFound"/>
<int value="12" label="WellKnownNoResponse"/>
<int value="13" label="WellKnownInvalidResponse"/>
<int value="14" label="WellKnownListEmpty"/>
<int value="15" label="ConfigNotInWellKnown"/>
<int value="16" label="WellKnownTooBig"/>
<int value="17" label="WellKnownInvalidContentType"/>
<int value="18" label="ConfigInvalidContentType"/>
<int value="19" label="IdpNotPotentiallyTrustworthy"/>
</enum>
<enum name="FedCmErrorDialogResult"> <enum name="FedCmErrorDialogResult">
<int value="0" label="More details clicked"/> <int value="0" label="More details clicked"/>
<int value="1" label="Got it clicked without more details available"/> <int value="1" label="Got it clicked without more details available"/>
@@ -25564,29 +25587,6 @@ Called by update_permissions_policy_enum.py.-->
<int value="17" label="ThirdPartyCookiesBlocked"/> <int value="17" label="ThirdPartyCookiesBlocked"/>
</enum> </enum>
<enum name="FedCmRevokeStatus2">
<int value="0" label="Success"/>
<int value="1" label="TooManyRequests"/>
<int value="2" label="UnhandledRequest"/>
<int value="3" label="NoAccountToRevoke"/>
<int value="4" label="RevokeUrlIsCrossOrigin"/>
<int value="5" label="RevocationFailedOnServer"/>
<int value="6" label="ConfigHttpNotFound"/>
<int value="7" label="ConfigNoResponse"/>
<int value="8" label="ConfigInvalidResponse"/>
<int value="9" label="DisabledInSettings"/>
<int value="10" label="DisabledInFlags"/>
<int value="11" label="WellKnownHttpNotFound"/>
<int value="12" label="WellKnownNoResponse"/>
<int value="13" label="WellKnownInvalidResponse"/>
<int value="14" label="WellKnownListEmpty"/>
<int value="15" label="ConfigNotInWellKnown"/>
<int value="16" label="WellKnownTooBig"/>
<int value="17" label="WellKnownInvalidContentType"/>
<int value="18" label="ConfigInvalidContentType"/>
<int value="19" label="IdpNotPotentiallyTrustworthy"/>
</enum>
<enum name="FedCmRpContext"> <enum name="FedCmRpContext">
<int value="0" label="Sign in"/> <int value="0" label="Sign in"/>
<int value="1" label="Sign up"/> <int value="1" label="Sign up"/>
@@ -39707,6 +39707,7 @@ from previous Chrome versions.
<int value="1030608602" label="AutofillAssistantProactiveHelp:enabled"/> <int value="1030608602" label="AutofillAssistantProactiveHelp:enabled"/>
<int value="1030922819" label="AlignWakeUps:enabled"/> <int value="1030922819" label="AlignWakeUps:enabled"/>
<int value="1031239808" label="ForceMajorVersion100InUserAgent:disabled"/> <int value="1031239808" label="ForceMajorVersion100InUserAgent:disabled"/>
<int value="1031278117" label="FedCmDisconnect:enabled"/>
<int value="1031281564" <int value="1031281564"
label="DisablePeripheralDataAccessProtection:disabled"/> label="DisablePeripheralDataAccessProtection:disabled"/>
<int value="1033148287" label="NTPShortcuts:disabled"/> <int value="1033148287" label="NTPShortcuts:disabled"/>
@@ -41605,6 +41606,7 @@ from previous Chrome versions.
<int value="1918984253" <int value="1918984253"
label="OmniboxUIExperimentBlueSearchLoopAndSearchQuery:disabled"/> label="OmniboxUIExperimentBlueSearchLoopAndSearchQuery:disabled"/>
<int value="1919380242" label="ReduceAcceptLanguage:disabled"/> <int value="1919380242" label="ReduceAcceptLanguage:disabled"/>
<int value="1919786756" label="FedCmDisconnect:disabled"/>
<int value="1919917329" label="ImplicitRootScroller:disabled"/> <int value="1919917329" label="ImplicitRootScroller:disabled"/>
<int value="1920894670" <int value="1920894670"
label="OmniboxPreserveDefaultMatchAgainstAsyncUpdate:enabled"/> label="OmniboxPreserveDefaultMatchAgainstAsyncUpdate:enabled"/>

@@ -1601,6 +1601,16 @@ chromium-metrics-reviews@google.com.
<summary>Records the result of CSP checks in the FedCM API.</summary> <summary>Records the result of CSP checks in the FedCM API.</summary>
</histogram> </histogram>
<histogram name="Blink.FedCm.Status.Disconnect" enum="FedCmDisconnectStatus"
expires_after="M125">
<owner>npm@chromium.org</owner>
<owner>web-identity-eng@google.com</owner>
<summary>
Records the status of a disconnect call to the FedCM API. Recorded once per
IdentityCredential.disconnect() call, once the browser knows the result.
</summary>
</histogram>
<histogram name="Blink.FedCm.Status.IdpSigninMatch" <histogram name="Blink.FedCm.Status.IdpSigninMatch"
enum="FedCmIdpSigninMatchStatus" expires_after="2024-05-05"> enum="FedCmIdpSigninMatchStatus" expires_after="2024-05-05">
<owner>cbiesinger@chromium.org</owner> <owner>cbiesinger@chromium.org</owner>
@@ -1634,16 +1644,6 @@ chromium-metrics-reviews@google.com.
</summary> </summary>
</histogram> </histogram>
<histogram name="Blink.FedCm.Status.Revoke2" enum="FedCmRevokeStatus2"
expires_after="M125">
<owner>npm@chromium.org</owner>
<owner>web-identity-eng@google.com</owner>
<summary>
Records the status of a revoke call to the FedCM API. Recorded once per
IdentityCredential.revoke() call, once the browser knows the result.
</summary>
</histogram>
<histogram name="Blink.FedCm.Status.SignInStateMatch" <histogram name="Blink.FedCm.Status.SignInStateMatch"
enum="FedCmSignInStateMatchStatus" expires_after="M125"> enum="FedCmSignInStateMatchStatus" expires_after="M125">
<owner>tanzachary@chromium.org</owner> <owner>tanzachary@chromium.org</owner>

@@ -4463,6 +4463,13 @@ be describing additional metrics about the same event.
where the site of the iframe is compared with the site of the main frame. where the site of the iframe is compared with the site of the main frame.
</summary> </summary>
</metric> </metric>
<metric name="Status.Disconnect" enum="FedCmDisconnectStatus">
<summary>
Records the status of a disconnect call to the FedCM API. Recorded once
per IdentityCredential.disconnect() call, once the browser knows the
result.
</summary>
</metric>
<metric name="Status.MediationRequirement" <metric name="Status.MediationRequirement"
enum="CredentialManagerMediationRequirement"> enum="CredentialManagerMediationRequirement">
<summary> <summary>
@@ -4484,12 +4491,6 @@ be describing additional metrics about the same event.
Records the status of a revoke call to the FedCM API. Records the status of a revoke call to the FedCM API.
</summary> </summary>
</metric> </metric>
<metric name="Status.Revoke2" enum="FedCmRevokeStatus2">
<summary>
Records the status of a revoke call to the FedCM API. Recorded once per
IdentityCredential.revoke() call, once the browser knows the result.
</summary>
</metric>
<metric name="Status.SignInStateMatch" enum="FedCmSignInStateMatchStatus"> <metric name="Status.SignInStateMatch" enum="FedCmSignInStateMatchStatus">
<summary> <summary>
Records whether user sign-in states between IDP and browser match after Records whether user sign-in states between IDP and browser match after
@@ -4605,6 +4606,13 @@ be describing additional metrics about the same event.
Records a 1 each time a mismatch dialog is shown. Records a 1 each time a mismatch dialog is shown.
</summary> </summary>
</metric> </metric>
<metric name="Status.Disconnect" enum="FedCmDisconnectStatus">
<summary>
Records the status of a disconnect call to the FedCM API. Recorded once
per IdentityCredential.disconnect() call, once the browser knows the
result.
</summary>
</metric>
<metric name="Status.MediationRequirement" <metric name="Status.MediationRequirement"
enum="CredentialManagerMediationRequirement"> enum="CredentialManagerMediationRequirement">
<summary> <summary>
@@ -4618,12 +4626,6 @@ be describing additional metrics about the same event.
Records the status of a request id token call to the FedCM API. Records the status of a request id token call to the FedCM API.
</summary> </summary>
</metric> </metric>
<metric name="Status.Revoke2" enum="FedCmRevokeStatus2">
<summary>
Records the status of a revoke call to the FedCM API. Recorded once per
IdentityCredential.revoke() call, once the browser knows the result.
</summary>
</metric>
<metric name="Status.SignInStateMatch" enum="FedCmSignInStateMatchStatus"> <metric name="Status.SignInStateMatch" enum="FedCmSignInStateMatchStatus">
<summary> <summary>
Records whether user sign-in states between IDP and browser match after Records whether user sign-in states between IDP and browser match after