Commit e5895b96 authored by Julie Jeongeun Kim's avatar Julie Jeongeun Kim Committed by Commit Bot

Move the refresh token encryption and decryption to DeviceO2TS

This CL is a part of moving access token management to
OAuth2AccessTokenManager.

It is specifically a step toward folding
DeviceO2TSDelegate into DeviceO2TS now that the latter
is no longer an O2TS subclass.

It moves the refresh token encryption and decryption
from DeviceO2TSDelegate to DeviceO2TS. The sequence
is that if the system salt is available, it gets the
encrypted refresh token from pref and decrypted it
using the system salt. Previously DeviceO2TSDelegate
had it. It's moved to DeviceO2TS with this CL.

Bug: 967598
Change-Id: I668704ce0ac655c64ddfe493c4ee5f0cc6fa1559
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1695281
Commit-Queue: Julie Jeongeun Kim <jkim@igalia.com>
Reviewed-by: default avatarColin Blundell <blundell@chromium.org>
Cr-Commit-Position: refs/heads/master@{#676357}
parent f01c03dd
......@@ -14,9 +14,13 @@
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_task_runner_handle.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/settings/token_encryptor.h"
#include "chrome/common/pref_names.h"
#include "chromeos/cryptohome/system_salt_getter.h"
#include "chromeos/settings/cros_settings_names.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/google_service_auth_error.h"
#include "google_apis/gaia/oauth2_access_token_fetcher.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"
......@@ -51,7 +55,8 @@ void DeviceOAuth2TokenService::OnValidationCompleted(
DeviceOAuth2TokenService::DeviceOAuth2TokenService(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_state)
: service_account_identity_subscription_(
: local_state_(local_state),
service_account_identity_subscription_(
CrosSettings::Get()->AddSettingsObserver(
kServiceAccountIdentity,
base::Bind(
......@@ -59,13 +64,18 @@ DeviceOAuth2TokenService::DeviceOAuth2TokenService(
base::Unretained(this)))),
weak_ptr_factory_(this) {
delegate_ = std::make_unique<DeviceOAuth2TokenServiceDelegate>(
url_loader_factory, local_state, this);
url_loader_factory, this);
token_manager_ = std::make_unique<OAuth2AccessTokenManager>(
this /* OAuth2AccessTokenManager::Delegate* */);
delegate_->InitializeWithValidationStatusDelegate(this);
// Pull in the system salt.
SystemSaltGetter::Get()->GetSystemSalt(
base::Bind(&DeviceOAuth2TokenService::DidGetSystemSalt,
weak_ptr_factory_.GetWeakPtr()));
}
DeviceOAuth2TokenService::~DeviceOAuth2TokenService() {
FlushTokenSaveCallbacks(false);
delegate_->ClearValidationStatusDelegate();
FlushPendingRequests(false, GoogleServiceAuthError::REQUEST_CANCELED);
}
......@@ -79,7 +89,27 @@ void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) {
void DeviceOAuth2TokenService::SetAndSaveRefreshToken(
const std::string& refresh_token,
const StatusCallback& result_callback) {
delegate_->SetAndSaveRefreshToken(refresh_token, result_callback);
delegate_->ReportServiceError(GoogleServiceAuthError::REQUEST_CANCELED);
bool waiting_for_salt =
delegate_->state_ == DeviceOAuth2TokenServiceDelegate::STATE_LOADING;
refresh_token_ = refresh_token;
delegate_->state_ =
DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_PENDING;
// If the robot account ID is not available yet, do not announce the token. It
// will be done from OnServiceAccountIdentityChanged() once the robot account
// ID becomes available as well.
if (!GetRobotAccountId().empty())
FireRefreshTokenAvailable(GetRobotAccountId());
token_save_callbacks_.push_back(result_callback);
if (!waiting_for_salt) {
if (system_salt_.empty())
FlushTokenSaveCallbacks(false);
else
EncryptAndSaveToken();
}
}
CoreAccountId DeviceOAuth2TokenService::GetRobotAccountId() const {
......@@ -258,7 +288,7 @@ std::vector<CoreAccountId> DeviceOAuth2TokenService::GetAccounts() const {
}
void DeviceOAuth2TokenService::OnServiceAccountIdentityChanged() {
if (!GetRobotAccountId().empty() && !delegate_->refresh_token_.empty())
if (!GetRobotAccountId().empty() && !refresh_token_.empty())
FireRefreshTokenAvailable(GetRobotAccountId());
}
......@@ -300,4 +330,98 @@ void DeviceOAuth2TokenService::CheckRobotAccountId(
GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS);
}
}
std::string DeviceOAuth2TokenService::GetRefreshToken() const {
switch (delegate_->state_) {
case DeviceOAuth2TokenServiceDelegate::STATE_LOADING:
case DeviceOAuth2TokenServiceDelegate::STATE_NO_TOKEN:
case DeviceOAuth2TokenServiceDelegate::STATE_TOKEN_INVALID:
// This shouldn't happen: GetRefreshToken() is only called for actual
// token minting operations. In above states, requests are either queued
// or short-circuited to signal error immediately, so no actual token
// minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
NOTREACHED();
return std::string();
case DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_PENDING:
case DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_STARTED:
case DeviceOAuth2TokenServiceDelegate::STATE_TOKEN_VALID:
return refresh_token_;
}
NOTREACHED() << "Unhandled state " << delegate_->state_;
return std::string();
}
void DeviceOAuth2TokenService::DidGetSystemSalt(
const std::string& system_salt) {
system_salt_ = system_salt;
// Bail out if system salt is not available.
if (system_salt_.empty()) {
LOG(ERROR) << "Failed to get system salt.";
FlushTokenSaveCallbacks(false);
delegate_->state_ = DeviceOAuth2TokenServiceDelegate::STATE_NO_TOKEN;
return;
}
// If the token has been set meanwhile, write it to |local_state_|.
if (!refresh_token_.empty()) {
EncryptAndSaveToken();
return;
}
// Otherwise, load the refresh token from |local_state_|.
std::string encrypted_refresh_token =
local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
if (!encrypted_refresh_token.empty()) {
CryptohomeTokenEncryptor encryptor(system_salt_);
refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
if (refresh_token_.empty()) {
LOG(ERROR) << "Failed to decrypt refresh token.";
delegate_->state_ = DeviceOAuth2TokenServiceDelegate::STATE_NO_TOKEN;
return;
}
}
delegate_->state_ =
DeviceOAuth2TokenServiceDelegate::STATE_VALIDATION_PENDING;
// If there are pending requests, start a validation.
if (delegate_->validation_requested_)
delegate_->StartValidation();
// Announce the token.
if (!GetRobotAccountId().empty()) {
FireRefreshTokenAvailable(GetRobotAccountId());
}
}
void DeviceOAuth2TokenService::EncryptAndSaveToken() {
DCHECK_NE(delegate_->state_, DeviceOAuth2TokenServiceDelegate::STATE_LOADING);
CryptohomeTokenEncryptor encryptor(system_salt_);
std::string encrypted_refresh_token =
encryptor.EncryptWithSystemSalt(refresh_token_);
bool result = true;
if (encrypted_refresh_token.empty()) {
LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
result = false;
} else {
local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
encrypted_refresh_token);
}
FlushTokenSaveCallbacks(result);
}
void DeviceOAuth2TokenService::FlushTokenSaveCallbacks(bool result) {
std::vector<StatusCallback> callbacks;
callbacks.swap(token_save_callbacks_);
for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
callback != callbacks.end(); ++callback) {
if (!callback->is_null())
callback->Run(result);
}
}
} // namespace chromeos
......@@ -149,6 +149,19 @@ class DeviceOAuth2TokenService
// 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);
// TODO(https://crbug.com/967598): Merge DeviceOAuth2TokenServiceDelegate
// into DeviceOAuth2TokenService.
std::unique_ptr<DeviceOAuth2TokenServiceDelegate> delegate_;
......@@ -162,11 +175,22 @@ class DeviceOAuth2TokenService
RefreshTokenAvailableCallback on_refresh_token_available_callback_;
RefreshTokenRevokedCallback on_refresh_token_revoked_callback_;
PrefService* local_state_;
std::unique_ptr<CrosSettings::ObserverSubscription>
service_account_identity_subscription_;
CoreAccountId robot_account_id_for_testing_;
// 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_;
// Cache the decrypted refresh token, so we only decrypt once.
std::string refresh_token_;
base::WeakPtrFactory<DeviceOAuth2TokenService> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenService);
......
......@@ -5,19 +5,9 @@
#include "chrome/browser/chromeos/settings/device_oauth2_token_service_delegate.h"
#include <string>
#include <vector>
#include "base/bind.h"
#include "base/bind_helpers.h"
#include "base/memory/weak_ptr.h"
#include "base/values.h"
#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h"
#include "chrome/browser/chromeos/settings/token_encryptor.h"
#include "chrome/common/pref_names.h"
#include "chromeos/cryptohome/system_salt_getter.h"
#include "components/policy/proto/device_management_backend.pb.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "google_apis/gaia/gaia_constants.h"
#include "google_apis/gaia/gaia_urls.h"
#include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
......@@ -27,49 +17,15 @@ namespace chromeos {
DeviceOAuth2TokenServiceDelegate::DeviceOAuth2TokenServiceDelegate(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_state,
DeviceOAuth2TokenService* service)
: url_loader_factory_(url_loader_factory),
local_state_(local_state),
state_(STATE_LOADING),
max_refresh_token_validation_retries_(3),
validation_requested_(false),
validation_status_delegate_(nullptr),
service_(service),
weak_ptr_factory_(this) {
// Pull in the system salt.
SystemSaltGetter::Get()->GetSystemSalt(
base::Bind(&DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt,
weak_ptr_factory_.GetWeakPtr()));
}
DeviceOAuth2TokenServiceDelegate::~DeviceOAuth2TokenServiceDelegate() {
FlushTokenSaveCallbacks(false);
}
void DeviceOAuth2TokenServiceDelegate::SetAndSaveRefreshToken(
const std::string& refresh_token,
const StatusCallback& result_callback) {
ReportServiceError(GoogleServiceAuthError::REQUEST_CANCELED);
service_(service) {}
bool waiting_for_salt = state_ == STATE_LOADING;
refresh_token_ = refresh_token;
state_ = STATE_VALIDATION_PENDING;
// If the robot account ID is not available yet, do not announce the token. It
// will be done from OnServiceAccountIdentityChanged() once the robot account
// ID becomes available as well.
if (!service_->GetRobotAccountId().empty())
service_->FireRefreshTokenAvailable(service_->GetRobotAccountId());
token_save_callbacks_.push_back(result_callback);
if (!waiting_for_salt) {
if (system_salt_.empty())
FlushTokenSaveCallbacks(false);
else
EncryptAndSaveToken();
}
}
DeviceOAuth2TokenServiceDelegate::~DeviceOAuth2TokenServiceDelegate() = default;
void DeviceOAuth2TokenServiceDelegate::OnRefreshTokenResponse(
const std::string& access_token,
......@@ -103,27 +59,6 @@ void DeviceOAuth2TokenServiceDelegate::OnNetworkError(int response_code) {
ReportServiceError(GoogleServiceAuthError::CONNECTION_FAILED);
}
std::string DeviceOAuth2TokenServiceDelegate::GetRefreshToken() const {
switch (state_) {
case STATE_LOADING:
case STATE_NO_TOKEN:
case STATE_TOKEN_INVALID:
// This shouldn't happen: GetRefreshToken() is only called for actual
// token minting operations. In above states, requests are either queued
// or short-circuited to signal error immediately, so no actual token
// minting via OAuth2TokenService::FetchOAuth2Token should be triggered.
NOTREACHED();
return std::string();
case STATE_VALIDATION_PENDING:
case STATE_VALIDATION_STARTED:
case STATE_TOKEN_VALID:
return refresh_token_;
}
NOTREACHED() << "Unhandled state " << state_;
return std::string();
}
scoped_refptr<network::SharedURLLoaderFactory>
DeviceOAuth2TokenServiceDelegate::GetURLLoaderFactory() const {
return url_loader_factory_;
......@@ -134,73 +69,12 @@ DeviceOAuth2TokenServiceDelegate::CreateAccessTokenFetcher(
const CoreAccountId& account_id,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
OAuth2AccessTokenConsumer* consumer) {
std::string refresh_token = GetRefreshToken();
std::string refresh_token = service_->GetRefreshToken();
DCHECK(!refresh_token.empty());
return std::make_unique<OAuth2AccessTokenFetcherImpl>(
consumer, url_loader_factory, refresh_token);
}
void DeviceOAuth2TokenServiceDelegate::DidGetSystemSalt(
const std::string& system_salt) {
system_salt_ = system_salt;
// Bail out if system salt is not available.
if (system_salt_.empty()) {
LOG(ERROR) << "Failed to get system salt.";
FlushTokenSaveCallbacks(false);
state_ = STATE_NO_TOKEN;
return;
}
// If the token has been set meanwhile, write it to |local_state_|.
if (!refresh_token_.empty()) {
EncryptAndSaveToken();
return;
}
// Otherwise, load the refresh token from |local_state_|.
std::string encrypted_refresh_token =
local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken);
if (!encrypted_refresh_token.empty()) {
CryptohomeTokenEncryptor encryptor(system_salt_);
refresh_token_ = encryptor.DecryptWithSystemSalt(encrypted_refresh_token);
if (refresh_token_.empty()) {
LOG(ERROR) << "Failed to decrypt refresh token.";
state_ = STATE_NO_TOKEN;
return;
}
}
state_ = STATE_VALIDATION_PENDING;
// If there are pending requests, start a validation.
if (validation_requested_)
StartValidation();
// Announce the token.
if (!service_->GetRobotAccountId().empty()) {
service_->FireRefreshTokenAvailable(service_->GetRobotAccountId());
}
}
void DeviceOAuth2TokenServiceDelegate::EncryptAndSaveToken() {
DCHECK_NE(state_, STATE_LOADING);
CryptohomeTokenEncryptor encryptor(system_salt_);
std::string encrypted_refresh_token =
encryptor.EncryptWithSystemSalt(refresh_token_);
bool result = true;
if (encrypted_refresh_token.empty()) {
LOG(ERROR) << "Failed to encrypt refresh token; save aborted.";
result = false;
} else {
local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken,
encrypted_refresh_token);
}
FlushTokenSaveCallbacks(result);
}
void DeviceOAuth2TokenServiceDelegate::StartValidation() {
DCHECK_EQ(state_, STATE_VALIDATION_PENDING);
DCHECK(!gaia_oauth_client_);
......@@ -216,21 +90,11 @@ void DeviceOAuth2TokenServiceDelegate::StartValidation() {
client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
gaia_oauth_client_->RefreshToken(
client_info, refresh_token_,
client_info, service_->refresh_token_,
std::vector<std::string>(1, GaiaConstants::kOAuthWrapBridgeUserInfoScope),
max_refresh_token_validation_retries_, this);
}
void DeviceOAuth2TokenServiceDelegate::FlushTokenSaveCallbacks(bool result) {
std::vector<StatusCallback> callbacks;
callbacks.swap(token_save_callbacks_);
for (std::vector<StatusCallback>::iterator callback(callbacks.begin());
callback != callbacks.end(); ++callback) {
if (!callback->is_null())
callback->Run(result);
}
}
void DeviceOAuth2TokenServiceDelegate::RequestValidation() {
validation_requested_ = true;
}
......
......@@ -7,16 +7,12 @@
#include <memory>
#include <string>
#include <vector>
#include "base/callback.h"
#include "base/macros.h"
#include "base/memory/ref_counted.h"
#include "base/memory/weak_ptr.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 "net/url_request/url_request_context_getter.h"
namespace gaia {
class GaiaOAuthClient;
......@@ -26,7 +22,6 @@ namespace network {
class SharedURLLoaderFactory;
}
class PrefService;
class OAuth2AccessTokenFetcher;
class OAuth2AccessTokenConsumer;
......@@ -39,18 +34,9 @@ class DeviceOAuth2TokenServiceDelegate
public:
DeviceOAuth2TokenServiceDelegate(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
PrefService* local_state,
DeviceOAuth2TokenService* service);
~DeviceOAuth2TokenServiceDelegate() override;
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);
scoped_refptr<network::SharedURLLoaderFactory> GetURLLoaderFactory() const;
std::unique_ptr<OAuth2AccessTokenFetcher> CreateAccessTokenFetcher(
const CoreAccountId& account_id,
......@@ -90,22 +76,9 @@ class DeviceOAuth2TokenServiceDelegate
STATE_TOKEN_VALID,
};
// 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();
// Starts the token validation flow, i.e. token info fetch.
void StartValidation();
// Flushes |token_save_callbacks_|, indicating the specified result.
void FlushTokenSaveCallbacks(bool result);
void RequestValidation();
void InitializeWithValidationStatusDelegate(
......@@ -116,17 +89,10 @@ class DeviceOAuth2TokenServiceDelegate
// Dependencies.
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.
......@@ -135,17 +101,12 @@ class DeviceOAuth2TokenServiceDelegate
// Validation status delegate
ValidationStatusDelegate* validation_status_delegate_;
// Cache the decrypted refresh token, so we only decrypt once.
std::string refresh_token_;
std::unique_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_;
// TODO(https://crbug.com/967598): Completely merge this class into
// DeviceOAuth2TokenService.
DeviceOAuth2TokenService* service_;
base::WeakPtrFactory<DeviceOAuth2TokenServiceDelegate> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(DeviceOAuth2TokenServiceDelegate);
};
......
......@@ -126,10 +126,6 @@ class DeviceOAuth2TokenServiceTest : public testing::Test {
" \"user_id\": \"1234567890\" }";
}
DeviceOAuth2TokenServiceDelegate* GetDelegate() {
return oauth2_service_->delegate_.get();
}
bool RefreshTokenIsAvailable() {
return oauth2_service_->RefreshTokenIsAvailable(
oauth2_service_->GetRobotAccountId());
......@@ -139,7 +135,7 @@ class DeviceOAuth2TokenServiceTest : public testing::Test {
if (!RefreshTokenIsAvailable())
return std::string();
return GetDelegate()->GetRefreshToken();
return oauth2_service_->GetRefreshToken();
}
// A utility method to return fake URL results, for testing the refresh token
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment