Reland "Reland "[UNO] Change reconcilor behavior when user not signed in to chrome""
This is a reland of commit 07e3f75861d73c22a8e176638f3ffe36e4373be3
Original change's description:
> Reland "[UNO] Change reconcilor behavior when user not signed in to chrome"
>
> This is a reland of commit 3ba4805e4d5c68485cec63c5d5f835231d43089c
>
> Original change's description:
> > [UNO] Change reconcilor behavior when user not signed in to chrome
> >
> > This CL does the following:
> > - Disables cookie updates/OAuthMultiLogin when the user is not signed in
> > to chrome.
> > - In the above mode, accounts in the cookie are the source of truth.
> > Refresh tokens are updated to match the accounts in the cookie. If a
> > refresh token doesn't have an account in the gaia cookie or the
> > account is invalid (session expired) the refresh token is removed from
> > chrome.
> >
> > Bug: b/320279580
> > Change-Id: I26dc6e62cfba34fd6304f43e8963952bb1fed9a6
> > Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5212147
> > Reviewed-by: Alex Ilin <alexilin@chromium.org>
> > Reviewed-by: David Roger <droger@chromium.org>
> > Commit-Queue: Monica Basta <msalama@chromium.org>
> > Cr-Commit-Position: refs/heads/main@{#1251450}
>
> Bug: b/320279580
> Change-Id: Id670fef5a715ad214e3457b1c67ebbbe28259eec
> Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5258540
> Mega-CQ: Monica Basta <msalama@chromium.org>
> Reviewed-by: Alex Ilin <alexilin@chromium.org>
> Reviewed-by: David Roger <droger@chromium.org>
> Commit-Queue: Monica Basta <msalama@chromium.org>
> Cr-Commit-Position: refs/heads/main@{#1256749}
Bug: b/320279580
Change-Id: I5021f8b3f00b50ea8fb06bbb5d4552c81cd267ed
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/5272597
Commit-Queue: Monica Basta <msalama@chromium.org>
Reviewed-by: David Roger <droger@chromium.org>
Reviewed-by: Alex Ilin <alexilin@chromium.org>
Cr-Commit-Position: refs/heads/main@{#1258416}
diff --git a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
index 10bda6e..50632f0 100644
--- a/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
+++ b/chrome/browser/policy/cloud/user_policy_signin_service_browsertest.cc
@@ -235,10 +235,11 @@
embedded_test_server_.StartAcceptingConnections();
- account_info_ =
- signin::MakeAccountAvailable(identity_manager(), kTestEmail);
- signin::SetRefreshTokenForAccount(
- identity_manager(), account_info_.account_id, kTestRefreshToken);
+ account_info_ = MakeAccountAvailable(
+ identity_manager(), signin::AccountAvailabilityOptionsBuilder()
+ .AsPrimary(signin::ConsentLevel::kSignin)
+ .WithRefreshToken(kTestRefreshToken)
+ .Build(kTestEmail));
SetupFakeGaiaResponses();
}
@@ -457,9 +458,7 @@
CreateTurnSyncOnHelper();
WaitForPolicyHanging();
- // User is not signed in, policy is not applied.
- EXPECT_EQ(std::nullopt,
- signin::GetPrimaryAccountConsentLevel(identity_manager()));
+ // Policy hanging, policy is not applied.
EXPECT_FALSE(profile()->GetPrefs()->GetBoolean(prefs::kShowHomeButton));
EXPECT_TRUE(signin_client()->IsClearPrimaryAccountAllowed(
/*has_sync_account=*/false));
diff --git a/chrome/browser/ui/webui/signin/turn_sync_on_helper_browsertest.cc b/chrome/browser/ui/webui/signin/turn_sync_on_helper_browsertest.cc
index d920421..e9964d6 100644
--- a/chrome/browser/ui/webui/signin/turn_sync_on_helper_browsertest.cc
+++ b/chrome/browser/ui/webui/signin/turn_sync_on_helper_browsertest.cc
@@ -379,8 +379,15 @@
// For the scenario in https://crbug.com/1404961, the reconcilor has to be
// triggered by the account removal.
+#if BUILDFLAG(IS_CHROMEOS_LACROS)
ASSERT_EQ(reconcilor->GetState(),
signin_metrics::AccountReconcilorState::kRunning);
+#endif // BUILDFLAG(IS_CHROMEOS_LACROS)
+ // On Dice platforms with `switches::kUnoDesktop` enabled and empty primary
+ // account, updating cookies is disabled. Therefore running the reconcilor
+ // doesn't require any network requests and might have been completed by now.
+ // The reconcilor will not remove the account from cookies but revoking
+ // refresh tokens should be sufficient to invalidate cookies.
}
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_MAC) || BUILDFLAG(IS_WIN)
diff --git a/components/signin/core/browser/BUILD.gn b/components/signin/core/browser/BUILD.gn
index 8023bd5a..35913e8 100644
--- a/components/signin/core/browser/BUILD.gn
+++ b/components/signin/core/browser/BUILD.gn
@@ -165,6 +165,10 @@
"mirror_landing_account_reconcilor_delegate_unittest.cc",
]
}
+
+ if (enable_dice_support) {
+ sources += [ "dice_account_reconcilor_delegate_unittest.cc" ]
+ }
}
if (is_android) {
diff --git a/components/signin/core/browser/account_reconcilor.cc b/components/signin/core/browser/account_reconcilor.cc
index a417222..ada6c17 100644
--- a/components/signin/core/browser/account_reconcilor.cc
+++ b/components/signin/core/browser/account_reconcilor.cc
@@ -596,7 +596,15 @@
// completely remove them from Chrome.
// Revoking the token for the primary account is not supported (it should be
// signed out or put to auth error state instead).
- delegate_->RevokeSecondaryTokensBeforeReconcileIfNeeded();
+ delegate_->RevokeSecondaryTokensForReconcileIfNeeded(verified_gaia_accounts);
+ if (!delegate_->IsUpdateCookieAllowed()) {
+ // TODO(b/320279580): Record reconcilor operation if tokens were revoked to
+ // match cookies.
+ error_during_last_reconcile_ = GoogleServiceAuthError::AuthErrorNone();
+ CalculateIfMultiloginReconcileIsDone();
+ ScheduleStartReconcileIfChromeAccountsChanged();
+ return;
+ }
std::vector<CoreAccountId> chrome_accounts =
LoadValidAccountsFromTokenService();
diff --git a/components/signin/core/browser/account_reconcilor_delegate.cc b/components/signin/core/browser/account_reconcilor_delegate.cc
index 4b58001..b046242 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/account_reconcilor_delegate.cc
@@ -22,6 +22,10 @@
return false;
}
+bool AccountReconcilorDelegate::IsUpdateCookieAllowed() const {
+ return true;
+}
+
gaia::GaiaSource AccountReconcilorDelegate::GetGaiaApiSource() const {
NOTREACHED() << "Reconcile is not enabled, no Gaia API calls should be made.";
return gaia::GaiaSource::kChrome;
@@ -175,8 +179,8 @@
return std::vector<CoreAccountId>();
}
-void AccountReconcilorDelegate::RevokeSecondaryTokensBeforeReconcileIfNeeded() {
-}
+void AccountReconcilorDelegate::RevokeSecondaryTokensForReconcileIfNeeded(
+ const std::vector<gaia::ListedAccount>& gaia_accounts) {}
void AccountReconcilorDelegate::OnAccountsCookieDeletedByUserAction(
bool synced_data_deletion_in_progress) {}
diff --git a/components/signin/core/browser/account_reconcilor_delegate.h b/components/signin/core/browser/account_reconcilor_delegate.h
index 777778a..f54cca5 100644
--- a/components/signin/core/browser/account_reconcilor_delegate.h
+++ b/components/signin/core/browser/account_reconcilor_delegate.h
@@ -29,6 +29,11 @@
// false.
virtual bool IsReconcileEnabled() const;
+ // Returns true if calling OAuthMultiLogin to update cookies to match
+ // refresh tokens is allowed. Used in Dice to disable updating cookies when
+ // the user is not signed in to chrome.
+ virtual bool IsUpdateCookieAllowed() const;
+
// Returns the value to set in the "source" parameter for Gaia API calls.
virtual gaia::GaiaSource GetGaiaApiSource() const;
@@ -55,8 +60,14 @@
const std::vector<gaia::ListedAccount>& gaia_accounts,
bool first_execution);
- // Revokes secondary accounts if needed.
- virtual void RevokeSecondaryTokensBeforeReconcileIfNeeded();
+ // On Dice platforms:
+ // - Revokes tokens in error state except the primary account with consent
+ // level `GetConsentLevelForPrimaryAccount()`.
+ // - If `IsUpdateCookieAllowed()` returns false, it also revokes tokens not
+ // present in the gaia cookies to maintain account consistency.
+ // On other platforms, this is no-op.
+ virtual void RevokeSecondaryTokensForReconcileIfNeeded(
+ const std::vector<gaia::ListedAccount>& gaia_accounts);
// Called when cookies are deleted by user action.
// This might be a no-op or signout the profile or lead to a sync paused state
diff --git a/components/signin/core/browser/account_reconcilor_unittest.cc b/components/signin/core/browser/account_reconcilor_unittest.cc
index d26877c..961caf1 100644
--- a/components/signin/core/browser/account_reconcilor_unittest.cc
+++ b/components/signin/core/browser/account_reconcilor_unittest.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include "components/signin/core/browser/account_reconcilor.h"
+
#include <cstring>
#include <map>
#include <memory>
@@ -10,6 +12,7 @@
#include <vector>
#include "base/containers/contains.h"
+#include "base/feature_list.h"
#include "base/ranges/algorithm.h"
#include "base/run_loop.h"
#include "base/scoped_observation.h"
@@ -25,7 +28,6 @@
#include "components/content_settings/core/browser/content_settings_observer.h"
#include "components/content_settings/core/common/content_settings_pattern.h"
#include "components/prefs/pref_service.h"
-#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/core/browser/mirror_account_reconcilor_delegate.h"
#include "components/signin/public/base/account_consistency_method.h"
#include "components/signin/public/base/consent_level.h"
@@ -1571,6 +1573,40 @@
account_info.account_id));
}
+const std::vector<AccountReconcilorTestTableParam>
+ kDiceParamsUnoPreChromeSignIn = {
+ // clang-format off
+ // See `kDiceParams` above for detailed params format.
+ { "", "A", IsFirstReconcile::kBoth, "", "", "A" },
+ { "AB", "", IsFirstReconcile::kBoth, "", "" , "" },
+ { "AB", "A", IsFirstReconcile::kBoth, "", "A", "A" },
+ { "A", "B", IsFirstReconcile::kBoth, "", "" , "B" },
+ { "xA", "A", IsFirstReconcile::kBoth, "", "", "A" },
+ { "xAB", "A", IsFirstReconcile::kBoth, "", "", "A" },
+
+ // Account marked as invalid in cookies.
+ { "A", "xA", IsFirstReconcile::kBoth, "", "", "xA" },
+ { "AB", "AxB", IsFirstReconcile::kBoth, "", "A", "AxB" },
+ { "xA", "xA", IsFirstReconcile::kBoth, "", "", "xA" },
+ // clang-format on
+};
+class AccountReconcilorTestDiceWithUnoPreChromeSignIn
+ : public AccountReconcilorTestTable {
+ private:
+ base::test::ScopedFeatureList scoped_feature_list_{switches::kUnoDesktop};
+};
+
+// Checks one row of the `kDiceParamsUnoPreChromeSignIn` table above.
+TEST_P(AccountReconcilorTestDiceWithUnoPreChromeSignIn, TableRowTest) {
+ SetAccountConsistency(signin::AccountConsistencyMethod::kDice);
+ RunRowTest(GetParam());
+}
+
+INSTANTIATE_TEST_SUITE_P(,
+ AccountReconcilorTestDiceWithUnoPreChromeSignIn,
+ ::testing::ValuesIn(GenerateTestCasesFromParams(
+ kDiceParamsUnoPreChromeSignIn)));
+
#endif // BUILDFLAG(ENABLE_DICE_SUPPORT)
// clang-format off
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.cc b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
index 586a50f..6e05332b 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.cc
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.cc
@@ -17,6 +17,7 @@
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_switches.h"
+#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
#include "components/signin/public/identity_manager/accounts_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_mutator.h"
@@ -35,7 +36,7 @@
} // namespace
#endif
-// Revokes tokens for all accounts in chrome_accounts but the primary account.
+// Revokes tokens for all accounts in chrome accounts but the primary account.
void RevokeAllSecondaryTokens(
IdentityManager* identity_manager,
ConsentLevel consent_level,
@@ -97,6 +98,40 @@
return true;
}
+bool DiceAccountReconcilorDelegate::IsUpdateCookieAllowed() const {
+ CHECK(IsReconcileEnabled());
+ if (switches::IsExplicitBrowserSigninUIOnDesktopEnabled(
+ switches::ExplicitBrowserSigninPhase::kExperimental)) {
+ // If the user is not signed in to chrome, cookie updates (OAuthMultiLogin)
+ // is not allowed. In this mode, cookies are the source of truth.
+ return identity_manager_->HasPrimaryAccount(
+ GetConsentLevelForPrimaryAccount());
+ }
+ return true;
+}
+
+void DiceAccountReconcilorDelegate::MatchTokensWithAccountsInCookie(
+ const std::vector<gaia::ListedAccount>& gaia_accounts) {
+ CHECK(!IsUpdateCookieAllowed());
+ const signin_metrics::SourceForRefreshTokenOperation source =
+ signin_metrics::SourceForRefreshTokenOperation::
+ kAccountReconcilor_RevokeTokensNotInCookies;
+ auto* accounts_mutator = identity_manager_->GetAccountsMutator();
+ for (const CoreAccountInfo& account_info :
+ identity_manager_->GetAccountsWithRefreshTokens()) {
+ auto it = base::ranges::find(gaia_accounts, account_info.account_id,
+ &gaia::ListedAccount::id);
+ if (it == gaia_accounts.end() || !it->valid) {
+ // Account not in the cookie or the account is not valid (session
+ // expired) and requires the user to reauth.
+ accounts_mutator->RemoveAccount(account_info.account_id, source);
+ }
+ }
+ // TODO(b/320279580): Record a histogram with the number of valid signed in
+ // accounts in the cookie but doesn't have a refresh token (aka Chrome
+ // account).
+}
+
DiceAccountReconcilorDelegate::InconsistencyReason
DiceAccountReconcilorDelegate::GetInconsistencyReason(
const CoreAccountId& primary_account,
@@ -331,14 +366,18 @@
: gaia::MultiloginMode::MULTILOGIN_UPDATE_COOKIE_ACCOUNTS_ORDER;
}
-void DiceAccountReconcilorDelegate::
- RevokeSecondaryTokensBeforeReconcileIfNeeded() {
+void DiceAccountReconcilorDelegate::RevokeSecondaryTokensForReconcileIfNeeded(
+ const std::vector<gaia::ListedAccount>& gaia_accounts) {
RevokeAllSecondaryTokens(identity_manager_,
GetConsentLevelForPrimaryAccount(),
signin_metrics::SourceForRefreshTokenOperation::
kAccountReconcilor_GaiaCookiesUpdated,
signin_metrics::ProfileSignout::kGaiaCookieUpdated,
/*revoke_only_if_in_error=*/true);
+ if (!IsUpdateCookieAllowed()) {
+ // Cookie update not enabled, match tokens with Gaia accounts.
+ MatchTokensWithAccountsInCookie(gaia_accounts);
+ }
}
void DiceAccountReconcilorDelegate::OnAccountsCookieDeletedByUserAction(
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate.h b/components/signin/core/browser/dice_account_reconcilor_delegate.h
index 33ac57a..fea1581 100644
--- a/components/signin/core/browser/dice_account_reconcilor_delegate.h
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate.h
@@ -28,8 +28,10 @@
// AccountReconcilorDelegate:
bool IsReconcileEnabled() const override;
+ bool IsUpdateCookieAllowed() const override;
gaia::GaiaSource GetGaiaApiSource() const override;
- void RevokeSecondaryTokensBeforeReconcileIfNeeded() override;
+ void RevokeSecondaryTokensForReconcileIfNeeded(
+ const std::vector<gaia::ListedAccount>& gaia_accounts) override;
void OnReconcileFinished(const CoreAccountId& first_account) override;
void OnAccountsCookieDeletedByUserAction(
bool synced_data_deletion_in_progress) override;
@@ -57,6 +59,12 @@
kMaxValue = kSyncCookieNotFirst
};
+ // Used when update cookies to match refresh tokens is not allowed, see
+ // `IsUpdateCookieAllowed()`. In this mode, refresh tokens are updated to
+ // match accounts in the Gaia cookies to maintain consistency.
+ void MatchTokensWithAccountsInCookie(
+ const std::vector<gaia::ListedAccount>& gaia_accounts);
+
// Computes inconsistency reason between tokens and gaia cookies.
InconsistencyReason GetInconsistencyReason(
const CoreAccountId& primary_account,
diff --git a/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc b/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
new file mode 100644
index 0000000..8039cd2
--- /dev/null
+++ b/components/signin/core/browser/dice_account_reconcilor_delegate_unittest.cc
@@ -0,0 +1,165 @@
+// Copyright 2024 The Chromium Authors
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "components/signin/core/browser/dice_account_reconcilor_delegate.h"
+
+#include <optional>
+
+#include "base/test/scoped_feature_list.h"
+#include "base/test/task_environment.h"
+#include "components/signin/public/base/consent_level.h"
+#include "components/signin/public/base/signin_client.h"
+#include "components/signin/public/base/signin_switches.h"
+#include "components/signin/public/identity_manager/accounts_in_cookie_jar_info.h"
+#include "components/signin/public/identity_manager/identity_manager.h"
+#include "components/signin/public/identity_manager/identity_test_environment.h"
+#include "components/signin/public/identity_manager/identity_test_utils.h"
+#include "google_apis/gaia/gaia_auth_util.h"
+#include "google_apis/gaia/gaia_constants.h"
+#include "services/network/test/test_url_loader_factory.h"
+#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace signin {
+namespace {
+constexpr char kPrimaryAccountEmail[] = "primary@gmail.com ";
+
+gaia::ListedAccount GetListedAccountFromAccountInfo(
+ const AccountInfo& account_info,
+ bool valid = true) {
+ gaia::ListedAccount gaia_account;
+ gaia_account.id = account_info.account_id;
+ gaia_account.email = account_info.email;
+ gaia_account.gaia_id = account_info.gaia;
+ gaia_account.valid = valid;
+ return gaia_account;
+}
+
+class DiceAccountReconcilorDelegateTest : public testing::Test {
+ public:
+ DiceAccountReconcilorDelegateTest()
+ : delegate_(identity_manager(),
+ identity_test_environment_.signin_client()) {}
+
+ DiceAccountReconcilorDelegate& delegate() { return delegate_; }
+
+ IdentityTestEnvironment& identity_test_environment() {
+ return identity_test_environment_;
+ }
+
+ IdentityManager* identity_manager() {
+ return identity_test_environment().identity_manager();
+ }
+
+ private:
+ base::test::SingleThreadTaskEnvironment task_environment_;
+ base::test::ScopedFeatureList scoped_feature_list_{switches::kUnoDesktop};
+ IdentityTestEnvironment identity_test_environment_;
+ DiceAccountReconcilorDelegate delegate_;
+};
+
+TEST_F(DiceAccountReconcilorDelegateTest, GetConsentLevelForPrimaryAccount) {
+ // Uno enabled.
+ EXPECT_EQ(delegate().GetConsentLevelForPrimaryAccount(),
+ ConsentLevel::kSignin);
+}
+
+TEST_F(DiceAccountReconcilorDelegateTest,
+ IsUpdateCookieAllowedPreChromeSignIn) {
+ EXPECT_FALSE(delegate().IsUpdateCookieAllowed());
+}
+
+TEST_F(DiceAccountReconcilorDelegateTest,
+ IsUpdateCookieAllowedPostChromeSignIn) {
+ identity_test_environment().MakePrimaryAccountAvailable(
+ kPrimaryAccountEmail, ConsentLevel::kSignin);
+ ASSERT_TRUE(identity_manager()->HasPrimaryAccount(ConsentLevel::kSignin));
+ EXPECT_TRUE(delegate().IsUpdateCookieAllowed());
+}
+
+TEST_F(DiceAccountReconcilorDelegateTest,
+ RevokeSecondaryTokensForReconcileIfNeededPreChromeSignIn) {
+ AccountInfo valid_account =
+ identity_test_environment().MakeAccountAvailable("account@gmail.com");
+
+ // Accounts with invalid refresh token should be revoked.
+ AccountInfo account_with_invalid_refresh_token =
+ identity_test_environment().MakeAccountAvailable(
+ AccountAvailabilityOptionsBuilder()
+ .WithRefreshToken(GaiaConstants::kInvalidRefreshToken)
+ .Build("invalid_refresh_token@gmail.com"));
+
+ // Accounts not in the gaia cookie should be revoked.
+ AccountInfo no_cookie_account =
+ identity_test_environment().MakeAccountAvailable("no_cookie@gmail.com");
+ AccountInfo cookie_no_refresh_token_account =
+ identity_test_environment().MakeAccountAvailable(
+ AccountAvailabilityOptionsBuilder().WithoutRefreshToken().Build(
+ "no_refresh_token_with_cookie@gmail.com"));
+
+ // Setup account with refresh token and invalid cookie account.
+ // The refresh token should be revoked
+ AccountInfo invalid_cookie_account =
+ identity_test_environment().MakeAccountAvailable(
+ "invalid_cookie@gmail.com");
+
+ // Verify the test setup.
+ EXPECT_THAT(identity_manager()->GetAccountsWithRefreshTokens(),
+ ::testing::UnorderedElementsAre(
+ valid_account, account_with_invalid_refresh_token,
+ no_cookie_account, invalid_cookie_account));
+ ASSERT_FALSE(delegate().IsUpdateCookieAllowed());
+
+ const std::vector<gaia::ListedAccount> gaia_signed_in_accounts{
+ GetListedAccountFromAccountInfo(valid_account),
+ GetListedAccountFromAccountInfo(cookie_no_refresh_token_account),
+ GetListedAccountFromAccountInfo(invalid_cookie_account, /*valid=*/false)};
+
+ delegate().RevokeSecondaryTokensForReconcileIfNeeded(gaia_signed_in_accounts);
+ std::vector<CoreAccountInfo> chrome_accounts =
+ identity_manager()->GetAccountsWithRefreshTokens();
+ ASSERT_EQ(chrome_accounts.size(), 1u);
+ EXPECT_THAT(chrome_accounts[0], ::testing::Eq(valid_account));
+}
+
+TEST(DiceAccountReconcilorDelegateTestUnoDisabled,
+ RevokeSecondaryTokensForReconcile) {
+ base::test::SingleThreadTaskEnvironment task_environment;
+ base::test::ScopedFeatureList scoped_feature_list;
+ IdentityTestEnvironment identity_test_environment;
+ IdentityManager* identity_manager =
+ identity_test_environment.identity_manager();
+ DiceAccountReconcilorDelegate delegate(
+ identity_manager, identity_test_environment.signin_client());
+
+ EXPECT_TRUE(delegate.IsUpdateCookieAllowed());
+
+ scoped_feature_list.InitAndDisableFeature(switches::kUnoDesktop);
+
+ AccountInfo valid_account =
+ identity_test_environment.MakeAccountAvailable("account@gmail.com");
+
+ // Accounts with invalid refresh token should be revoked.
+ AccountInfo account_with_invalid_refresh_token =
+ identity_test_environment.MakeAccountAvailable(
+ AccountAvailabilityOptionsBuilder()
+ .WithRefreshToken(GaiaConstants::kInvalidRefreshToken)
+ .Build("invalid_refresh_token@gmail.com"));
+
+ // Uno disabled, only accounts with invalid refresh tokens should be revoked.
+ AccountInfo no_cookie_account =
+ identity_test_environment.MakeAccountAvailable("no_cookie@gmail.com");
+ EXPECT_EQ(identity_manager->GetAccountsWithRefreshTokens().size(), 3u);
+
+ const std::vector<gaia::ListedAccount> gaia_signed_in_accounts{
+ GetListedAccountFromAccountInfo(valid_account),
+ GetListedAccountFromAccountInfo(account_with_invalid_refresh_token)};
+ delegate.RevokeSecondaryTokensForReconcileIfNeeded(gaia_signed_in_accounts);
+ EXPECT_THAT(
+ identity_manager->GetAccountsWithRefreshTokens(),
+ ::testing::UnorderedElementsAre(valid_account, no_cookie_account));
+}
+
+} // namespace
+} // namespace signin
diff --git a/components/signin/internal/identity_manager/profile_oauth2_token_service.cc b/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
index dc5d3e9..aaed39d 100644
--- a/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
+++ b/components/signin/internal/identity_manager/profile_oauth2_token_service.cc
@@ -12,6 +12,7 @@
#include "components/prefs/pref_registry_simple.h"
#include "components/signin/internal/identity_manager/profile_oauth2_token_service_delegate.h"
#include "components/signin/public/base/device_id_helper.h"
+#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/google_service_auth_error.h"
@@ -63,6 +64,9 @@
return "LogoutTabHelper::PrimaryPageChanged";
case SourceForRefreshTokenOperation::kForceSigninReauthWithDifferentAccount:
return "ForceSigninReauthWithDifferentAccount";
+ case SourceForRefreshTokenOperation::
+ kAccountReconcilor_RevokeTokensNotInCookies:
+ return "AccountReconcilor::RevokeTokensNotInCookies";
}
}
} // namespace
diff --git a/components/signin/public/base/signin_metrics.h b/components/signin/public/base/signin_metrics.h
index 479cb96..a3379d9 100644
--- a/components/signin/public/base/signin_metrics.h
+++ b/components/signin/public/base/signin_metrics.h
@@ -438,8 +438,9 @@
// kAccountReconcilor_RevokeTokensNotInCookies = 18,
kLogoutTabHelper_PrimaryPageChanged = 19,
kForceSigninReauthWithDifferentAccount = 20,
+ kAccountReconcilor_RevokeTokensNotInCookies = 21,
- kMaxValue = kForceSigninReauthWithDifferentAccount,
+ kMaxValue = kAccountReconcilor_RevokeTokensNotInCookies,
};
// Different types of reporting. This is used as a histogram suffix.
diff --git a/tools/metrics/histograms/metadata/signin/enums.xml b/tools/metrics/histograms/metadata/signin/enums.xml
index 17c633e..ede12f47 100644
--- a/tools/metrics/histograms/metadata/signin/enums.xml
+++ b/tools/metrics/histograms/metadata/signin/enums.xml
@@ -766,6 +766,7 @@
</int>
<int value="19" label="LogoutTabHelper::DidFinishNavigation"/>
<int value="20" label="ForceSigninReauthWithDifferentAccount"/>
+ <int value="21" label="AccountReconcilor::RevokeTokensNotInCookies"/>
</enum>
<enum name="SyncErrorPromptUIAction">