chromium/chrome/browser/extensions/api/identity/identity_apitest.cc

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

#include <memory>
#include <optional>
#include <set>
#include <string>
#include <utility>
#include <vector>

#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/memory/ptr_util.h"
#include "base/memory/raw_ptr.h"
#include "base/memory/scoped_refptr.h"
#include "base/notreached.h"
#include "base/run_loop.h"
#include "base/strings/strcat.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/test/bind.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/test/test_future.h"
#include "base/test/values_test_util.h"
#include "base/values.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "build/chromeos_buildflags.h"
#include "chrome/browser/extensions/api/identity/gaia_remote_consent_flow.h"
#include "chrome/browser/extensions/api/identity/identity_api.h"
#include "chrome/browser/extensions/api/identity/identity_constants.h"
#include "chrome/browser/extensions/api/identity/identity_get_accounts_function.h"
#include "chrome/browser/extensions/api/identity/identity_get_auth_token_error.h"
#include "chrome/browser/extensions/api/identity/identity_get_auth_token_function.h"
#include "chrome/browser/extensions/api/identity/identity_get_profile_user_info_function.h"
#include "chrome/browser/extensions/api/identity/identity_launch_web_auth_flow_function.h"
#include "chrome/browser/extensions/api/identity/identity_remove_cached_auth_token_function.h"
#include "chrome/browser/extensions/api/identity/launch_web_auth_flow_delegate.h"
#include "chrome/browser/extensions/component_loader.h"
#include "chrome/browser/extensions/extension_apitest.h"
#include "chrome/browser/extensions/extension_browsertest.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/signin/account_consistency_mode_manager.h"
#include "chrome/browser/signin/account_reconcilor_factory.h"
#include "chrome/browser/signin/chrome_signin_client_factory.h"
#include "chrome/browser/signin/chrome_signin_client_test_util.h"
#include "chrome/browser/signin/identity_manager_factory.h"
#include "chrome/browser/signin/identity_test_environment_profile_adaptor.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_finder.h"
#include "chrome/browser/ui/browser_window.h"
#include "chrome/common/chrome_features.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/extensions/api/identity.h"
#include "chrome/common/pref_names.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "components/crx_file/id_util.h"
#include "components/guest_view/browser/guest_view_base.h"
#include "components/guest_view/browser/guest_view_manager_delegate.h"
#include "components/guest_view/browser/guest_view_manager_factory.h"
#include "components/guest_view/browser/test_guest_view_manager.h"
#include "components/prefs/pref_service.h"
#include "components/signin/core/browser/account_reconcilor.h"
#include "components/signin/public/base/consent_level.h"
#include "components/signin/public/base/list_accounts_test_utils.h"
#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_mutator.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "components/signin/public/identity_manager/identity_test_utils.h"
#include "content/public/browser/storage_partition.h"
#include "content/public/browser/web_contents.h"
#include "content/public/test/browser_test.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/test_navigation_observer.h"
#include "content/public/test/test_utils.h"
#include "extensions/browser/api/extensions_api_client.h"
#include "extensions/browser/api_test_utils.h"
#include "extensions/browser/pref_names.h"
#include "extensions/common/api/oauth2.h"
#include "extensions/common/extension_builder.h"
#include "extensions/common/extension_features.h"
#include "extensions/common/extension_id.h"
#include "extensions/common/manifest_handlers/oauth2_manifest_handler.h"
#include "google_apis/gaia/oauth2_mint_token_flow.h"
#include "net/cookies/cookie_util.h"
#include "net/test/embedded_test_server/embedded_test_server.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
#include "services/network/public/mojom/cookie_manager.mojom.h"
#include "services/network/test/test_url_loader_factory.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/idle/idle.h"
#include "ui/base/idle/scoped_set_idle_state.h"
#include "ui/gfx/geometry/rect.h"
#include "ui/views/widget/any_widget_observer.h"
#include "ui/views/window/dialog_delegate.h"
#include "url/gurl.h"

