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