device_oauth2_token_service.cc revision a36e5920737c6adbddd3e43b760e5de8431db6e0
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/policy/browser_policy_connector.h" 16#include "chrome/browser/policy/proto/cloud/device_management_backend.pb.h" 17#include "chrome/common/pref_names.h" 18#include "chromeos/cryptohome/cryptohome_library.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} // namespace 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 implements the Request interface, 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::Request, 38 public gaia::GaiaOAuthClient::Delegate { 39 public: 40 explicit ValidatingConsumer(DeviceOAuth2TokenService* token_service, 41 Consumer* consumer); 42 virtual ~ValidatingConsumer(); 43 44 void StartValidation(scoped_ptr<Request> request); 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 // The request instance returned by OAuth2TokenService, which we're 80 // wrapping. If the this object is deleted, |request_| will also be 81 // deleted and the OAuth2TokenService won't call back on this object. 82 scoped_ptr<OAuth2TokenService::Request> request_; 83 84 // OAuth2TokenService::Consumer results 85 bool token_fetch_done_; 86 std::string access_token_; 87 base::Time expiration_time_; 88 scoped_ptr<GoogleServiceAuthError> error_; 89}; 90 91DeviceOAuth2TokenService::ValidatingConsumer::ValidatingConsumer( 92 DeviceOAuth2TokenService* token_service, 93 Consumer* consumer) 94 : token_service_(token_service), 95 consumer_(consumer), 96 token_validation_done_(false), 97 token_is_valid_(false), 98 token_fetch_done_(false) { 99} 100 101DeviceOAuth2TokenService::ValidatingConsumer::~ValidatingConsumer() { 102} 103 104void DeviceOAuth2TokenService::ValidatingConsumer::StartValidation( 105 scoped_ptr<Request> request) { 106 DCHECK(!gaia_oauth_client_); 107 request_ = request.Pass(); 108 gaia_oauth_client_.reset(new gaia::GaiaOAuthClient( 109 g_browser_process->system_request_context())); 110 111 GaiaUrls* gaia_urls = GaiaUrls::GetInstance(); 112 gaia::OAuthClientInfo client_info; 113 client_info.client_id = gaia_urls->oauth2_chrome_client_id(); 114 client_info.client_secret = gaia_urls->oauth2_chrome_client_secret(); 115 116 gaia_oauth_client_->RefreshToken( 117 client_info, 118 token_service_->GetRefreshToken(), 119 std::vector<std::string>(1, kServiceScopeGetUserInfo), 120 token_service_->max_refresh_token_validation_retries_, 121 this); 122} 123 124void DeviceOAuth2TokenService::ValidatingConsumer::OnRefreshTokenResponse( 125 const std::string& access_token, 126 int expires_in_seconds) { 127 gaia_oauth_client_->GetTokenInfo( 128 access_token, 129 token_service_->max_refresh_token_validation_retries_, 130 this); 131} 132 133void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenInfoResponse( 134 scoped_ptr<DictionaryValue> token_info) { 135 std::string gaia_robot_id; 136 token_info->GetString("email", &gaia_robot_id); 137 138 std::string policy_robot_id = token_service_->GetRobotAccountId(); 139 140 if (policy_robot_id == gaia_robot_id) { 141 RefreshTokenIsValid(true); 142 } else { 143 if (gaia_robot_id.empty()) { 144 LOG(WARNING) << "Device service account owner in policy is empty."; 145 } else { 146 LOG(INFO) << "Device service account owner in policy does not match " 147 << "refresh token owner \"" << gaia_robot_id << "\"."; 148 } 149 RefreshTokenIsValid(false); 150 } 151} 152 153void DeviceOAuth2TokenService::ValidatingConsumer::OnOAuthError() { 154 RefreshTokenIsValid(false); 155} 156 157void DeviceOAuth2TokenService::ValidatingConsumer::OnNetworkError( 158 int response_code) { 159 RefreshTokenIsValid(false); 160} 161 162void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenSuccess( 163 const Request* request, 164 const std::string& access_token, 165 const base::Time& expiration_time) { 166 token_fetch_done_ = true; 167 access_token_ = access_token; 168 expiration_time_ = expiration_time; 169 if (token_validation_done_) 170 InformConsumer(); 171} 172 173void DeviceOAuth2TokenService::ValidatingConsumer::OnGetTokenFailure( 174 const Request* request, 175 const GoogleServiceAuthError& error) { 176 token_fetch_done_ = true; 177 error_.reset(new GoogleServiceAuthError(error.state())); 178 if (token_validation_done_) 179 InformConsumer(); 180} 181 182void DeviceOAuth2TokenService::ValidatingConsumer::RefreshTokenIsValid( 183 bool is_valid) { 184 token_validation_done_ = true; 185 token_is_valid_ = is_valid; 186 if (token_fetch_done_) 187 InformConsumer(); 188} 189 190void DeviceOAuth2TokenService::ValidatingConsumer::InformConsumer() { 191 DCHECK(token_fetch_done_); 192 DCHECK(token_validation_done_); 193 token_service_->OnValidationComplete(token_is_valid_); 194 // Note: this object (which is also the Request instance) may be deleted in 195 // these consumer callbacks, so the callbacks must be the last line executed. 196 if (!token_is_valid_) { 197 consumer_->OnGetTokenFailure(this, GoogleServiceAuthError( 198 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 199 } else if (error_) { 200 consumer_->OnGetTokenFailure(this, *error_.get()); 201 } else { 202 consumer_->OnGetTokenSuccess(this, access_token_, expiration_time_); 203 } 204} 205 206DeviceOAuth2TokenService::DeviceOAuth2TokenService( 207 net::URLRequestContextGetter* getter, 208 PrefService* local_state) 209 : refresh_token_is_valid_(false), 210 max_refresh_token_validation_retries_(3), 211 url_request_context_getter_(getter), 212 local_state_(local_state) { 213} 214 215DeviceOAuth2TokenService::~DeviceOAuth2TokenService() { 216} 217 218net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() { 219 return url_request_context_getter_.get(); 220} 221 222scoped_ptr<OAuth2TokenService::Request> DeviceOAuth2TokenService::StartRequest( 223 const OAuth2TokenService::ScopeSet& scopes, 224 OAuth2TokenService::Consumer* consumer) { 225 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 226 227 if (refresh_token_is_valid_) { 228 return OAuth2TokenService::StartRequest(scopes, consumer).Pass(); 229 } else { 230 scoped_ptr<ValidatingConsumer> validating_consumer( 231 new ValidatingConsumer(this, consumer)); 232 233 scoped_ptr<Request> request = OAuth2TokenService::StartRequest( 234 scopes, validating_consumer.get()); 235 validating_consumer->StartValidation(request.Pass()); 236 return validating_consumer.PassAs<Request>(); 237 } 238} 239 240void DeviceOAuth2TokenService::OnValidationComplete( 241 bool refresh_token_is_valid) { 242 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 243 refresh_token_is_valid_ = refresh_token_is_valid; 244} 245 246// static 247void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) { 248 registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken, 249 std::string()); 250} 251 252void DeviceOAuth2TokenService::SetAndSaveRefreshToken( 253 const std::string& refresh_token) { 254 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 255 std::string encrypted_refresh_token = 256 CryptohomeLibrary::Get()->EncryptWithSystemSalt(refresh_token); 257 258 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken, 259 encrypted_refresh_token); 260} 261 262std::string DeviceOAuth2TokenService::GetRefreshToken() { 263 if (refresh_token_.empty()) { 264 std::string encrypted_refresh_token = 265 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); 266 267 refresh_token_ = CryptohomeLibrary::Get()->DecryptWithSystemSalt( 268 encrypted_refresh_token); 269 } 270 return refresh_token_; 271} 272 273std::string DeviceOAuth2TokenService::GetRobotAccountId() { 274 policy::BrowserPolicyConnector* connector = 275 g_browser_process->browser_policy_connector(); 276 if (connector) 277 return connector->GetDeviceCloudPolicyManager()->GetRobotAccountId(); 278 return std::string(); 279} 280 281} // namespace chromeos 282