| // Copyright 2018 The Chromium Authors | |
| // Use of this source code is governed by a BSD-style license that can be | |
| // found in the LICENSE file. | |
| #ifndef COMPONENTS_SYNC_SERVICE_SYNC_AUTH_MANAGER_H_ | |
| #define COMPONENTS_SYNC_SERVICE_SYNC_AUTH_MANAGER_H_ | |
| #include <memory> | |
| #include <string> | |
| #include <vector> | |
| #include "base/functional/callback.h" | |
| #include "base/memory/raw_ptr.h" | |
| #include "base/memory/weak_ptr.h" | |
| #include "base/scoped_observation.h" | |
| #include "base/time/time.h" | |
| #include "base/timer/timer.h" | |
| #include "components/signin/public/identity_manager/access_token_info.h" | |
| #include "components/signin/public/identity_manager/account_info.h" | |
| #include "components/signin/public/identity_manager/account_managed_status_finder_outcome.h" | |
| #include "components/signin/public/identity_manager/identity_manager.h" | |
| #include "components/sync/engine/connection_status.h" | |
| #include "components/sync/service/sync_token_status.h" | |
| #include "google_apis/gaia/google_service_auth_error.h" | |
| #include "net/base/backoff_entry.h" | |
| namespace signin { | |
| class AccessTokenFetcher; | |
| class AccountManagedStatusFinder; | |
| } // namespace signin | |
| namespace syncer { | |
| struct SyncCredentials; | |
| struct SyncAccountInfo { | |
| CoreAccountInfo account_info; | |
| bool is_sync_consented = false; | |
| signin::AccountManagedStatusFinderOutcome managed_status = | |
| signin::AccountManagedStatusFinderOutcome::kPending; | |
| friend bool operator==(const SyncAccountInfo&, | |
| const SyncAccountInfo&) = default; | |
| }; | |
| // SyncAuthManager tracks the account to be used for Sync and its authentication | |
| // state. Note that this account may or may not be the primary account (as per | |
| // IdentityManager::GetPrimaryAccountInfo() etc). | |
| class SyncAuthManager : public signin::IdentityManager::Observer { | |
| public: | |
| class Delegate { | |
| public: | |
| virtual ~Delegate() = default; | |
| // Called when the existence of an authenticated account changes. It's | |
| // guaranteed that this is only called for going from "no account" to "have | |
| // account" or vice versa, or if the existing account's `is_primary` bit | |
| // changed. I.e. SyncAuthManager will never directly switch from one account | |
| // to a different one. Call GetActiveAccountInfo to get the new state. | |
| virtual void SyncAuthAccountStateChanged() = 0; | |
| // Called when the credential state changes, i.e. an access token was | |
| // added/changed/removed. Call GetCredentials to get the new state. | |
| virtual void SyncAuthCredentialsChanged() = 0; | |
| }; | |
| // `identity_manager` may be null (this is the case if local Sync is enabled), | |
| // but if non-null, must outlive this object. `delegate` must not be null and | |
| // must outlive this object. | |
| SyncAuthManager(signin::IdentityManager* identity_manager, | |
| Delegate* delegate, | |
| base::TimeDelta account_managed_status_finder_timeout); | |
| SyncAuthManager(const SyncAuthManager&) = delete; | |
| SyncAuthManager& operator=(const SyncAuthManager&) = delete; | |
| ~SyncAuthManager() override; | |
| // Tells the tracker to start listening for changes to the account/sign-in | |
| // status. This gets called during SyncService initialization, except in the | |
| // case of local Sync. Before this is called, GetActiveAccountInfo will always | |
| // return an empty AccountInfo. Note that this will *not* trigger any | |
| // callbacks, even if there is an active account afterwards. | |
| void RegisterForAuthNotifications(); | |
| // Returns whether all relevant account information as returned by | |
| // GetActiveAccountInfo() has been fully loaded. | |
| bool IsActiveAccountInfoFullyLoaded() const; | |
| // Returns the account which should be used when communicating with the Sync | |
| // server. Note that this account may not be blessed for Sync-the-feature. | |
| SyncAccountInfo GetActiveAccountInfo() const; | |
| // Returns the last auth error that was encountered. The error could have come | |
| // from the Sync server or from the IdentityManager. | |
| GoogleServiceAuthError GetLastAuthError() const; | |
| // Returns the time at which the last auth error was set. | |
| base::Time GetLastAuthErrorTime() const; | |
| // Returns whether we are in the "Sync paused" state. That means there is a | |
| // primary account, but the user signed out in the content area, and so we | |
| // don't have credentials for it anymore. | |
| bool IsSyncPaused() const; | |
| // Returns the credentials to be passed to the SyncEngine. | |
| SyncCredentials GetCredentials() const; | |
| // Returns the state of the access token and token request, for display in | |
| // internals UI. | |
| SyncTokenStatus GetSyncTokenStatus() const; | |
| // Requests an access token. runs `callback` with the result (can be called | |
| // synchronously or asynchronously). | |
| void FetchAccessToken( | |
| base::OnceCallback<void(signin::AccessTokenInfo)> callback); | |
| // Called by SyncServiceImpl when Sync starts up and will try talking to | |
| // the server soon. This initiates fetching an access token. | |
| void ConnectionOpened(); | |
| // Called by SyncServiceImpl when the status of the connection to the Sync | |
| // server changed. Updates auth error state accordingly. | |
| void ConnectionStatusChanged(ConnectionStatus status); | |
| // Called by SyncServiceImpl when the connection to the Sync server is | |
| // closed (due to Sync being shut down). Clears all related state (such as | |
| // cached access token, error from the server, etc). | |
| void ConnectionClosed(); | |
| // signin::IdentityManager::Observer implementation. | |
| void OnPrimaryAccountChanged( | |
| const signin::PrimaryAccountChangeEvent& event) override; | |
| void OnRefreshTokenUpdatedForAccount( | |
| const CoreAccountInfo& account_info) override; | |
| void OnRefreshTokenRemovedForAccount( | |
| const CoreAccountId& account_id) override; | |
| void OnErrorStateOfRefreshTokenUpdatedForAccount( | |
| const CoreAccountInfo& account_info, | |
| const GoogleServiceAuthError& error, | |
| signin_metrics::SourceForRefreshTokenOperation token_operation_source) | |
| override; | |
| void OnRefreshTokensLoaded() override; | |
| void OnIdentityManagerShutdown( | |
| signin::IdentityManager* identity_manager) override; | |
| // Test-only methods for inspecting/modifying internal state. | |
| bool IsRetryingAccessTokenFetchForTest() const; | |
| void ResetRequestAccessTokenBackoffForTest(); | |
| private: | |
| // Helper class that ensures the account's managed-status gets queried | |
| // whenever the account itself changes. | |
| class ActiveAccount { | |
| public: | |
| // The `account_changed_callback` will be called whenever an account's | |
| // managed-ness is determined asynchronously. | |
| ActiveAccount(signin::IdentityManager* identity_manager, | |
| base::TimeDelta managed_status_finder_timeout, | |
| base::RepeatingClosure account_changed_callback); | |
| ~ActiveAccount(); | |
| ActiveAccount(const ActiveAccount&) = delete; | |
| ActiveAccount& operator=(const ActiveAccount&) = delete; | |
| // To be called when the basic account info changes (e.g. sign in or sign | |
| // out). Will kick off determining the managed-ness status, which may | |
| // complete synchronously or asynchronously. | |
| void Set(const SyncAccountInfo& new_account); | |
| const SyncAccountInfo& Get() const; | |
| private: | |
| // Starts the process of determining the account type (managed or not). This | |
| // may be synchronous or asynchronous. | |
| void StartDeterminingAccountType(); | |
| // Callback for the async case. | |
| void AccountTypeDeterminedAsynchronously(); | |
| const raw_ptr<signin::IdentityManager> identity_manager_; | |
| const base::TimeDelta managed_status_finder_timeout_; | |
| base::RepeatingClosure account_changed_callback_; | |
| SyncAccountInfo account_info_; | |
| std::unique_ptr<signin::AccountManagedStatusFinder> managed_status_finder_; | |
| base::Time managed_status_finder_start_time_; | |
| }; | |
| // Updates `sync_account_` to the appropriate account (i.e. | |
| // DetermineAccountToUse()) if necessary, and notifies observers of any | |
| // changes (sign-in/sign-out/"primary" bit change). Note that changing from | |
| // one account to another is exposed to observers as a sign-out + sign-in. | |
| // Returns whether the syncing account was updated. | |
| bool UpdateSyncAccountIfNecessary(); | |
| // Called by ActiveAccount when the account's managed-ness has been determined | |
| // asynchronously. | |
| void AccountManagednessDetermined(); | |
| // Invalidates any current access token, which means invalidating it with the | |
| // IdentityManager and also dropping our own cached copy. Meant to be called | |
| // when we know the current token is invalid (e.g. expired). Does not do | |
| // anything about any scheduled or ongoing request. | |
| void InvalidateAccessToken(); | |
| // Clears any access token we have, and cancels any pending or scheduled | |
| // request for one. | |
| void ClearAccessTokenAndRequest(); | |
| // Schedules a request for an access token according to the current | |
| // `request_access_token_backoff_`. Usually called after some transient error. | |
| void ScheduleAccessTokenRequest(); | |
| // Immediately starts an access token request, unless one is already ongoing. | |
| // If another request is scheduled for later, it is canceled. Any access token | |
| // we currently have is invalidated. | |
| void RequestAccessTokenNow(); | |
| // Callback for `ongoing_access_token_fetch_`. | |
| void AccessTokenFetched(GoogleServiceAuthError error, | |
| signin::AccessTokenInfo access_token_info); | |
| void SetLastAuthError(const GoogleServiceAuthError& error); | |
| void NotifyAccessTokenCallbacks(const signin::AccessTokenInfo& token); | |
| const raw_ptr<signin::IdentityManager> identity_manager_; | |
| base::ScopedObservation<signin::IdentityManager, | |
| signin::IdentityManager::Observer> | |
| identity_manager_observation_{this}; | |
| const raw_ptr<Delegate> delegate_; | |
| bool registered_for_auth_notifications_ = false; | |
| // The account which we are using to sync. If this is non-empty, that does | |
| // *not* necessarily imply that Sync is actually running, e.g. because of | |
| // delayed startup. | |
| ActiveAccount sync_account_; | |
| // This is a cache of the last authentication response we received from | |
| // Chrome's identity/token management system. | |
| GoogleServiceAuthError last_auth_error_; | |
| base::Time last_auth_error_time_; | |
| // Whether Sync is currently connected to the server, i.e. ConnectionOpened() | |
| // has been called, but ConnectionClosed() hasn't. While this is false, we | |
| // don't try to get an access token. While it's true, we will *usually* have | |
| // either an access token or a pending/scheduled request for one, but this is | |
| // not guaranteed (e.g. in the case of a persistent auth error). | |
| bool connection_open_ = false; | |
| // The current access token. This is mutually exclusive with | |
| // `ongoing_access_token_fetch_` and `request_access_token_retry_timer_`: | |
| // We have at most one of a) an access token OR b) a pending request OR c) a | |
| // pending retry i.e. a scheduled request. | |
| signin::AccessTokenInfo access_token_info_; | |
| // Pending request for an access token. Non-null iff there is a request | |
| // ongoing. | |
| std::unique_ptr<signin::AccessTokenFetcher> ongoing_access_token_fetch_; | |
| // If `RequestAccessTokenNow()` fails with transient error then retry | |
| // requesting access token with exponential backoff. | |
| base::OneShotTimer request_access_token_retry_timer_; | |
| net::BackoffEntry request_access_token_backoff_; | |
| // Info about the state of our access token, for display in the internals UI. | |
| // "Partial" because this instance is not fully populated - in particular, | |
| // `has_token` and `next_token_request_time` get computed on demand. | |
| SyncTokenStatus partial_token_status_; | |
| // Whether there was a retry done to fetch the access token when the request | |
| // was cancelled for the first time. This works around an issue that can only | |
| // happen once during browser startup, so it's sufficient to have a single | |
| // retry (i.e. not per request). | |
| bool access_token_retried_ = false; | |
| std::vector<base::OnceCallback<void(signin::AccessTokenInfo)>> | |
| access_token_callbacks_; | |
| base::WeakPtrFactory<SyncAuthManager> weak_ptr_factory_{this}; | |
| }; | |
| } // namespace syncer | |
| #endif // COMPONENTS_SYNC_SERVICE_SYNC_AUTH_MANAGER_H_ |