// Copyright 2014 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/signin/chrome_signin_client.h"

#include <stddef.h>

#include <memory>
#include <string>
#include <string_view>
#include <utility>

#include "base/feature_list.h"
#include "base/functional/bind.h"
#include "base/metrics/histogram_functions.h"
#include "base/strings/strcat.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/content_settings/cookie_settings_factory.h"
#include "chrome/browser/content_settings/host_content_settings_map_factory.h"
#include "chrome/browser/enterprise/util/managed_browser_utils.h"
#include "chrome/browser/metrics/chrome_metrics_service_accessor.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_attributes_entry.h"
#include "chrome/browser/profiles/profile_attributes_storage.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profile_metrics.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/chrome_device_id_helper.h"
#include "chrome/browser/signin/force_signin_verifier.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/signin_hats_util.h"
#include "chrome/browser/signin/signin_util.h"
#include "chrome/browser/ui/hats/survey_config.h"
#include "chrome/common/buildflags.h"
#include "chrome/common/channel_info.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/pref_names.h"
#include "components/browser_actuator/public/features.h"
#include "components/content_settings/core/browser/cookie_settings.h"
#include "components/contextual_tasks/public/features.h"
#include "components/metrics/metrics_service.h"
#include "components/omnibox/common/omnibox_features.h"
#include "components/policy/core/browser/browser_policy_connector.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/cookie_settings_util.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/base/signin_client.h"
#include "components/signin/public/base/signin_metrics.h"
#include "components/signin/public/base/signin_pref_names.h"
#include "components/signin/public/base/signin_prefs.h"
#include "components/signin/public/identity_manager/access_token_info.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/primary_account_change_event.h"
#include "components/site_token_provider/features.h"
#include "components/skills/features.h"
#include "components/version_info/channel.h"
#include "content/public/browser/browser_context.h"
#include "content/public/browser/storage_partition.h"
#include "google_apis/gaia/gaia_auth_fetcher.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "ui/base/models/tree_node_iterator.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/signin/wait_for_network_callback_helper_ash.h"
#include "chromeos/ash/components/network/network_handler.h"
#endif

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/android/tab_model/tab_model.h"
#include "chrome/browser/ui/android/tab_model/tab_model_list.h"
#endif

#if !BUILDFLAG(IS_ANDROID)
#include "chrome/browser/ui/browser_window/public/browser_window_interface.h"
#include "chrome/browser/ui/tabs/tab_strip_model.h"
#endif

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/lifetime/application_lifetime_desktop.h"
#include "chrome/browser/ui/browser_window/public/browser_window_interface_iterator.h"
#include "chrome/browser/ui/profiles/profile_picker.h"
#endif

#if !BUILDFLAG(IS_CHROMEOS)
#include "chrome/browser/signin/wait_for_network_callback_helper_chrome.h"
#endif

#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_cookie_refresh_service_factory.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_oauth_multilogin_delegate_impl.h"
#include "chrome/browser/signin/bound_session_credentials/bound_session_request_throttled_handler_browser_impl.h"
#include "chrome/browser/signin/bound_session_credentials/throttled_gaia_auth_fetcher.h"
#endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)

#if BUILDFLAG(ENABLE_EXTENSIONS)
#include "extensions/common/extension_features.h"
#endif

