device_oauth2_token_service.cc revision 3551c9c881056c480085172ff9840cab31610854
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 // Also, make copies of the parameters passed to the consumer to avoid invalid 197 // memory accesses when the consumer deletes |this| immediately. 198 if (!token_is_valid_) { 199 consumer_->OnGetTokenFailure(this, GoogleServiceAuthError( 200 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS)); 201 } else if (error_) { 202 GoogleServiceAuthError error_copy = *error_; 203 consumer_->OnGetTokenFailure(this, error_copy); 204 } else { 205 std::string access_token_copy = access_token_; 206 base::Time expiration_time_copy = expiration_time_; 207 consumer_->OnGetTokenSuccess(this, access_token_copy, expiration_time_copy); 208 } 209} 210 211DeviceOAuth2TokenService::DeviceOAuth2TokenService( 212 net::URLRequestContextGetter* getter, 213 PrefService* local_state) 214 : refresh_token_is_valid_(false), 215 max_refresh_token_validation_retries_(3), 216 url_request_context_getter_(getter), 217 local_state_(local_state) { 218} 219 220DeviceOAuth2TokenService::~DeviceOAuth2TokenService() { 221} 222 223scoped_ptr<OAuth2TokenService::Request> DeviceOAuth2TokenService::StartRequest( 224 const OAuth2TokenService::ScopeSet& scopes, 225 OAuth2TokenService::Consumer* consumer) { 226 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 227 228 if (refresh_token_is_valid_) { 229 return OAuth2TokenService::StartRequest(scopes, consumer).Pass(); 230 } else { 231 scoped_ptr<ValidatingConsumer> validating_consumer( 232 new ValidatingConsumer(this, consumer)); 233 234 scoped_ptr<Request> request = OAuth2TokenService::StartRequest( 235 scopes, validating_consumer.get()); 236 validating_consumer->StartValidation(request.Pass()); 237 return validating_consumer.PassAs<Request>(); 238 } 239} 240 241void DeviceOAuth2TokenService::OnValidationComplete( 242 bool refresh_token_is_valid) { 243 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 244 refresh_token_is_valid_ = refresh_token_is_valid; 245} 246 247// static 248void DeviceOAuth2TokenService::RegisterPrefs(PrefRegistrySimple* registry) { 249 registry->RegisterStringPref(prefs::kDeviceRobotAnyApiRefreshToken, 250 std::string()); 251} 252 253void DeviceOAuth2TokenService::SetAndSaveRefreshToken( 254 const std::string& refresh_token) { 255 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::UI)); 256 std::string encrypted_refresh_token = 257 CryptohomeLibrary::Get()->EncryptWithSystemSalt(refresh_token); 258 259 local_state_->SetString(prefs::kDeviceRobotAnyApiRefreshToken, 260 encrypted_refresh_token); 261} 262 263std::string DeviceOAuth2TokenService::GetRefreshToken() { 264 if (refresh_token_.empty()) { 265 std::string encrypted_refresh_token = 266 local_state_->GetString(prefs::kDeviceRobotAnyApiRefreshToken); 267 268 refresh_token_ = CryptohomeLibrary::Get()->DecryptWithSystemSalt( 269 encrypted_refresh_token); 270 } 271 return refresh_token_; 272} 273 274std::string DeviceOAuth2TokenService::GetRobotAccountId() { 275 policy::BrowserPolicyConnector* connector = 276 g_browser_process->browser_policy_connector(); 277 if (connector) 278 return connector->GetDeviceCloudPolicyManager()->GetRobotAccountId(); 279 return std::string(); 280} 281 282net::URLRequestContextGetter* DeviceOAuth2TokenService::GetRequestContext() { 283 return url_request_context_getter_.get(); 284} 285 286} // namespace chromeos 287