#if BUILDFLAG(IS_CHROMEOS_ASH)
#include "chrome/browser/ash/login/users/fake_chrome_user_manager.h"
#include "chrome/browser/ash/net/network_portal_detector_test_impl.h"
#include "chromeos/ash/components/install_attributes/stub_install_attributes.h"
#include "chromeos/ash/components/login/login_state/login_state.h"
#include "chromeos/ash/components/network/network_handler.h"
#include "chromeos/ash/components/network/network_state.h"
#include "chromeos/ash/components/network/network_state_handler.h"
#include "components/user_manager/scoped_user_manager.h"
#endif

#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "chrome/browser/signin/signin_util.h"
#include "chromeos/crosapi/mojom/crosapi.mojom.h"
#include "chromeos/startup/browser_init_params.h"
#endif

ExtensionsAPIClient;
_;
Return;

namespace extensions {

namespace {

errors;
utils;

OAuth2Info;

const char kAccessToken[] =;
const char kExtensionId[] =;

const char kGetAuthTokenResultHistogramName[] =;
const char kGetAuthTokenResultAfterConsentApprovedHistogramName[] =;

const char kLaunchWebAuthFlowResultHistogramName[] =;

#if BUILDFLAG(IS_CHROMEOS_ASH)
void InitNetwork() {
  const ash::NetworkState* default_network =
      ash::NetworkHandler::Get()->network_state_handler()->DefaultNetwork();

  auto* portal_detector = new ash::NetworkPortalDetectorTestImpl();
  portal_detector->SetDefaultNetworkForTesting(default_network->guid());

  ash::network_portal_detector::InitializeForTesting(portal_detector);
}
#endif

// Asynchronous function runner allows tests to manipulate the browser window
// after the call happens.
class AsyncFunctionRunner {};

class AsyncExtensionBrowserTest : public ExtensionBrowserTest {};

class TestHangOAuth2MintTokenFlow : public OAuth2MintTokenFlow {};

class TestOAuth2MintTokenFlow : public OAuth2MintTokenFlow {};

std::unique_ptr<net::EmbeddedTestServer> LaunchHttpsServer() {}

scoped_refptr<IdentityLaunchWebAuthFlowFunction>
CreateLaunchWebAuthFlowFunction() {}

// Simulate a redirects to the expected url from the Chrome extension API via
// `EvalJS`.
// Expecting it to work with
// "chrome/test/data/extensions/api_test/identity/consent_page.html" web
// page loaded in the `auth_web_contents`.
// `url_prefix` is used to determine the redirect link, it will match the
// pattern: "https://%s.chromiumapp.org/".
void SimulateUrlRedirect(const std::string& url_prefix,
                         content::WebContents* auth_web_contents) {}

// Similar to SimulateUrlRedirect, but uses provided url instead of the pattern
void SimulateCustomUrlRedirect(const std::string& redirect_url,
                               content::WebContents* auth_web_contents) {}

}  // namespace

class FakeGetAuthTokenFunction : public IdentityGetAuthTokenFunction {};

class MockQueuedMintRequest : public IdentityMintRequestQueue::Request {};

class IdentityTestWithSignin : public AsyncExtensionBrowserTest {};

class IdentityGetAccountsFunctionTest : public IdentityTestWithSignin {};

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, AllAccountsOn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoneSignedIn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, NoPrimaryAccount) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest,
                       PrimaryAccountHasInvalidRefreshToken) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest,
                       PrimaryAccountSignedIn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, TwoAccountsSignedIn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetAccountsFunctionTest, SignedInOnTheWeb) {}

class IdentityGetProfileUserInfoFunctionTest : public IdentityTestWithSignin {};

IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest, NotSignedIn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest, SignedIn) {}

IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest,
                       SignedInUnconsented) {}

IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest,
                       NotSignedInNoEmail) {}

IN_PROC_BROWSER_TEST_F(IdentityGetProfileUserInfoFunctionTest,
                       SignedInNoEmail) {}

class IdentityGetProfileUserInfoFunctionTestWithAccountStatusParam
    : public IdentityGetProfileUserInfoFunctionTest,
      public ::testing::WithParamInterface<std::string> {};

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_P(
    IdentityGetProfileUserInfoFunctionTestWithAccountStatusParam,
    NotSignedIn) {}

IN_PROC_BROWSER_TEST_P(
    IdentityGetProfileUserInfoFunctionTestWithAccountStatusParam,
    SignedIn) {}

IN_PROC_BROWSER_TEST_P(
    IdentityGetProfileUserInfoFunctionTestWithAccountStatusParam,
    SignedInUnconsented) {}

class GetAuthTokenFunctionTest
    : public IdentityTestWithSignin,
      public signin::IdentityManager::DiagnosticsObserver {};

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoClientId) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoScopes) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveNotSignedIn) {}

// The signin flow is simply not used on ChromeOS.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveNotSignedInShowSigninOnlyOnce) {}

// Signin is always allowed on Lacros.
#if !BUILDFLAG(IS_CHROMEOS_LACROS)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       PRE_InteractiveNotSignedAndSigninNotAllowed) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveNotSignedAndSigninNotAllowed) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_LACROS)
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveMintFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveLoginAccessTokenFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveRemoteConsentSuccess) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveMintBadCredentials) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveMintServiceError) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveMintServiceErrorAccountValid) {}

// The signin flow is simply not used on Ash.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveMintServiceErrorShowSigninOnlyOnce) {}
#endif

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveSuccess) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveLoginCanceled) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveMintBadCredentialsAccountValid) {}

// The interactive login flow is always short-circuited out with failure on
// Ash, so the tests of the interactive login flow being successful are not
// relevant on that platform.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveLoginSuccessMintFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveLoginSuccessMintBadCredentials) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveLoginSuccessLoginAccessTokenFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveLoginSuccessMintSuccess) {}

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, SignedInWebOnlyAcceptPrompt) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, SignedInWebOnlyDeclinePrompt) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       SignedInWebOnlyAcceptPromptMultipleFunctions) {}
#endif

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveLoginSuccessApprovalAborted) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

class GetAuthTokenFunctionInteractivityTest
    : public GetAuthTokenFunctionTest,
      public testing::WithParamInterface<
          IdentityGetAuthTokenFunction::InteractivityStatus> {};

// The interactive login flow is always short-circuited out with failure on
// Ash, so the tests of the interactive login flow being successful are not
// relevant on that platform.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_P(GetAuthTokenFunctionInteractivityTest,
                       SigninInteractivityTest) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

IN_PROC_BROWSER_TEST_P(GetAuthTokenFunctionInteractivityTest,
                       ConsentInteractivityTest) {}

INSTANTIATE_TEST_SUITE_P();

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalAborted) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveApprovalLoadFailed) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveApprovalInvalidConsentResult) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveApprovalNoGrant) {}

// The signin flow is simply not used on Ash.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveApprovalNoGrantShowSigninUIOnlyOnce) {}
#endif

#if !BUILDFLAG(IS_MAC)
// Test was originally written for http://crbug.com/753014 and subsequently
// modified to use the remote consent flow.
//
// On macOS, closing all browsers does not shut down the browser process.
// TODO(http://crbug.com/756462): Figure out how to shut down the browser
// process on macOS and enable this test on macOS as well.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveSigninFailedDuringBrowserProcessShutDown) {}
#endif  // !BUILDFLAG(IS_MAC)

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveQueue) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueue) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveQueueShutdown) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NoninteractiveShutdown) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       InteractiveQueuedNoninteractiveFails) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, NonInteractiveCacheHit) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveCacheHitSecondary) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveRemoteConsentCacheHit) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       NonInteractiveRemoteConsentApprovedCacheHit) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, InteractiveCacheHit) {}

// The interactive login UI is never shown on Ash, so tests of the interactive
// login flow being successful are not relevant on that platform.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, LoginInvalidatesTokenCache) {}
#endif

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithChromeClientId) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ComponentWithNormalClientId) {}

// Ensure that IdentityAPI shutdown triggers an active function call to return
// with an error.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, IdentityAPIShutdown) {}

// Ensure that when there are multiple active function calls, IdentityAPI
// shutdown triggers them all to return with errors.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       IdentityAPIShutdownWithMultipleActiveTokenRequests) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ManuallyIssueToken) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ManuallyIssueTokenFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiDefaultUserManuallyIssueToken) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiPrimaryUserManuallyIssueToken) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryUserManuallyIssueToken) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiUnknownUserGetTokenFromTokenServiceFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryNonInteractiveMintFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryNonInteractiveLoginAccessTokenFailure) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryInteractiveApprovalAborted) {}

// Tests that Chrome remembers user's choice of an account at the end of the
// remote consent flow. Chrome should reuse this account in the next
// getAuthToken() call for the same extension.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryInteractiveRemoteConsent) {}

// Tests two concurrent remote consent flows. Both of them should succeed.
// The second flow starts while the first one is blocked on an interactive mint
// token flow. This is a regression test for https://crbug.com/1091423.
IN_PROC_BROWSER_TEST_F(
    GetAuthTokenFunctionTest,
    RemoteConsentMultipleActiveRequests_BlockedOnInteractive) {}

// Tests two concurrent remote consent flows. Both of them should succeed.
// The second flow starts while the first one is blocked on a non-interactive
// mint token flow. This is a regression test for https://crbug.com/1091423.
IN_PROC_BROWSER_TEST_F(
    GetAuthTokenFunctionTest,
    RemoteConsentMultipleActiveRequests_BlockedOnNoninteractive) {}

// The signin flow is simply not used on Ash.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest,
                       MultiSecondaryInteractiveInvalidToken) {}
#endif

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesDefault) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmpty) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmail) {}

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, ScopesEmailFooBar) {}

// Ensure that the returned scopes from the function is the cached scopes and
// not the requested scopes.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, SubsetMatchCacheHit) {}

// Ensure that the newly cached token uses the granted scopes and not the
// requested scopes.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, SubsetMatchCachePopulate) {}

// Ensure that the scopes returned by the function reflects the granted scopes
// and not the requested scopes.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionTest, GranularPermissionsResponse) {}

#if BUILDFLAG(IS_CHROMEOS)
enum class DeviceLocalAccountSessionType { kPublic, kAppKiosk, kWebKiosk };

#if BUILDFLAG(IS_CHROMEOS_ASH)
class GetAuthTokenFunctionDeviceLocalAccountTestPlatformHelper {
 public:
  const AccountId kFakeAccountId = AccountId::FromUserEmail("test@test");

  explicit GetAuthTokenFunctionDeviceLocalAccountTestPlatformHelper(
      DeviceLocalAccountSessionType session_type)
      : session_type_(session_type) {}

  void SetUpOnMainThread() {
    ash::LoginState::Get()->SetLoggedInState(
        ash::LoginState::LoggedInState::LOGGED_IN_ACTIVE,
        session_type_ == DeviceLocalAccountSessionType::kPublic
            ? ash::LoginState::LoggedInUserType::LOGGED_IN_USER_PUBLIC_ACCOUNT
            : ash::LoginState::LoggedInUserType::LOGGED_IN_USER_KIOSK);
    auto user_manager = std::make_unique<ash::FakeChromeUserManager>();
    user_manager::User* user = nullptr;
    switch (session_type_) {
      case DeviceLocalAccountSessionType::kPublic:
        user = user_manager->AddPublicAccountUser(kFakeAccountId);
        break;
      case DeviceLocalAccountSessionType::kAppKiosk:
        user = user_manager->AddKioskAppUser(kFakeAccountId);
        break;
      case DeviceLocalAccountSessionType::kWebKiosk:
        user = user_manager->AddWebKioskAppUser(kFakeAccountId);
        break;
    }
    ASSERT_TRUE(user);
    user_manager->UserLoggedIn(kFakeAccountId, user->username_hash(),
                               /*browser_restart=*/false, /*is_child=*/false);
    scoped_user_manager_ = std::make_unique<user_manager::ScopedUserManager>(
        std::move(user_manager));
  }

  void TearDownOnMainThread() {
    auto* fake_manager = static_cast<ash::FakeChromeUserManager*>(
        user_manager::UserManager::Get());
    // Explicitly removing the user is required; otherwise ProfileHelper keeps
    // a dangling pointer to the User.
    // TODO(b/208629291): Consider removing all users from ProfileHelper in the
    // destructor of `ash::FakeChromeUserManager`.
    fake_manager->RemoveUserFromList(kFakeAccountId);
    scoped_user_manager_.reset();
  }

