blob: 106dbc98821d94bb215b2faa5b0a7e4548d7b38a [file] [log] [blame]
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_OAUTH2_TOKEN_SERVICE_H_
#define CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_OAUTH2_TOKEN_SERVICE_H_
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/weak_ptr.h"
#include "chrome/browser/chromeos/settings/cros_settings.h"
#include "google_apis/gaia/core_account_id.h"
#include "google_apis/gaia/gaia_oauth_client.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_access_token_manager.h"
namespace network {
class SharedURLLoaderFactory;
}
class OAuth2AccessTokenFetcher;
class OAuth2AccessTokenConsumer;
class PrefRegistrySimple;
class PrefService;
namespace chromeos {
// DeviceOAuth2TokenService retrieves OAuth2 access tokens for a given
// set of scopes using the device-level OAuth2 any-api refresh token
// obtained during enterprise device enrollment.
// When using DeviceOAuth2TokenService, a value of |GetRobotAccountId| should
// be used in places where API expects |account_id|.
//
// Note that requests must be made from the UI thread.
class DeviceOAuth2TokenService : public OAuth2AccessTokenManager::Delegate,
public gaia::GaiaOAuthClient::Delegate {
public:
typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */)>
RefreshTokenAvailableCallback;
typedef base::RepeatingCallback<void(const CoreAccountId& /* account_id */)>
RefreshTokenRevokedCallback;
typedef base::Callback<void(bool)> StatusCallback;
// Persist the given refresh token on the device. Overwrites any previous
// value. Should only be called during initial device setup. Signals
// completion via the given callback, passing true if the operation succeeded.
void SetAndSaveRefreshToken(const std::string& refresh_token,
const StatusCallback& callback);
static void RegisterPrefs(PrefRegistrySimple* registry);
// Pull the robot account ID from device policy.
CoreAccountId GetRobotAccountId() const;
// Can be used to override the robot account ID for testing purposes. Most
// common use case is to easily inject a non-empty account ID to make the
// refresh token for the robot account visible via GetAccounts() and
// RefreshTokenIsAvailable().
void set_robot_account_id_for_testing(const CoreAccountId& account_id);
// If set, this callback will be invoked when a new refresh token is
// available.
void SetRefreshTokenAvailableCallback(RefreshTokenAvailableCallback callback);
// If set, this callback will be invoked when a refresh token is revoked.
void SetRefreshTokenRevokedCallback(RefreshTokenRevokedCallback callback);
// Checks in the cache for a valid access token for a specified |account_id|
// and |scopes|, and if not found starts a request for an OAuth2 access token
// using the OAuth2 refresh token maintained by this instance for that
// |account_id|. The caller owns the returned Request.
// |scopes| is the set of scopes to get an access token for, |consumer| is
// the object that will be called back with results if the returned request
// is not deleted.
std::unique_ptr<OAuth2AccessTokenManager::Request> StartAccessTokenRequest(
const CoreAccountId& account_id,
const OAuth2AccessTokenManager::ScopeSet& scopes,
OAuth2AccessTokenManager::Consumer* consumer);
// Mark an OAuth2 |access_token| issued for |account_id| and |scopes| as
// invalid. This should be done if the token was received from this class,
// but was not accepted by the server (e.g., the server returned
// 401 Unauthorized). The token will be removed from the cache for the given
// scopes.
void InvalidateAccessToken(const CoreAccountId& account_id,
const OAuth2AccessTokenManager::ScopeSet& scopes,
const std::string& access_token);
bool RefreshTokenIsAvailable(const CoreAccountId& account_id) const;
OAuth2AccessTokenManager* GetAccessTokenManager();
// gaia::GaiaOAuthClient::Delegate implementation.
void OnRefreshTokenResponse(const std::string& access_token,
int expires_in_seconds) override;
void OnGetTokenInfoResponse(
std::unique_ptr<base::DictionaryValue> token_info) override;
void OnOAuthError() override;
void OnNetworkError(int response_code) override;
private:
friend class DeviceOAuth2TokenServiceFactory;
friend class DeviceOAuth2TokenServiceTest;
struct PendingRequest;
// Describes the operational state of this object.
enum State {
// Pending system salt / refresh token load.
STATE_LOADING,
// No token available.
STATE_NO_TOKEN,
// System salt loaded, validation not started yet.
STATE_VALIDATION_PENDING,
// Refresh token validation underway.
STATE_VALIDATION_STARTED,
// Token validation failed.
STATE_TOKEN_INVALID,
// Refresh token is valid.
STATE_TOKEN_VALID,
};
// OAuth2AccessTokenManager::Delegate:
std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
OAuth2AccessTokenConsumer* consumer) override;
bool HasRefreshToken(const CoreAccountId& account_id) const override;
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory()
const override;
bool HandleAccessTokenFetch(
OAuth2AccessTokenManager::RequestImpl* request,
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
const std::string& client_id,
const std::string& client_secret,
const OAuth2AccessTokenManager::ScopeSet& scopes) override;
void FireRefreshTokenAvailable(const CoreAccountId& account_id);
void FireRefreshTokenRevoked(const CoreAccountId& account_id);
// Use DeviceOAuth2TokenServiceFactory to get an instance of this class.
explicit DeviceOAuth2TokenService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_state);
~DeviceOAuth2TokenService() override;
// Flushes |pending_requests_|, indicating the specified result.
void FlushPendingRequests(bool token_is_valid,
GoogleServiceAuthError::State error);
// Signals failure on the specified request, passing |error| as the reason.
void FailRequest(OAuth2AccessTokenManager::RequestImpl* request,
GoogleServiceAuthError::State error);
// Returns a list of accounts based on |state_|.
std::vector<CoreAccountId> GetAccounts() const;
// Starts the token validation flow, i.e. token info fetch.
void StartValidation();
void RequestValidation();
// Invoked by CrosSettings when the robot account ID becomes available.
void OnServiceAccountIdentityChanged();
// Checks whether |gaia_robot_id| matches the expected account ID indicated in
// device settings.
void CheckRobotAccountId(const CoreAccountId& gaia_robot_id);
// Returns the refresh token for the robot account id.
std::string GetRefreshToken() const;
// Handles completion of the system salt input.
void DidGetSystemSalt(const std::string& system_salt);
// Encrypts and saves the refresh token. Should only be called when the system
// salt is available.
void EncryptAndSaveToken();
// Flushes |token_save_callbacks_|, indicating the specified result.
void FlushTokenSaveCallbacks(bool result);
void ReportServiceError(GoogleServiceAuthError::State error);
std::unique_ptr<OAuth2AccessTokenManager> token_manager_;
// Currently open requests that are waiting while loading the system salt or
// validating the token.
std::vector<PendingRequest*> pending_requests_;
// Callbacks to invoke, if set, for refresh token-related events.
RefreshTokenAvailableCallback on_refresh_token_available_callback_;
RefreshTokenRevokedCallback on_refresh_token_revoked_callback_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
PrefService* local_state_;
// Current operational state.
State state_;
// Token save callbacks waiting to be completed.
std::vector<StatusCallback> token_save_callbacks_;
// The system salt for encrypting and decrypting the refresh token.
std::string system_salt_;
int max_refresh_token_validation_retries_;
// Flag to indicate whether there are pending requests.
bool validation_requested_;
// Cache the decrypted refresh token, so we only decrypt once.
std::string refresh_token_;
std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
std::unique_ptr<CrosSettings::ObserverSubscription>
service_account_identity_subscription_;
CoreAccountId robot_account_id_for_testing_;
base::WeakPtrFactory<DeviceOAuth2TokenService> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenService);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_SETTINGS_DEVICE_OAUTH2_TOKEN_SERVICE_H_