namespace {

// OAuth2 scopes for Contextual Tasks.
inline constexpr char kCalendarEventsOAuth2Scope[] =
    "https://www.googleapis.com/auth/calendar.events";
inline constexpr char kCalendarFreeBusyOAuth2Scope[] =
    "https://www.googleapis.com/auth/calendar.freebusy";
inline constexpr char kCalendarListOAuth2Scope[] =
    "https://www.googleapis.com/auth/calendar.calendarlist";
inline constexpr char kDocumentsOAuth2Scope[] =
    "https://www.googleapis.com/auth/documents";
inline constexpr char kGmailModifyOAuth2Scope[] =
    "https://www.googleapis.com/auth/gmail.modify";
inline constexpr char kPeopleReadOnlyOAuth2Scope[] =
    "https://www.googleapis.com/auth/peopleapi.readonly";
inline constexpr char kSpreadsheetsOAuth2Scope[] =
    "https://www.googleapis.com/auth/spreadsheets";

// List of sources for which sign out is always allowed.
// TODO(crbug.com/40162614): core product logic should not rely on metric
// sources/callsites.  Consider removing such logic, potentially as part of
// introducing a cross-platform SigninManager.
signin_metrics::ProfileSignout kAlwaysAllowedSignoutSources[] = {
    // Allowed, because data has not been synced yet.
    signin_metrics::ProfileSignout::kAbortSignin,
    // Allowed, because the primary account must be cleared when the account is
    // removed from device. Only used on Android.
    signin_metrics::ProfileSignout::kAccountRemovedFromDevice,
    // Allowed, for tests.
    signin_metrics::ProfileSignout::kForceSignoutAlwaysAllowedForTest,
    // Allowed, because the dialog offers the option to the user to sign out.
    // Note that the dialog is only shown on iOS and isn't planned to be shown
    // on the other platforms since they already support user policies (no need
    // for a notification in that case). Still, the metric is added to the
    // kAlwaysAllowedSignoutSources for coherence.
    signin_metrics::ProfileSignout::
        kUserClickedSignoutFromUserPolicyNotificationDialog,
    // Allowed, because the profile was signed out and the account was signed in
    // to the web only before showing the sync confirmation dialog. The account
    // was signed in to the profile in order to show the sync confirmation.
    signin_metrics::ProfileSignout::kCancelSyncConfirmationOnWebOnlySignedIn,
    // Allowed as the user wasn't signed in initially and data has not been
    // synced yet.
    signin_metrics::ProfileSignout::kCancelSyncConfirmationRemoveAccount,
    // Data not synced yet.
    // Used when moving the primary account (e.g. profile switch).
    signin_metrics::ProfileSignout::kMovePrimaryAccount,
    // Allowed as the profile is being deleted anyway.
    signin_metrics::ProfileSignout::kSignoutDuringProfileDeletion,
    // Allowed as the user declined the enterprise management disclaimer.
    signin_metrics::ProfileSignout::kUserDeclinedEnterpriseManagementDisclaimer,
    // Allowed as the user declined the enterprise signals disclaimer.
    signin_metrics::ProfileSignout::kUserDeclinedEnterpriseSignalsDisclaimer,
};

// Returns the HaTS survey trigger corresponding to the given AccessPoint, or
// the empty string if there is no such survey.
std::string HatsSurveyTriggerForAccessPoint(
    signin_metrics::AccessPoint access_point) {
  switch (access_point) {
#if BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
    case signin_metrics::AccessPoint::kAddressBubble:
      return kHatsSurveyTriggerIdentityAddressBubbleSignin;
    case signin_metrics::AccessPoint::kAvatarBubbleSignIn:
    case signin_metrics::AccessPoint::kAvatarBubbleSignInWithSyncPromo:
      return kHatsSurveyTriggerIdentityProfileMenuSignin;
    case signin_metrics::AccessPoint::kForYouFre:
      return kHatsSurveyTriggerIdentityFirstRunSignin;
    case signin_metrics::AccessPoint::kPasswordBubble:
      return kHatsSurveyTriggerIdentityPasswordBubbleSignin;
    case signin_metrics::AccessPoint::kSigninInterceptFirstRunExperience:
      return kHatsSurveyTriggerIdentitySigninInterceptProfileSeparation;
    case signin_metrics::AccessPoint::kUserManager:
      return kHatsSurveyTriggerIdentityProfilePickerAddProfileSignin;
#endif  // BUILDFLAG(IS_MAC) || BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_WIN)
    default:
      // No HaTS survey is defined for the rest of the access point.
      return std::string();
  }
}

class ChromeOAuthConsumerRegistry : public signin::OAuthConsumerRegistry {
 protected:
  signin::OAuthConsumer GetOAuthConsumerForGlicUserStatus() const override {
    CHECK(base::FeatureList::IsEnabled(features::kGlicUserStatusCheck));
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kGlicUserStatusName,
        {features::kGeminiOAuth2Scope.Get()});
  }