 private:
  const DeviceLocalAccountSessionType session_type_;

  // Set up fake install attributes to make the device appeared as
  // enterprise-managed.
  ash::ScopedStubInstallAttributes test_install_attributes_{
      ash::StubInstallAttributes::CreateCloudManaged("example.com", "fake-id")};

  std::unique_ptr<user_manager::ScopedUserManager> scoped_user_manager_;
};
#endif  // BUILDFLAG(IS_CHROMEOS_ASH)

#if BUILDFLAG(IS_CHROMEOS_LACROS)
class GetAuthTokenFunctionDeviceLocalAccountTestPlatformHelper {
 public:
  explicit GetAuthTokenFunctionDeviceLocalAccountTestPlatformHelper(
      DeviceLocalAccountSessionType session_type) {
    crosapi::mojom::SessionType init_params_session_type =
        crosapi::mojom::SessionType::kUnknown;
    switch (session_type) {
      case DeviceLocalAccountSessionType::kPublic:
        init_params_session_type = crosapi::mojom::SessionType::kPublicSession;
        break;
      case DeviceLocalAccountSessionType::kAppKiosk:
        init_params_session_type =
            crosapi::mojom::SessionType::kAppKioskSession;
        break;
      case DeviceLocalAccountSessionType::kWebKiosk:
        init_params_session_type =
            crosapi::mojom::SessionType::kWebKioskSession;
        break;
    }
    crosapi::mojom::BrowserInitParamsPtr init_params =
        crosapi::mojom::BrowserInitParams::New();
    init_params->session_type = init_params_session_type;
    init_params->is_device_enterprised_managed = true;
    init_params->device_settings = crosapi::mojom::DeviceSettings::New();
    chromeos::BrowserInitParams::SetInitParamsForTests(std::move(init_params));
  }

  void SetUpOnMainThread() {}
  void TearDownOnMainThread() {}
};
#endif  // BUILDFLAG(IS_CHROMEOS_LACROS)

class GetAuthTokenFunctionDeviceLocalAccountTest
    : public GetAuthTokenFunctionTest {
 public:
  explicit GetAuthTokenFunctionDeviceLocalAccountTest(
      DeviceLocalAccountSessionType session_type)
      : platform_helper_(session_type) {}

  void SetUpOnMainThread() override {
    platform_helper_.SetUpOnMainThread();
    GetAuthTokenFunctionTest::SetUpOnMainThread();
  }

  void TearDownOnMainThread() override {
    GetAuthTokenFunctionTest::TearDownOnMainThread();
    platform_helper_.TearDownOnMainThread();
  }

 protected:
  void RunExtensionAndVerifyNoError(bool is_extension_allowlisted) {
    scoped_refptr<FakeGetAuthTokenFunction> func(
        new FakeGetAuthTokenFunction());
    std::string extension_id = is_extension_allowlisted
                                   ? "ljacajndfccfgnfohlgkdphmbnpkjflk"
                                   : "test-id";
    func->set_extension(CreateTestExtension(extension_id));
    func->push_mint_token_result(TestOAuth2MintTokenFlow::MINT_TOKEN_SUCCESS);

    std::string access_token;
    std::set<std::string> granted_scopes;
    RunGetAuthTokenFunction(func.get(), "[{}]", browser(), &access_token,
                            &granted_scopes);
    EXPECT_EQ(std::string(kAccessToken), access_token);
    EXPECT_EQ(func->GetExtensionTokenKeyForTest()->scopes, granted_scopes);
    histogram_tester()->ExpectUniqueSample(
        kGetAuthTokenResultHistogramName,
        IdentityGetAuthTokenError::State::kNone, 1);
  }

  scoped_refptr<const Extension> CreateTestExtension(const std::string& id) {
    return ExtensionBuilder("Test")
        .SetManifestKey(
            "oauth2", base::Value::Dict()
                          .Set("client_id", "clientId")
                          .Set("scopes", base::Value::List().Append("scope1")))
        .SetID(id)
        .Build();
  }

  GetAuthTokenFunctionDeviceLocalAccountTestPlatformHelper platform_helper_;
};

