910e62b5创建于 1月15日历史提交
// 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.

#ifndef CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_
#define CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_

#include <optional>
#include <string>
#include <utility>

#include "base/callback_list.h"
#include "base/containers/flat_set.h"
#include "base/functional/callback_forward.h"
#include "base/gtest_prod_util.h"
#include "base/memory/raw_ptr.h"
#include "build/build_config.h"
#include "build/buildflag.h"
#include "chrome/browser/extensions/api/identity/identity_get_accounts_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_mint_queue.h"
#include "chrome/browser/extensions/api/identity/identity_token_cache.h"
#include "components/signin/public/base/signin_buildflags.h"
#include "components/signin/public/identity_manager/identity_manager.h"
#include "extensions/browser/browser_context_keyed_api_factory.h"
#include "extensions/browser/event_router.h"
#include "extensions/buildflags/buildflags.h"
#include "google_apis/gaia/gaia_id.h"

#if BUILDFLAG(IS_ANDROID)
#include "chrome/browser/extensions/api/identity/identity_unimplemented_functions_android.h"
#else
#include "chrome/browser/extensions/api/identity/identity_clear_all_cached_auth_tokens_function.h"
#include "chrome/browser/extensions/api/identity/identity_get_auth_token_function.h"
#include "chrome/browser/extensions/api/identity/identity_remove_cached_auth_token_function.h"
#endif

static_assert(BUILDFLAG(ENABLE_EXTENSIONS_CORE));

namespace content {
class BrowserContext;
}

class Profile;

namespace extensions {

class IdentityAPI : public BrowserContextKeyedAPI,
                    public signin::IdentityManager::Observer {
 public:
  explicit IdentityAPI(content::BrowserContext* context);
  ~IdentityAPI() override;

  // Request serialization queue for getAuthToken.
  IdentityMintRequestQueue* mint_queue();

  IdentityTokenCache* token_cache();

  // GAIA id cache.
  void SetGaiaIdForExtension(const std::string& extension_id,
                             const GaiaId& gaia_id);
  // Returns |std::nullopt| if no GAIA id is saved for `extension_id`.
  // Otherwise, returns GAIA id previously saved via SetGaiaIdForExtension().
  std::optional<GaiaId> GetGaiaIdForExtension(const std::string& extension_id);
  void EraseGaiaIdForExtension(const std::string& extension_id);
  // If refresh tokens have been loaded, erases GAIA ids of accounts that are no
  // longer signed in to Chrome for all extensions.
  void EraseStaleGaiaIdsForAllExtensions();

  // BrowserContextKeyedAPI:
  void Shutdown() override;
  static BrowserContextKeyedAPIFactory<IdentityAPI>* GetFactoryInstance();

  base::CallbackListSubscription RegisterOnShutdownCallback(
      base::OnceClosure cb);

  // Callback that is used in testing contexts to test the implementation of
  // the chrome.identity.onSignInChanged event. Note that the passed-in Event is
  // valid only for the duration of the callback.
  using OnSignInChangedCallback = base::RepeatingCallback<void(Event*)>;
  void set_on_signin_changed_callback_for_testing(
      const OnSignInChangedCallback& callback) {
    on_signin_changed_callback_for_testing_ = callback;
  }

  // Whether the chrome.identity API should use all accounts or the primary
  // account only.
  bool AreExtensionsRestrictedToPrimaryAccount();

  // Returns accounts that extension have access to.
  // This returns empty if Chrome is not signed in. Otherwise, returns all
  // accounts in the `identity_manager_`.
  std::vector<CoreAccountInfo> GetAccountsWithRefreshTokensForExtensions();

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
  // Shows the Chrome sign in dialog for extensions if:
  // - The dialog is not already showing
  // - The user is signed in on the web but not to Chrome
  // `on_complete` is guaranteed to be called.
  void MaybeShowChromeSigninDialog(
      const std::u16string& extension_name_for_display,
      base::OnceClosure on_complete);

  // Callback to be called when the tests triggers showing UI.
  // Should be used in unittests.
  void SetSkipUIForTesting(
      base::OnceCallback<void(base::OnceClosure)> callback) {
    skip_ui_for_testing_callback_ = std::move(callback);
  }
#endif

 private:
  friend class BrowserContextKeyedAPIFactory<IdentityAPI>;
  friend class IdentityAPITest;

  // BrowserContextKeyedAPI:
  static const char* service_name() { return "IdentityAPI"; }
  static const bool kServiceIsNULLWhileTesting = true;

  // This constructor allows to mock keyed services in tests.
  IdentityAPI(Profile* profile,
              signin::IdentityManager* identity_manager,
              ExtensionPrefs* extension_prefs,
              EventRouter* event_router);

  // Returns true if extensions have access to Chrome accounts.
  // Access requires Chrome to be signed in.
  bool HasAccessToChromeAccounts() const;

  // signin::IdentityManager::Observer:
  void OnPrimaryAccountChanged(
      const signin::PrimaryAccountChangeEvent& event_details) override;
  void OnRefreshTokensLoaded() override;
  void OnRefreshTokenUpdatedForAccount(
      const CoreAccountInfo& account_info) override;
  // NOTE: This class must listen for this callback rather than
  // OnRefreshTokenRemovedForAccount() to obtain the Gaia ID of the removed
  // account.
  void OnExtendedAccountInfoRemoved(const AccountInfo& info) override;

  // Fires the chrome.identity.onSignInChanged event.
  void FireOnAccountSignInChanged(const GaiaId& gaia_id, bool is_signed_in);

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
  void OnChromeSigninDialogDestroyed();
  void HandleSkipUIForTesting(base::OnceClosure on_complete);
#endif

  const raw_ptr<Profile> profile_;
  const raw_ptr<signin::IdentityManager> identity_manager_;
  const raw_ptr<ExtensionPrefs> extension_prefs_;
  const raw_ptr<EventRouter> event_router_;

  IdentityMintRequestQueue mint_queue_;
  IdentityTokenCache token_cache_;
  // Contains Gaia Id of accounts known to extensions.
  base::flat_set<GaiaId> accounts_known_to_extensions_;

  OnSignInChangedCallback on_signin_changed_callback_for_testing_;

  base::OnceCallbackList<void()> on_shutdown_callback_list_;

#if BUILDFLAG(ENABLE_DICE_SUPPORT)
  bool is_chrome_signin_dialog_open_ = false;
  std::vector<base::OnceClosure> on_chrome_signin_dialog_completed_;
  // Should only be set in unittests.
  base::OnceCallback<void(base::OnceClosure)> skip_ui_for_testing_callback_;
  base::WeakPtrFactory<IdentityAPI> weak_ptr_factory_{this};
#endif
};

template <>
void BrowserContextKeyedAPIFactory<IdentityAPI>::DeclareFactoryDependencies();

}  // namespace extensions

#endif  // CHROME_BROWSER_EXTENSIONS_API_IDENTITY_IDENTITY_API_H_