| OLD | NEW |
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include "google_apis/gaia/gaia_oauth_client.h" | 5 #include "google_apis/gaia/gaia_oauth_client.h" |
| 6 | 6 |
| 7 #include "base/json/json_reader.h" | 7 #include "base/json/json_reader.h" |
| 8 #include "base/logging.h" | 8 #include "base/logging.h" |
| 9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/string_util.h" |
| 10 #include "base/values.h" | 11 #include "base/values.h" |
| 11 #include "google_apis/gaia/gaia_urls.h" | 12 #include "google_apis/gaia/gaia_urls.h" |
| 12 #include "googleurl/src/gurl.h" | 13 #include "googleurl/src/gurl.h" |
| 13 #include "net/base/escape.h" | 14 #include "net/base/escape.h" |
| 14 #include "net/http/http_status_code.h" | 15 #include "net/http/http_status_code.h" |
| 15 #include "net/url_request/url_fetcher.h" | 16 #include "net/url_request/url_fetcher.h" |
| 16 #include "net/url_request/url_fetcher_delegate.h" | 17 #include "net/url_request/url_fetcher_delegate.h" |
| 17 #include "net/url_request/url_request_context_getter.h" | 18 #include "net/url_request/url_request_context_getter.h" |
| 18 | 19 |
| 19 namespace { | 20 namespace { |
| 20 const char kAccessTokenValue[] = "access_token"; | 21 const char kAccessTokenValue[] = "access_token"; |
| 21 const char kRefreshTokenValue[] = "refresh_token"; | 22 const char kRefreshTokenValue[] = "refresh_token"; |
| 22 const char kExpiresInValue[] = "expires_in"; | 23 const char kExpiresInValue[] = "expires_in"; |
| 23 } | 24 } |
| 24 | 25 |
| 25 namespace gaia { | 26 namespace gaia { |
| 26 | 27 |
| 28 // Use a non-zero number, so unit tests can differentiate the URLFetcher used by |
| 29 // this class from other fetchers (most other code just hardcodes the ID to 0). |
| 30 const int GaiaOAuthClient::kUrlFetcherId = 17109006; |
| 31 |
| 27 class GaiaOAuthClient::Core | 32 class GaiaOAuthClient::Core |
| 28 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, | 33 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, |
| 29 public net::URLFetcherDelegate { | 34 public net::URLFetcherDelegate { |
| 30 public: | 35 public: |
| 31 Core(const std::string& gaia_url, | 36 Core(net::URLRequestContextGetter* request_context_getter) |
| 32 net::URLRequestContextGetter* request_context_getter) | 37 : num_retries_(0), |
| 33 : gaia_url_(gaia_url), | 38 request_context_getter_(request_context_getter), |
| 34 num_retries_(0), | 39 delegate_(NULL), |
| 35 request_context_getter_(request_context_getter), | 40 request_type_(NO_PENDING_REQUEST) { |
| 36 delegate_(NULL), | |
| 37 request_type_(NO_PENDING_REQUEST) { | |
| 38 } | 41 } |
| 39 | 42 |
| 40 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, | 43 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, |
| 41 const std::string& auth_code, | 44 const std::string& auth_code, |
| 42 int max_retries, | 45 int max_retries, |
| 43 GaiaOAuthClient::Delegate* delegate); | 46 GaiaOAuthClient::Delegate* delegate); |
| 44 void RefreshToken(const OAuthClientInfo& oauth_client_info, | 47 void RefreshToken(const OAuthClientInfo& oauth_client_info, |
| 45 const std::string& refresh_token, | 48 const std::string& refresh_token, |
| 49 const std::vector<std::string>& scopes, |
| 46 int max_retries, | 50 int max_retries, |
| 47 GaiaOAuthClient::Delegate* delegate); | 51 GaiaOAuthClient::Delegate* delegate); |
| 48 void GetUserInfo(const std::string& oauth_access_token, | 52 void GetUserInfo(const std::string& oauth_access_token, |
| 53 int max_retries, |
| 54 Delegate* delegate); |
| 55 void GetTokenInfo(const std::string& oauth_access_token, |
| 49 int max_retries, | 56 int max_retries, |
| 50 Delegate* delegate); | 57 Delegate* delegate); |
| 51 | 58 |
| 52 // net::URLFetcherDelegate implementation. | 59 // net::URLFetcherDelegate implementation. |
| 53 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; | 60 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; |
| 54 | 61 |
| 55 private: | 62 private: |
| 56 friend class base::RefCountedThreadSafe<Core>; | 63 friend class base::RefCountedThreadSafe<Core>; |
| 57 | 64 |
| 58 enum RequestType { | 65 enum RequestType { |
| 59 NO_PENDING_REQUEST, | 66 NO_PENDING_REQUEST, |
| 60 TOKENS_FROM_AUTH_CODE, | 67 TOKENS_FROM_AUTH_CODE, |
| 61 REFRESH_TOKEN, | 68 REFRESH_TOKEN, |
| 69 TOKEN_INFO, |
| 62 USER_INFO, | 70 USER_INFO, |
| 63 }; | 71 }; |
| 64 | 72 |
| 65 virtual ~Core() {} | 73 virtual ~Core() {} |
| 66 | 74 |
| 67 void MakeGaiaRequest(const std::string& post_body, | 75 void MakeGaiaRequest(const GURL& url, |
| 76 const std::string& post_body, |
| 68 int max_retries, | 77 int max_retries, |
| 69 GaiaOAuthClient::Delegate* delegate); | 78 GaiaOAuthClient::Delegate* delegate); |
| 70 void HandleResponse(const net::URLFetcher* source, | 79 void HandleResponse(const net::URLFetcher* source, |
| 71 bool* should_retry_request); | 80 bool* should_retry_request); |
| 72 | 81 |
| 73 GURL gaia_url_; | |
| 74 int num_retries_; | 82 int num_retries_; |
| 75 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 83 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
| 76 GaiaOAuthClient::Delegate* delegate_; | 84 GaiaOAuthClient::Delegate* delegate_; |
| 77 scoped_ptr<net::URLFetcher> request_; | 85 scoped_ptr<net::URLFetcher> request_; |
| 78 RequestType request_type_; | 86 RequestType request_type_; |
| 79 }; | 87 }; |
| 80 | 88 |
| 81 void GaiaOAuthClient::Core::GetTokensFromAuthCode( | 89 void GaiaOAuthClient::Core::GetTokensFromAuthCode( |
| 82 const OAuthClientInfo& oauth_client_info, | 90 const OAuthClientInfo& oauth_client_info, |
| 83 const std::string& auth_code, | 91 const std::string& auth_code, |
| 84 int max_retries, | 92 int max_retries, |
| 85 GaiaOAuthClient::Delegate* delegate) { | 93 GaiaOAuthClient::Delegate* delegate) { |
| 86 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 94 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
| 87 request_type_ = TOKENS_FROM_AUTH_CODE; | 95 request_type_ = TOKENS_FROM_AUTH_CODE; |
| 88 std::string post_body = | 96 std::string post_body = |
| 89 "code=" + net::EscapeUrlEncodedData(auth_code, true) + | 97 "code=" + net::EscapeUrlEncodedData(auth_code, true) + |
| 90 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 98 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
| 91 true) + | 99 true) + |
| 92 "&client_secret=" + | 100 "&client_secret=" + |
| 93 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 101 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
| 94 "&redirect_uri=" + | 102 "&redirect_uri=" + |
| 95 net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) + | 103 net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) + |
| 96 "&grant_type=authorization_code"; | 104 "&grant_type=authorization_code"; |
| 97 MakeGaiaRequest(post_body, max_retries, delegate); | 105 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), |
| 106 post_body, max_retries, delegate); |
| 98 } | 107 } |
| 99 | 108 |
| 100 void GaiaOAuthClient::Core::RefreshToken( | 109 void GaiaOAuthClient::Core::RefreshToken( |
| 101 const OAuthClientInfo& oauth_client_info, | 110 const OAuthClientInfo& oauth_client_info, |
| 102 const std::string& refresh_token, | 111 const std::string& refresh_token, |
| 112 const std::vector<std::string>& scopes, |
| 103 int max_retries, | 113 int max_retries, |
| 104 GaiaOAuthClient::Delegate* delegate) { | 114 GaiaOAuthClient::Delegate* delegate) { |
| 105 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 115 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
| 106 request_type_ = REFRESH_TOKEN; | 116 request_type_ = REFRESH_TOKEN; |
| 107 std::string post_body = | 117 std::string post_body = |
| 108 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 118 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + |
| 109 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 119 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
| 110 true) + | 120 true) + |
| 111 "&client_secret=" + | 121 "&client_secret=" + |
| 112 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 122 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
| 113 "&grant_type=refresh_token"; | 123 "&grant_type=refresh_token"; |
| 114 MakeGaiaRequest(post_body, max_retries, delegate); | 124 |
| 125 if (!scopes.empty()) { |
| 126 std::string scopes_string = JoinString(scopes, ' '); |
| 127 post_body += "&scope=" + net::EscapeUrlEncodedData(scopes_string, true); |
| 128 } |
| 129 |
| 130 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), |
| 131 post_body, max_retries, delegate); |
| 115 } | 132 } |
| 116 | 133 |
| 117 void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token, | 134 void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token, |
| 118 int max_retries, | 135 int max_retries, |
| 119 Delegate* delegate) { | 136 Delegate* delegate) { |
| 120 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 137 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
| 121 DCHECK(!request_.get()); | 138 DCHECK(!request_.get()); |
| 122 request_type_ = USER_INFO; | 139 request_type_ = USER_INFO; |
| 123 delegate_ = delegate; | 140 delegate_ = delegate; |
| 124 num_retries_ = 0; | 141 num_retries_ = 0; |
| 125 request_.reset(net::URLFetcher::Create( | 142 request_.reset(net::URLFetcher::Create( |
| 126 0, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()), | 143 kUrlFetcherId, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()), |
| 127 net::URLFetcher::GET, this)); | 144 net::URLFetcher::GET, this)); |
| 128 request_->SetRequestContext(request_context_getter_.get()); | 145 request_->SetRequestContext(request_context_getter_.get()); |
| 129 request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token); | 146 request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token); |
| 130 request_->SetMaxRetriesOn5xx(max_retries); | 147 request_->SetMaxRetriesOn5xx(max_retries); |
| 131 // Fetchers are sometimes cancelled because a network change was detected, | 148 // Fetchers are sometimes cancelled because a network change was detected, |
| 132 // especially at startup and after sign-in on ChromeOS. Retrying once should | 149 // especially at startup and after sign-in on ChromeOS. Retrying once should |
| 133 // be enough in those cases; let the fetcher retry up to 3 times just in case. | 150 // be enough in those cases; let the fetcher retry up to 3 times just in case. |
| 134 // http://crbug.com/163710 | 151 // http://crbug.com/163710 |
| 135 request_->SetAutomaticallyRetryOnNetworkChanges(3); | 152 request_->SetAutomaticallyRetryOnNetworkChanges(3); |
| 136 request_->Start(); | 153 request_->Start(); |
| 137 } | 154 } |
| 138 | 155 |
| 156 void GaiaOAuthClient::Core::GetTokenInfo(const std::string& oauth_access_token, |
| 157 int max_retries, |
| 158 Delegate* delegate) { |
| 159 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
| 160 DCHECK(!request_.get()); |
| 161 request_type_ = TOKEN_INFO; |
| 162 std::string post_body = |
| 163 "access_token=" + net::EscapeUrlEncodedData(oauth_access_token, true); |
| 164 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()), |
| 165 post_body, |
| 166 max_retries, |
| 167 delegate); |
| 168 } |
| 169 |
| 139 void GaiaOAuthClient::Core::MakeGaiaRequest( | 170 void GaiaOAuthClient::Core::MakeGaiaRequest( |
| 171 const GURL& url, |
| 140 const std::string& post_body, | 172 const std::string& post_body, |
| 141 int max_retries, | 173 int max_retries, |
| 142 GaiaOAuthClient::Delegate* delegate) { | 174 GaiaOAuthClient::Delegate* delegate) { |
| 143 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 175 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; |
| 144 delegate_ = delegate; | 176 delegate_ = delegate; |
| 145 num_retries_ = 0; | 177 num_retries_ = 0; |
| 146 request_.reset(net::URLFetcher::Create( | 178 request_.reset(net::URLFetcher::Create( |
| 147 0, gaia_url_, net::URLFetcher::POST, this)); | 179 kUrlFetcherId, url, net::URLFetcher::POST, this)); |
| 148 request_->SetRequestContext(request_context_getter_.get()); | 180 request_->SetRequestContext(request_context_getter_.get()); |
| 149 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 181 request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
| 150 request_->SetMaxRetriesOn5xx(max_retries); | 182 request_->SetMaxRetriesOn5xx(max_retries); |
| 151 // See comment on SetAutomaticallyRetryOnNetworkChanges() above. | 183 // See comment on SetAutomaticallyRetryOnNetworkChanges() above. |
| 152 request_->SetAutomaticallyRetryOnNetworkChanges(3); | 184 request_->SetAutomaticallyRetryOnNetworkChanges(3); |
| 153 request_->Start(); | 185 request_->Start(); |
| 154 } | 186 } |
| 155 | 187 |
| 156 // URLFetcher::Delegate implementation. | 188 // URLFetcher::Delegate implementation. |
| 157 void GaiaOAuthClient::Core::OnURLFetchComplete( | 189 void GaiaOAuthClient::Core::OnURLFetchComplete( |
| (...skipping 10 matching lines...) Expand all Loading... |
| 168 // We must set our request_context_getter_ again because | 200 // We must set our request_context_getter_ again because |
| 169 // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... | 201 // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... |
| 170 request_->SetRequestContext(request_context_getter_.get()); | 202 request_->SetRequestContext(request_context_getter_.get()); |
| 171 request_->Start(); | 203 request_->Start(); |
| 172 } | 204 } |
| 173 } | 205 } |
| 174 | 206 |
| 175 void GaiaOAuthClient::Core::HandleResponse( | 207 void GaiaOAuthClient::Core::HandleResponse( |
| 176 const net::URLFetcher* source, | 208 const net::URLFetcher* source, |
| 177 bool* should_retry_request) { | 209 bool* should_retry_request) { |
| 178 // Keep the URLFetcher object in case we need to reuse it. | 210 // Move ownership of the request fetcher into a local scoped_ptr which |
| 211 // will be nuked when we're done handling the request, unless we need |
| 212 // to retry, in which case ownership will be returned to request_. |
| 179 scoped_ptr<net::URLFetcher> old_request = request_.Pass(); | 213 scoped_ptr<net::URLFetcher> old_request = request_.Pass(); |
| 180 DCHECK_EQ(source, old_request.get()); | 214 DCHECK_EQ(source, old_request.get()); |
| 181 | 215 |
| 182 // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are | 216 // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are |
| 183 // done here. | 217 // done here. |
| 184 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { | 218 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { |
| 185 delegate_->OnOAuthError(); | 219 delegate_->OnOAuthError(); |
| 186 return; | 220 return; |
| 187 } | 221 } |
| 188 | 222 |
| (...skipping 27 matching lines...) Expand all Loading... |
| 216 request_type_ = NO_PENDING_REQUEST; | 250 request_type_ = NO_PENDING_REQUEST; |
| 217 | 251 |
| 218 switch (type) { | 252 switch (type) { |
| 219 case USER_INFO: { | 253 case USER_INFO: { |
| 220 std::string email; | 254 std::string email; |
| 221 response_dict->GetString("email", &email); | 255 response_dict->GetString("email", &email); |
| 222 delegate_->OnGetUserInfoResponse(email); | 256 delegate_->OnGetUserInfoResponse(email); |
| 223 break; | 257 break; |
| 224 } | 258 } |
| 225 | 259 |
| 260 case TOKEN_INFO: { |
| 261 delegate_->OnGetTokenInfoResponse(response_dict.Pass()); |
| 262 break; |
| 263 } |
| 264 |
| 226 case TOKENS_FROM_AUTH_CODE: | 265 case TOKENS_FROM_AUTH_CODE: |
| 227 case REFRESH_TOKEN: { | 266 case REFRESH_TOKEN: { |
| 228 std::string access_token; | 267 std::string access_token; |
| 229 std::string refresh_token; | 268 std::string refresh_token; |
| 230 int expires_in_seconds = 0; | 269 int expires_in_seconds = 0; |
| 231 response_dict->GetString(kAccessTokenValue, &access_token); | 270 response_dict->GetString(kAccessTokenValue, &access_token); |
| 232 response_dict->GetString(kRefreshTokenValue, &refresh_token); | 271 response_dict->GetString(kRefreshTokenValue, &refresh_token); |
| 233 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); | 272 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); |
| 234 | 273 |
| 235 if (access_token.empty()) { | 274 if (access_token.empty()) { |
| 236 delegate_->OnOAuthError(); | 275 delegate_->OnOAuthError(); |
| 237 return; | 276 return; |
| 238 } | 277 } |
| 239 | 278 |
| 240 if (type == REFRESH_TOKEN) { | 279 if (type == REFRESH_TOKEN) { |
| 241 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); | 280 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); |
| 242 } else { | 281 } else { |
| 243 delegate_->OnGetTokensResponse(refresh_token, | 282 delegate_->OnGetTokensResponse(refresh_token, |
| 244 access_token, | 283 access_token, |
| 245 expires_in_seconds); | 284 expires_in_seconds); |
| 246 } | 285 } |
| 247 break; | 286 break; |
| 248 } | 287 } |
| 249 | 288 |
| 250 default: | 289 default: |
| 251 NOTREACHED(); | 290 NOTREACHED(); |
| 252 } | 291 } |
| 253 } | 292 } |
| 254 | 293 |
| 255 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, | 294 GaiaOAuthClient::GaiaOAuthClient(net::URLRequestContextGetter* context_getter) { |
| 256 net::URLRequestContextGetter* context_getter) { | 295 core_ = new Core(context_getter); |
| 257 core_ = new Core(gaia_url, context_getter); | |
| 258 } | 296 } |
| 259 | 297 |
| 260 GaiaOAuthClient::~GaiaOAuthClient() { | 298 GaiaOAuthClient::~GaiaOAuthClient() { |
| 261 } | 299 } |
| 262 | 300 |
| 263 void GaiaOAuthClient::GetTokensFromAuthCode( | 301 void GaiaOAuthClient::GetTokensFromAuthCode( |
| 264 const OAuthClientInfo& oauth_client_info, | 302 const OAuthClientInfo& oauth_client_info, |
| 265 const std::string& auth_code, | 303 const std::string& auth_code, |
| 266 int max_retries, | 304 int max_retries, |
| 267 Delegate* delegate) { | 305 Delegate* delegate) { |
| 268 return core_->GetTokensFromAuthCode(oauth_client_info, | 306 return core_->GetTokensFromAuthCode(oauth_client_info, |
| 269 auth_code, | 307 auth_code, |
| 270 max_retries, | 308 max_retries, |
| 271 delegate); | 309 delegate); |
| 272 } | 310 } |
| 273 | 311 |
| 274 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 312 void GaiaOAuthClient::RefreshToken( |
| 275 const std::string& refresh_token, | 313 const OAuthClientInfo& oauth_client_info, |
| 276 int max_retries, | 314 const std::string& refresh_token, |
| 277 Delegate* delegate) { | 315 const std::vector<std::string>& scopes, |
| 316 int max_retries, |
| 317 Delegate* delegate) { |
| 278 return core_->RefreshToken(oauth_client_info, | 318 return core_->RefreshToken(oauth_client_info, |
| 279 refresh_token, | 319 refresh_token, |
| 320 scopes, |
| 280 max_retries, | 321 max_retries, |
| 281 delegate); | 322 delegate); |
| 282 } | 323 } |
| 283 | 324 |
| 284 void GaiaOAuthClient::GetUserInfo(const std::string& access_token, | 325 void GaiaOAuthClient::GetUserInfo(const std::string& access_token, |
| 285 int max_retries, | 326 int max_retries, |
| 286 Delegate* delegate) { | 327 Delegate* delegate) { |
| 287 return core_->GetUserInfo(access_token, max_retries, delegate); | 328 return core_->GetUserInfo(access_token, max_retries, delegate); |
| 288 } | 329 } |
| 289 | 330 |
| 331 void GaiaOAuthClient::GetTokenInfo(const std::string& access_token, |
| 332 int max_retries, |
| 333 Delegate* delegate) { |
| 334 return core_->GetTokenInfo(access_token, max_retries, delegate); |
| 335 } |
| 336 |
| 290 } // namespace gaia | 337 } // namespace gaia |
| OLD | NEW |