class GetAuthTokenFunctionPublicSessionTest
    : public GetAuthTokenFunctionDeviceLocalAccountTest {
 protected:
  GetAuthTokenFunctionPublicSessionTest()
      : GetAuthTokenFunctionDeviceLocalAccountTest(
            DeviceLocalAccountSessionType::kPublic) {}
};

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionPublicSessionTest, NonAllowlisted) {
  // GetAuthToken() should return UserNotSignedIn in public sessions for
  // non-allowlisted extensions.
  scoped_refptr<FakeGetAuthTokenFunction> func(new FakeGetAuthTokenFunction());
  func->set_extension(CreateTestExtension("test-id"));
  std::string error =
      utils::RunFunctionAndReturnError(func.get(), "[]", browser()->profile());
  EXPECT_EQ(std::string(errors::kUserNotSignedIn), error);
  EXPECT_FALSE(func->login_ui_shown());
  EXPECT_FALSE(func->scope_ui_shown());
  histogram_tester()->ExpectUniqueSample(
      kGetAuthTokenResultHistogramName,
      IdentityGetAuthTokenError::State::kNotAllowlistedInPublicSession, 1);
}

class GetAuthTokenFunctionChromeKioskTest
    : public GetAuthTokenFunctionDeviceLocalAccountTest {
 protected:
  GetAuthTokenFunctionChromeKioskTest()
      : GetAuthTokenFunctionDeviceLocalAccountTest(
            DeviceLocalAccountSessionType::kAppKiosk) {}
};

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionChromeKioskTest, NonAllowlisted) {
  // GetAuthToken() should return a token for non-allowlisted extensions in the
  // Chrome Kiosk session.
  RunExtensionAndVerifyNoError(/*is_extension_allowlisted=*/false);
}

class GetAuthTokenFunctionWebKioskTest
    : public GetAuthTokenFunctionDeviceLocalAccountTest {
 protected:
  GetAuthTokenFunctionWebKioskTest()
      : GetAuthTokenFunctionDeviceLocalAccountTest(
            DeviceLocalAccountSessionType::kWebKiosk) {}
};

IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionWebKioskTest, NonAllowlisted) {
  // GetAuthToken() should return a token for non-allowlisted extensions in the
  // web Kiosk session.
  RunExtensionAndVerifyNoError(/*is_extension_allowlisted=*/false);
}

#endif  // BUILDFLAG(IS_CHROMEOS)

// There are two parameters, which are stored in a std::pair, for these tests.
//
// std::string: the GetAuthToken arguments
// bool: the expected value of GetAuthToken's enable_granular_permissions
class GetAuthTokenFunctionEnableGranularPermissionsTest
    : public GetAuthTokenFunctionTest,
      public testing::WithParamInterface<std::pair<std::string, bool>> {};

// Provided with the arguments for GetAuthToken, ensures that GetAuthToken's
// enable_granular_permissions is some expected value when the
// 'ReturnScopesInGetAuthToken' feature flag is enabled.
IN_PROC_BROWSER_TEST_P(GetAuthTokenFunctionEnableGranularPermissionsTest,
                       EnableGranularPermissions) {}

INSTANTIATE_TEST_SUITE_P();

class RemoveCachedAuthTokenFunctionTest : public ExtensionBrowserTest {};

class GetAuthTokenFunctionSelectedUserIdTest : public GetAuthTokenFunctionTest {};

// Tests that Chrome uses the correct selected user id value when a gaia id was
// cached and only the primary account is signed in.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionSelectedUserIdTest, SingleAccount) {}

// Tests that Chrome uses the correct selected user id value when a gaia id was
// cached for a secondary account.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionSelectedUserIdTest,
                       MultipleAccounts) {}

// Tests that Chrome uses the correct selected user id value when a gaia id was
// cached but the extension specifies an account id for a different available
// account.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionSelectedUserIdTest,
                       RequestedAccountAvailable) {}

// The signin flow is not used on Ash.
#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Tests that Chrome does not have any selected user id value if the account
// specified by the extension is not available.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionSelectedUserIdTest,
                       RequestedAccountUnavailable) {}