  signin::OAuthConsumer GetOAuthConsumerForGlicInvokeApi() const override {
#if BUILDFLAG(ENABLE_EXTENSIONS)
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kGlicInvokeApiName,
        {extensions_features::kGlicInvokeApiOAuth2ScopeParam.Get()});
#else
    NOTREACHED();
#endif
  }

  signin::OAuthConsumer GetOAuthConsumerForSkillsService() const override {
    CHECK(base::FeatureList::IsEnabled(features::kSkillsEnabled));
    CHECK(base::FeatureList::IsEnabled(features::kSkillsServiceApi));
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kSkillsServiceName,
        {features::kSkillsServiceApiOAuth2Scope.Get()});
  }

  signin::OAuthConsumer GetOAuthConsumerForContextualTasks() const override {
    CHECK(contextual_tasks::IsContextualTasksUIEnabled());
    signin::ScopeSet scopes = {
        GaiaConstants::kSearchResultsOAuth2Scope,
        kCalendarEventsOAuth2Scope,
        kCalendarFreeBusyOAuth2Scope,
        kCalendarListOAuth2Scope,
        GaiaConstants::kClearCutOAuth2Scope,
        kDocumentsOAuth2Scope,
        GaiaConstants::kDriveOAuth2Scope,
        kGmailModifyOAuth2Scope,
        GaiaConstants::kLensOAuth2Scope,
        kPeopleReadOnlyOAuth2Scope,
        kSpreadsheetsOAuth2Scope,
    };
    if (base::FeatureList::IsEnabled(
            contextual_tasks::kContextualTasksExtraOauthScopes)) {
      std::string extra_scopes_str =
          contextual_tasks::kContextualTasksOAuthScopes.Get();
      std::vector<std::string> extra_scopes_vec =
          base::SplitString(extra_scopes_str, ",", base::TRIM_WHITESPACE,
                            base::SPLIT_WANT_NONEMPTY);
      for (const std::string& extra_scope : extra_scopes_vec) {
        scopes.insert(extra_scope);
      }
    }
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kContextualTasksName, std::move(scopes));
  }

  signin::OAuthConsumer GetOAuthConsumerForDrivePickerHost() const override {
    if (base::FeatureList::IsEnabled(omnibox::kDrivePickerV2Scope)) {
      return signin::OAuthConsumer(
          signin::oauth_consumer_name::kDrivePickerHostName,
          {"https://www.googleapis.com/auth/drive.file"});
    }
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kDrivePickerHostName,
        {"https://www.googleapis.com/auth/drive.readonly"});
  }

  signin::OAuthConsumer GetOAuthConsumerForIndigo() const override {
    CHECK(base::FeatureList::IsEnabled(features::kIndigo));
    std::string scopes_str = features::kIndigoScopes.Get();
    std::vector<std::string> scopes_vec = base::SplitString(
        scopes_str, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
    signin::ScopeSet scopes(scopes_vec.begin(), scopes_vec.end());
    return signin::OAuthConsumer(signin::oauth_consumer_name::kIndigoName,
                                 std::move(scopes));
  }

  signin::OAuthConsumer GetOAuthConsumerForBrowserActuator() const override {
    CHECK(base::FeatureList::IsEnabled(browser_actuator::kBrowserActuator));

    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kBrowserActuatorName,
        {browser_actuator::kBrowserActuatorOAuth2ScopeParam.Get()});
  }

  signin::OAuthConsumer GetOAuthConsumerForSiteTokenProvider() const override {
    return signin::OAuthConsumer(
        signin::oauth_consumer_name::kSiteTokenProviderName,
        {site_token_provider::features::kSiteTokenOAuth2Scope.Get()});
  }
};

}  // namespace

