device_oauth2_token_service.cc revision 4e180b6a0b4720a9b8e9e959a882386f690f08ff
1// Copyright 2013 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chromeos/settings/device_oauth2_token_service.h" 6 7#include <string> 8#include <vector> 9 10#include "base/prefs/pref_registry_simple.h" 11#include "base/prefs/pref_service.h" 12#include "base/values.h" 13#include "chrome/browser/browser_process.h" 14#include "chrome/browser/chromeos/policy/device_cloud_policy_manager_chromeos.h" 15#include "chrome/browser/chromeos/settings/token_encryptor.h" 16#include "chrome/browser/policy/browser_policy_connector.h" 17#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 18#include "chrome/common/pref_names.h" 19#include "chromeos/cryptohome/cryptohome_library.h" 20#include "content/public/browser/browser_thread.h" 21#include "google_apis/gaia/gaia_urls.h" 22#include "google_apis/gaia/google_service_auth_error.h" 23 24namespace { 25const char kServiceScopeGetUserInfo[] = 26 "https://www.googleapis.com/auth/userinfo.email"; 27} 28 29namespace chromeos { 30 31// A wrapper for the consumer passed to StartRequest, which doesn't call 32// through to the target Consumer unless the refresh token validation is 33// complete. Additionally derives from the RequestImpl, so that it 34// can be passed back to the caller and directly deleted when cancelling 35// the request. 36class DeviceOAuth2TokenService::ValidatingConsumer 37 : public OAuth2TokenService::Consumer, 38 public OAuth2TokenService::RequestImpl, 39 public gaia::GaiaOAuthClient::Delegate { 40 public: 41 explicit ValidatingConsumer(DeviceOAuth2TokenService* token_service, 42 Consumer* consumer); 43 virtual ~ValidatingConsumer(); 44 45 void StartValidation(); 46 47 // OAuth2TokenService::Consumer 48 virtual void OnGetTokenSuccess( 49 const Request* request, 50 const std::string& access_token, 51 const base::Time& expiration_time) OVERRIDE; 52 virtual void OnGetTokenFailure( 53 const Request* request, 54 const GoogleServiceAuthError& error) OVERRIDE; 55 56 // gaia::GaiaOAuthClient::Delegate implementation. 57 virtual void OnRefreshTokenResponse(const std::string& access_token, 58 int expires_in_seconds) OVERRIDE; 59 virtual void OnGetTokenInfoResponse(scoped_ptr<DictionaryValue> token_info) 60 OVERRIDE; 61 virtual void OnOAuthError() OVERRIDE; 62 virtual void OnNetworkError(int response_code) OVERRIDE; 63 64 private: 65 void RefreshTokenIsValid(bool is_valid); 66 void InformConsumer(); 67 68 DeviceOAuth2TokenService* token_service_; 69 Consumer* consumer_; 70 scoped_ptr<gaia::GaiaOAuthClient> gaia_oauth_client_; 71 72 // We don't know which will complete first: the validation or the token 73 // minting. So, we need to cache the results so the final callback can 74 // take action. 75 76 // RefreshTokenValidationConsumer results 77 bool token_validation_done_; 78 bool token_is_valid_; 79 80 // OAuth2TokenService::Consumer results 81 bool token_fetch_done_; 82 std::string access_token_; 83 base::Time expiration_time_; 84 scoped_ptr<GoogleServiceAuthError> error_; 85}; 86 87DeviceOAuth2TokenService::ValidatingConsumer::ValidatingConsumer( 88 DeviceOAuth2TokenService* token_service, 89 Consumer* consumer) 90 : OAuth2TokenService::RequestImpl(this), 91 token_service_(token_service), 92 consumer_(consumer), 93 token_validation_done_(false), 94 token_is_valid_(false), 95 token_fetch_done_(false) { 96} 97 98DeviceOAuth2TokenService::ValidatingConsumer::~ValidatingConsumer() { 99} 100 101void DeviceOAuth2TokenService::ValidatingConsumer::StartValidation() { 102 DCHECK(!gaia_oauth_client_); 103 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( 104 g_browser_process->system_request_context())); 105 106 GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); 107 gaia::OAuthClientInfo client_info; 108 client_info.client_id = gaia_urls->oauth2_chrome_client_id(); 109 client_info.client_secret = gaia_urls->oauth2_chrome_client_secret(); 110 111 gaia_oauth_client_->RefreshToken( 112 client_info, 113 token_service_->GetRefreshToken(token_service_->GetRobotAccountId()), 114 std::vector<std::string>(1, kServiceScopeGetUserInfo), 115 token_service_->max_refresh_token_validation_retries_, 116 this); 117} 118 119void DeviceOAuth2TokenService::ValidatingConsumer::OnRefreshTokenResponse( 120 const std::string& access_token, 121 int expires_in_seconds) { 122 gaia_oauth_client_->GetTokenInfo( 123 access_token, 124 token_service_->max_refresh_token_validation_retries_, 125 this); 126} 127 128void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenInfoResponse( 129 scoped_ptr<DictionaryValue> token_info) { 130 std::string gaia_robot_id; 131 token_info->GetString("email", &gaia_robot_id); 132 133 std::string policy_robot_id = token_service_->GetRobotAccountId(); 134 135 if (policy_robot_id == gaia_robot_id) { 136 RefreshTokenIsValid(true); 137 } else { 138 if (gaia_robot_id.empty()) { 139 LOG(WARNING) << "Device service account owner in policy is empty."; 140 } else { 141 LOG(INFO) << "Device service account owner in policy does not match " 142 << "refresh token owner \"" << gaia_robot_id << "\"."; 143 } 144 RefreshTokenIsValid(false); 145 } 146} 147 148void DeviceOAuth2TokenService::ValidatingConsumer::OnOAuthError() { 149 RefreshTokenIsValid(false); 150} 151 152void DeviceOAuth2TokenService::ValidatingConsumer::OnNetworkError( 153 int response_code) { 154 RefreshTokenIsValid(false); 155} 156 157void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenSuccess( 158 const Request* request, 159 const std::string& access_token, 160 const base::Time& expiration_time) { 161 DCHECK_EQ(request, this); 162 token_fetch_done_ = true; 163 access_token_ = access_token; 164 expiration_time_ = expiration_time; 165 if (token_validation_done_) 166 InformConsumer(); 167} 168 169void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenFailure( 170 const Request* request, 171 const GoogleServiceAuthError& error) { 172 DCHECK_EQ(request, this); 173 token_fetch_done_ = true; 174 error_.reset(new GoogleServiceAuthError(error.state())); 175 if (token_validation_done_) 176 InformConsumer(); 177} 178 179void DeviceOAuth2TokenService::ValidatingConsumer::RefreshTokenIsValid( 180 bool is_valid) { 181 token_validation_done_ = true; 182 token_is_valid_ = is_valid; 183 token_service_->OnValidationComplete(is_valid); 184 if (token_fetch_done_) 185 InformConsumer(); 186} 187 188void DeviceOAuth2TokenService::ValidatingConsumer::InformConsumer() { 189 DCHECK(token_fetch_done_); 190 DCHECK(token_validation_done_); 191 192 // Note: this object (which is also the Request instance) may be deleted in 193 // these consumer callbacks, so the callbacks must be the last line executed. 194 // Also, make copies of the parameters passed to the consumer to avoid invalid 195 // memory accesses when the consumer deletes |this| immediately. 196 if (!token_is_valid_) { 197 consumer_->OnGetTokenFailure(this, GoogleServiceAuthError( 198 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 199 } else if (error_) { 200 GoogleServiceAuthError error_copy = *error_; 201 consumer_->OnGetTokenFailure(this, error_copy); 202 } else { 203 std::string access_token_copy = access_token_; 204 base::Time expiration_time_copy = expiration_time_; 205 consumer_->OnGetTokenSuccess(this, access_token_copy, expiration_time_copy); 206 } 207} 208 209DeviceOAuth2TokenService::DeviceOAuth2TokenService( 210 net::URLRequestContextGetter* getter, 211 PrefService* local_state, 212 TokenEncryptor* token_encryptor) 213 : refresh_token_is_valid_(false), 214 max_refresh_token_validation_retries_(3), 215 url_request_context_getter_(getter), 216 local_state_(local_state), 217 token_encryptor_(token_encryptor), 218 weak_ptr_factory_(this) { 219} 220 221DeviceOAuth2TokenService::~DeviceOAuth2TokenService() { 222} 223 224void DeviceOAuth2TokenService::OnValidationComplete( 225 bool refresh_token_is_valid) { 226 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 227 refresh_token_is_valid_ = refresh_token_is_valid; 228} 229 230// static 231void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) { 232 registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken, 233 std::string()); 234} 235 236void DeviceOAuth2TokenService::SetAndSaveRefreshToken( 237 const std::string& refresh_token) { 238 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 239 240 // TODO(xiyuan): Use async GetSystemSalt after merging to M31. 241 const std::string system_salt = CryptohomeLibrary::Get()->GetSystemSaltSync(); 242 if (system_salt.empty()) { 243 const int64 kRequestSystemSaltDelayMs = 500; 244 content::BrowserThread::PostDelayedTask( 245 content::BrowserThread::UI, 246 FROM_HERE, 247 base::Bind(&DeviceOAuth2TokenService::SetAndSaveRefreshToken, 248 weak_ptr_factory_.GetWeakPtr(), 249 refresh_token), 250 base::TimeDelta::FromMilliseconds(kRequestSystemSaltDelayMs)); 251 return; 252 } 253 254 std::string encrypted_refresh_token = 255 token_encryptor_->EncryptWithSystemSalt(refresh_token); 256 257 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken, 258 encrypted_refresh_token); 259} 260 261std::string DeviceOAuth2TokenService::GetRefreshToken( 262 const std::string& account_id) { 263 DCHECK_EQ(account_id, GetRobotAccountId()); 264 if (refresh_token_.empty()) { 265 std::string encrypted_refresh_token = 266 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); 267 268 // TODO(xiyuan): This needs a proper fix after M31. 269 LOG_IF(ERROR, CryptohomeLibrary::Get()->GetSystemSaltSync().empty()) 270 << "System salt is not available for decryption"; 271 272 refresh_token_ = token_encryptor_->DecryptWithSystemSalt( 273 encrypted_refresh_token); 274 } 275 return refresh_token_; 276} 277 278std::string DeviceOAuth2TokenService::GetRobotAccountId() { 279 policy::BrowserPolicyConnector* connector = 280 g_browser_process->browser_policy_connector(); 281 if (connector) 282 return connector->GetDeviceCloudPolicyManager()->GetRobotAccountId(); 283 return std::string(); 284} 285 286net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() { 287 return url_request_context_getter_.get(); 288} 289 290scoped_ptr<OAuth2TokenService::RequestImpl> 291DeviceOAuth2TokenService::CreateRequest( 292 OAuth2TokenService::Consumer* consumer) { 293 if (refresh_token_is_valid_) 294 return OAuth2TokenService::CreateRequest(consumer); 295 296 // Substitute our own consumer to wait for refresh token validation. 297 scoped_ptr<ValidatingConsumer> validating_consumer( 298 new ValidatingConsumer(this, consumer)); 299 validating_consumer->StartValidation(); 300 return validating_consumer.PassAs<RequestImpl>(); 301} 302 303} // namespace chromeos 304