// Tests that Chrome uses the correct selected user id value after logging into
// the account requested by the extension.
IN_PROC_BROWSER_TEST_F(GetAuthTokenFunctionSelectedUserIdTest,
                       RequestedAccountLogin) {}
#endif

IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NotFound) {}

IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, RemoteConsent) {}

IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, NonMatchingToken) {}

IN_PROC_BROWSER_TEST_F(RemoveCachedAuthTokenFunctionTest, MatchingToken) {}

class LaunchWebAuthFlowFunctionTest : public AsyncExtensionBrowserTest {};

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, InteractionRequired) {}

// Checks that, by default, when a page fully loads in silent mode and doesn't
// redirect, `launchWebAuthFlow()` terminates with an error.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       InteractionRequiredAfterLoad) {}

// Checks that when a page fully loads in silent mode and doesn't redirect,
// `launchWebAuthFlow()` terminates with an error after a specific timeout.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       InteractionRequiredWithTimeout) {}

// Checks that when a page fails to fully load before timeout in silent mode,
// `launchWebAuthFlow()` terminates with an error after a specific timeout.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadTimedOut) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, LoadFailed) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, NonInteractiveSuccess) {}

// Checks that when a page fully loads in silent mode and then performs a
// JavaScript redirect `launchWebAuthFlow()` finishes successfully.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       NonInteractiveSuccessAfterLoad) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       InteractiveFirstNavigationSuccess) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       InteractiveSecondNavigationSuccess) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest, UserCloseWindow) {}

// Regression test for http://b/290733700.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTest,
                       SchemeOtherThanHttpOrHttpsNotAllowed) {}

class LaunchWebAuthFlowFunctionTestWithBrowserTab
    : public LaunchWebAuthFlowFunctionTest {};

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
                       PageNavigateFromInitURLToFinalURL) {}

class TestDelegate : public LaunchWebAuthFlowDelegate {};

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
                       PopupBoundsComeFromDelegate) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
                       PageNavigateFromInitURLToCustomFinalURL) {}

// TODO(crbug.com/40259192): This test should be adapted after the
// implementation of the bug. Multiple TODOs in the test to fix.
IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
                       SimilarExtensionAndArgsShouldGenerateSameFlow) {}

IN_PROC_BROWSER_TEST_F(LaunchWebAuthFlowFunctionTestWithBrowserTab,
                       DifferentExtensionsShouldGenerateDifferentFlows) {}

// TODO(crbug.com/40259192): This test should be adapted after the
// implementation of the bug.
IN_PROC_BROWSER_TEST_F(
    LaunchWebAuthFlowFunctionTestWithBrowserTab,
    ExtensionWithDifferentArgsShouldGenerateDifferentFlowsInAQueue) {}

class ClearAllCachedAuthTokensFunctionTest : public AsyncExtensionBrowserTest {};

IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest,
                       EraseCachedGaiaId) {}

IN_PROC_BROWSER_TEST_F(ClearAllCachedAuthTokensFunctionTest,
                       EraseCachedTokens) {}

class OnSignInChangedEventTest : public IdentityTestWithSignin {};

// Test that an event is fired when the primary account signs in.
IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountSignIn) {}

#if !BUILDFLAG(IS_CHROMEOS_ASH)
// Test that an event is fired when the primary account signs out. Only
// applicable in non-DICE mode, as when DICE is enabled clearing the primary
// account does not result in its refresh token being removed and hence does
// not trigger an event to fire.
IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireOnPrimaryAccountSignOut) {}
#endif  // !BUILDFLAG(IS_CHROMEOS_ASH)

// Test that an event is fired when the primary account has a refresh token
// invalidated.
IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest,
                       FireOnPrimaryAccountRefreshTokenInvalidated) {}

// Test that an event is fired when the primary account has a refresh token
// newly available.
IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest,
                       FireOnPrimaryAccountRefreshTokenAvailable) {}

// Test that an event is fired for changes to a secondary account.
IN_PROC_BROWSER_TEST_F(OnSignInChangedEventTest, FireForSecondaryAccount) {}

// Tests the chrome.identity API implemented by custom JS bindings.
IN_PROC_BROWSER_TEST_F(ExtensionApiTest, ChromeIdentityJsBindings) {}

}  // namespace extensions