ChromeSigninClient::ChromeSigninClient(Profile* profile)
    : wait_for_network_callback_helper_(
#if BUILDFLAG(IS_CHROMEOS)
          std::make_unique<WaitForNetworkCallbackHelperAsh>()
#else
          std::make_unique<WaitForNetworkCallbackHelperChrome>(
              profile->AsTestingProfile())
#endif
              ),
      profile_(profile),
      oauth_consumer_registry_(
          std::make_unique<ChromeOAuthConsumerRegistry>()) {
}

ChromeSigninClient::~ChromeSigninClient() = default;

void ChromeSigninClient::DoFinalInit() {
  VerifySyncToken();
}

// static
bool ChromeSigninClient::ProfileAllowsSigninCookies(Profile* profile) {
  scoped_refptr<content_settings::CookieSettings> cookie_settings =
      CookieSettingsFactory::GetForProfile(profile);
  return signin::SettingsAllowSigninCookies(cookie_settings.get());
}

PrefService* ChromeSigninClient::GetPrefs() {
  return profile_->GetPrefs();
}

scoped_refptr<network::SharedURLLoaderFactory>
ChromeSigninClient::GetURLLoaderFactory() {
  if (url_loader_factory_for_testing_) {
    return url_loader_factory_for_testing_;
  }

  return profile_->GetDefaultStoragePartition()
      ->GetURLLoaderFactoryForBrowserProcess();
}

network::mojom::CookieManager* ChromeSigninClient::GetCookieManager() {
  return profile_->GetDefaultStoragePartition()
      ->GetCookieManagerForBrowserProcess();
}

network::mojom::DeviceBoundSessionManager*
ChromeSigninClient::GetDeviceBoundSessionManager() const {
  return profile_->GetDefaultStoragePartition()->GetDeviceBoundSessionManager();
}

network::mojom::NetworkContext* ChromeSigninClient::GetNetworkContext() {
  return profile_->GetDefaultStoragePartition()->GetNetworkContext();
}

bool ChromeSigninClient::AreSigninCookiesAllowed() {
  return ProfileAllowsSigninCookies(profile_);
}

bool ChromeSigninClient::AreSigninCookiesDeletedOnExit() {
  scoped_refptr<content_settings::CookieSettings> cookie_settings =
      CookieSettingsFactory::GetForProfile(profile_);
  return signin::SettingsDeleteSigninCookiesOnExit(cookie_settings.get());
}

void ChromeSigninClient::AddContentSettingsObserver(
    content_settings::Observer* observer) {
  HostContentSettingsMapFactory::GetForProfile(profile_)->AddObserver(observer);
}

void ChromeSigninClient::RemoveContentSettingsObserver(
    content_settings::Observer* observer) {
  HostContentSettingsMapFactory::GetForProfile(profile_)->RemoveObserver(
      observer);
}

bool ChromeSigninClient::IsClearPrimaryAccountAllowed() const {
  return GetSignoutDecision(
             /*signout_source=*/std::nullopt) ==
         SigninClient::SignoutDecision::ALLOW;
}

void ChromeSigninClient::PreSignOut(
    base::OnceCallback<void(SignoutDecision)> on_signout_decision_reached,
    signin_metrics::ProfileSignout signout_source_metric) {
  DCHECK(on_signout_decision_reached);
  DCHECK(!on_signout_decision_reached_) << "SignOut already in-progress!";
  on_signout_decision_reached_ = std::move(on_signout_decision_reached);

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
  // `signout_source_metric` is `signin_metrics::ProfileSignout::kAbortSignin`
  // if the user declines sync in the signin process. In case the user accepts
  // the managed account but declines sync, we should keep the window open.
  // `signout_source_metric` is
  // `signin_metrics::ProfileSignout::kRevokeSyncFromSettings` when the user
  // turns off sync from the settings, we should also keep the window open at
  // this point.
  bool user_declines_sync_after_consenting_to_management =
      (signout_source_metric == signin_metrics::ProfileSignout::kAbortSignin ||
       signout_source_metric ==
           signin_metrics::ProfileSignout::kRevokeSyncFromSettings ||
       signout_source_metric == signin_metrics::ProfileSignout::
                                    kCancelSyncConfirmationOnWebOnlySignedIn) &&
      enterprise_util::UserAcceptedAccountManagement(profile_);
  // These sign out won't remove the policy cache, keep the window opened.
  bool keep_window_opened =
      signout_source_metric ==
          signin_metrics::ProfileSignout::kGoogleServiceNamePatternChanged ||
      signout_source_metric ==
          signin_metrics::ProfileSignout::kServerForcedDisable ||
      signout_source_metric == signin_metrics::ProfileSignout::kPrefChanged ||
      user_declines_sync_after_consenting_to_management;
  if (signin_util::IsForceSigninEnabled() && !profile_->IsSystemProfile() &&
      !profile_->IsGuestSession() && !profile_->IsChild() &&
      !keep_window_opened) {
    chrome::CloseAllBrowsersWithProfile(
        profile_,
        signout_source_metric == signin_metrics::ProfileSignout::kAbortSignin ||
            signout_source_metric == signin_metrics::ProfileSignout::
                                         kAuthenticationFailedWithForceSignin ||
            signout_source_metric ==
                signin_metrics::ProfileSignout::
                    kCancelSyncConfirmationOnWebOnlySignedIn,
        base::BindRepeating(&ChromeSigninClient::OnCloseBrowsersSuccess,
                            base::Unretained(this), signout_source_metric,
                            /*should_sign_out=*/true),
        base::BindRepeating(&ChromeSigninClient::OnCloseBrowsersAborted,
                            base::Unretained(this)));
  } else {
#else
  {
#endif
    std::move(on_signout_decision_reached_)
        .Run(GetSignoutDecision(signout_source_metric));
  }
}

bool ChromeSigninClient::AreNetworkCallsDelayed() {
  return wait_for_network_callback_helper_->AreNetworkCallsDelayed();
}

void ChromeSigninClient::DelayNetworkCall(base::OnceClosure callback) {
  wait_for_network_callback_helper_->DelayNetworkCall(std::move(callback));
}

std::unique_ptr<GaiaAuthFetcher> ChromeSigninClient::CreateGaiaAuthFetcher(
    GaiaAuthConsumer* consumer,
    gaia::GaiaSource source) {
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
  if (BoundSessionCookieRefreshService* bound_session_cookie_refresh_service =
          BoundSessionCookieRefreshServiceFactory::GetForProfile(profile_);
      bound_session_cookie_refresh_service) {
    return std::make_unique<ThrottledGaiaAuthFetcher>(
        consumer, source, GetURLLoaderFactory(),
        bound_session_cookie_refresh_service->GetBoundSessionThrottlerParams(),
        std::make_unique<BoundSessionRequestThrottledHandlerBrowserImpl>(
            *bound_session_cookie_refresh_service));
  }
#endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
  return std::make_unique<GaiaAuthFetcher>(consumer, source,
                                           GetURLLoaderFactory());
}

version_info::Channel ChromeSigninClient::GetClientChannel() {
  return chrome::GetChannel();
}

void ChromeSigninClient::OnPrimaryAccountChanged(
    signin::PrimaryAccountChangeEvent event_details) {
  for (signin::ConsentLevel consent_level :
       {signin::ConsentLevel::kSignin, signin::ConsentLevel::kSync}) {
    // Only record metrics when setting the primary account.
    switch (event_details.GetEventTypeFor(consent_level)) {
      case signin::PrimaryAccountChangeEvent::Type::kNone:
      case signin::PrimaryAccountChangeEvent::Type::kCleared:
        break;
      case signin::PrimaryAccountChangeEvent::Type::kSet:
        CHECK(event_details.GetSetPrimaryAccountAccessPoint().has_value());
        signin_metrics::AccessPoint access_point =
            event_details.GetSetPrimaryAccountAccessPoint().value();

        if (consent_level == signin::ConsentLevel::kSignin) {
          std::string trigger = HatsSurveyTriggerForAccessPoint(access_point);
          signin::LaunchHatsSurveyForProfile(trigger, profile_,
                                             /*defer_if_no_browser=*/true);
        }

#if !BUILDFLAG(IS_CHROMEOS)
        RecordOpenTabCount(access_point, consent_level);
#endif

        break;
    }
  }
}

std::unique_ptr<signin::BoundSessionOAuthMultiLoginDelegate>
ChromeSigninClient::CreateBoundSessionOAuthMultiloginDelegate() const {
#if BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
  BoundSessionCookieRefreshService* bound_session_cookie_refresh_service =
      BoundSessionCookieRefreshServiceFactory::GetForProfile(profile_);
  if (bound_session_cookie_refresh_service) {
    return std::make_unique<BoundSessionOAuthMultiLoginDelegateImpl>(
        bound_session_cookie_refresh_service->GetWeakPtr(),
        IdentityManagerFactory::GetForProfile(profile_));
  }
#endif  // BUILDFLAG(ENABLE_BOUND_SESSION_CREDENTIALS)
  return nullptr;
}

signin::OAuthConsumer ChromeSigninClient::GetOAuthConsumerFromId(
    signin::OAuthConsumerId oauth_consumer_id) const {
  return oauth_consumer_registry_->GetOAuthConsumerFromId(oauth_consumer_id);
}

SigninClient::SignoutDecision ChromeSigninClient::GetSignoutDecision(
    const std::optional<signin_metrics::ProfileSignout> signout_source) const {
  // TODO(crbug.com/40239707): Revisit |kAlwaysAllowedSignoutSources|.
  for (const auto& always_allowed_source : kAlwaysAllowedSignoutSources) {
    if (!signout_source.has_value()) {
      break;
    }
    if (signout_source.value() == always_allowed_source) {
      return SigninClient::SignoutDecision::ALLOW;
    }
  }

  if (is_clear_primary_account_allowed_for_testing_.has_value()) {
    return is_clear_primary_account_allowed_for_testing_.value();
  }

#if BUILDFLAG(IS_ANDROID)
  // On Android we do not allow supervised users to sign out.
  // We also don't allow sign out on ChromeOS, though this is enforced outside
  // the scope of this method.
  // Other platforms do not restrict signout of supervised users.
  if (profile_->IsChild()) {
    return SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED;
  }
#endif

// Android allows signing out of Managed accounts.
#if !BUILDFLAG(IS_ANDROID)
  // Check if managed user.
  if (enterprise_util::UserAcceptedAccountManagement(profile_)) {
    // Disallow signout regardless of consent level of the primary account.
    return SigninClient::SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED;
  }
#endif
  return SigninClient::SignoutDecision::ALLOW;
}

void ChromeSigninClient::VerifySyncToken() {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
  // We only verify the token once when Profile is just created.
  if (signin_util::IsForceSigninEnabled() && !force_signin_verifier_) {
    force_signin_verifier_ = std::make_unique<ForceSigninVerifier>(
        profile_, IdentityManagerFactory::GetForProfile(profile_),
        base::BindOnce(&ChromeSigninClient::OnTokenFetchComplete,
                       base::Unretained(this)));
  }
#endif
}

#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
void ChromeSigninClient::OnTokenFetchComplete(bool token_is_valid) {
  // If the token is valid we do need to do anything special and let the user
  // proceed.
  if (token_is_valid) {
    return;
  }

  // Token is not valid, we close all the browsers and open the Profile
  // Picker.
  should_display_user_manager_ = true;
  chrome::CloseAllBrowsersWithProfile(
      profile_,
      /*skip_beforeunload=*/true,
      base::BindRepeating(
          &ChromeSigninClient::OnCloseBrowsersSuccess, base::Unretained(this),
          signin_metrics::ProfileSignout::kAuthenticationFailedWithForceSignin,
          // Do not sign the user out to allow them to reauthenticate from the
          // profile picker.
          /*should_sign_out=*/false));
}
#endif

#if !BUILDFLAG(IS_CHROMEOS)
void ChromeSigninClient::RecordOpenTabCount(
    signin_metrics::AccessPoint access_point,
    signin::ConsentLevel consent_level) {
  size_t tabs_count = 0;

#if BUILDFLAG(IS_ANDROID)
  for (const TabModel* model : TabModelList::models()) {
    // Note: Even though on Android only a single regular profile is supported,
    // there can also be an incognito profile which should be excluded here.
    if (model->GetProfile() != profile_) {
      continue;
    }

    tabs_count += model->GetTabCount();
  }
#else   // !BUILDFLAG(IS_ANDROID)
  ForEachCurrentBrowserWindowInterfaceOrderedByActivation(
      [this, &tabs_count](BrowserWindowInterface* browser) {
        if (browser->GetProfile() != profile_) {
          return true;
        }
        if (TabStripModel* const tab_strip_model =
                browser->GetTabStripModel()) {
          tabs_count += tab_strip_model->count();
        }
        return true;
      });
#endif  // !BUILDFLAG(IS_ANDROID)

  signin_metrics::RecordOpenTabCountOnSignin(consent_level, tabs_count);
}
#endif  // !BUILDFLAG(IS_CHROMEOS)

void ChromeSigninClient::SetURLLoaderFactoryForTest(
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
  url_loader_factory_for_testing_ = url_loader_factory;
#if BUILDFLAG(IS_CHROMEOS)
  // Do not make network requests in unit tests. ash::NetworkHandler should
  // not be used and is not expected to have been initialized in unit tests.
  wait_for_network_callback_helper_
      ->DisableNetworkCallsDelayedForTesting(  // IN-TEST
          url_loader_factory_for_testing_ &&
          !ash::NetworkHandler::IsInitialized());
#endif  // BUILDFLAG(IS_CHROMEOS)
}

void ChromeSigninClient::OnCloseBrowsersSuccess(
    const signin_metrics::ProfileSignout signout_source_metric,
    bool should_sign_out,
    const base::FilePath& profile_path) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
  if (signin_util::IsForceSigninEnabled() && force_signin_verifier_.get()) {
    force_signin_verifier_->Cancel();
  }
#endif

  if (should_sign_out) {
    std::move(on_signout_decision_reached_)
        .Run(GetSignoutDecision(signout_source_metric));
  }

  LockForceSigninProfile(profile_path);
  // After sign out, lock the profile and show UserManager if necessary.
  if (should_display_user_manager_) {
    ShowUserManager(profile_path);
  } else {
    should_display_user_manager_ = true;
  }
}

void ChromeSigninClient::OnCloseBrowsersAborted(
    const base::FilePath& profile_path) {
  should_display_user_manager_ = true;

  // Disallow sign-out (aborted).
  std::move(on_signout_decision_reached_)
      .Run(SignoutDecision::CLEAR_PRIMARY_ACCOUNT_DISALLOWED);
}

void ChromeSigninClient::LockForceSigninProfile(
    const base::FilePath& profile_path) {
  ProfileAttributesEntry* entry =
      g_browser_process->profile_manager()
          ->GetProfileAttributesStorage()
          .GetProfileAttributesWithPath(profile_->GetPath());
  if (!entry) {
    return;
  }
  entry->LockForceSigninProfile(true);
}

void ChromeSigninClient::ShowUserManager(const base::FilePath& profile_path) {
#if !BUILDFLAG(IS_ANDROID) && !BUILDFLAG(IS_CHROMEOS)
  ProfilePicker::Show(ProfilePicker::Params::FromEntryPoint(
      ProfilePicker::EntryPoint::kProfileLocked));
#endif
}
