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 3 // found in the LICENSE file. 4 5 #include "chrome/browser/net/gaia/gaia_oauth_fetcher.h" 6 7 #include <string> 8 #include <utility> 9 #include <vector> 10 11 #include "base/json/json_reader.h" 12 #include "base/strings/string_split.h" 13 #include "base/strings/string_util.h" 14 #include "base/values.h" 15 #include "chrome/browser/net/gaia/gaia_oauth_consumer.h" 16 #include "google_apis/gaia/gaia_auth_fetcher.h" 17 #include "google_apis/gaia/gaia_constants.h" 18 #include "google_apis/gaia/gaia_urls.h" 19 #include "google_apis/gaia/oauth_request_signer.h" 20 #include "grit/chromium_strings.h" 21 #include "net/base/load_flags.h" 22 #include "net/cookies/parsed_cookie.h" 23 #include "net/http/http_status_code.h" 24 #include "net/url_request/url_fetcher.h" 25 #include "net/url_request/url_request_context_getter.h" 26 #include "net/url_request/url_request_status.h" 27 #include "ui/base/l10n/l10n_util.h" 28 29 static const char kOAuthTokenCookie[] = "oauth_token"; 30 31 GaiaOAuthFetcher::GaiaOAuthFetcher(GaiaOAuthConsumer* consumer, 32 net::URLRequestContextGetter* getter, 33 const std::string& service_scope) 34 : consumer_(consumer), 35 getter_(getter), 36 service_scope_(service_scope), 37 fetch_pending_(false), 38 auto_fetch_limit_(USER_INFO) {} 39 40 GaiaOAuthFetcher::~GaiaOAuthFetcher() {} 41 42 bool GaiaOAuthFetcher::HasPendingFetch() const { 43 return fetch_pending_; 44 } 45 46 void GaiaOAuthFetcher::CancelRequest() { 47 fetcher_.reset(); 48 fetch_pending_ = false; 49 } 50 51 // static 52 net::URLFetcher* GaiaOAuthFetcher::CreateGaiaFetcher( 53 net::URLRequestContextGetter* getter, 54 const GURL& gaia_gurl, 55 const std::string& body, 56 const std::string& headers, 57 bool send_cookies, 58 net::URLFetcherDelegate* delegate) { 59 bool empty_body = body.empty(); 60 net::URLFetcher* result = net::URLFetcher::Create( 61 0, gaia_gurl, 62 empty_body ? net::URLFetcher::GET : net::URLFetcher::POST, 63 delegate); 64 result->SetRequestContext(getter); 65 // Fetchers are sometimes cancelled because a network change was detected, 66 // especially at startup and after sign-in on ChromeOS. Retrying once should 67 // be enough in those cases; let the fetcher retry up to 3 times just in case. 68 // http://crbug.com/163710 69 result->SetAutomaticallyRetryOnNetworkChanges(3); 70 71 // The Gaia/OAuth token exchange requests do not require any cookie-based 72 // identification as part of requests. We suppress sending any cookies to 73 // maintain a separation between the user's browsing and Chrome's internal 74 // services. Where such mixing is desired (prelogin, autologin 75 // or chromeos login), it will be done explicitly. 76 if (!send_cookies) 77 result->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES); 78 79 if (!empty_body) 80 result->SetUploadData("application/x-www-form-urlencoded", body); 81 if (!headers.empty()) 82 result->SetExtraRequestHeaders(headers); 83 84 return result; 85 } 86 87 // static 88 GURL GaiaOAuthFetcher::MakeGetOAuthTokenUrl( 89 const std::string& oauth1_login_scope, 90 const std::string& product_name) { 91 return GaiaUrls::GetInstance()->get_oauth_token_url().Resolve( 92 "?scope=" + oauth1_login_scope + 93 "&xoauth_display_name=" + 94 OAuthRequestSigner::Encode(product_name)); 95 } 96 97 // static 98 std::string GaiaOAuthFetcher::MakeOAuthLoginBody( 99 const char* source, 100 const char* service, 101 const std::string& oauth1_access_token, 102 const std::string& oauth1_access_token_secret) { 103 OAuthRequestSigner::Parameters parameters; 104 parameters["service"] = service; 105 parameters["source"] = source; 106 std::string signed_request; 107 bool is_signed = OAuthRequestSigner::SignURL( 108 GaiaUrls::GetInstance()->oauth1_login_url(), 109 parameters, 110 OAuthRequestSigner::HMAC_SHA1_SIGNATURE, 111 OAuthRequestSigner::POST_METHOD, 112 "anonymous", // oauth_consumer_key 113 "anonymous", // consumer secret 114 oauth1_access_token, // oauth_token 115 oauth1_access_token_secret, // token secret 116 &signed_request); 117 DCHECK(is_signed); 118 return signed_request; 119 } 120 121 // static 122 std::string GaiaOAuthFetcher::MakeOAuthGetAccessTokenBody( 123 const std::string& oauth1_request_token) { 124 OAuthRequestSigner::Parameters empty_parameters; 125 std::string signed_request; 126 bool is_signed = OAuthRequestSigner::SignURL( 127 GaiaUrls::GetInstance()->oauth_get_access_token_url(), 128 empty_parameters, 129 OAuthRequestSigner::HMAC_SHA1_SIGNATURE, 130 OAuthRequestSigner::POST_METHOD, 131 "anonymous", // oauth_consumer_key 132 "anonymous", // consumer secret 133 oauth1_request_token, // oauth_token 134 "", // token secret 135 &signed_request); 136 DCHECK(is_signed); 137 return signed_request; 138 } 139 140 // static 141 std::string GaiaOAuthFetcher::MakeOAuthWrapBridgeBody( 142 const std::string& oauth1_access_token, 143 const std::string& oauth1_access_token_secret, 144 const std::string& wrap_token_duration, 145 const std::string& oauth2_scope) { 146 OAuthRequestSigner::Parameters parameters; 147 parameters["wrap_token_duration"] = wrap_token_duration; 148 parameters["wrap_scope"] = oauth2_scope; 149 std::string signed_request; 150 bool is_signed = OAuthRequestSigner::SignURL( 151 GaiaUrls::GetInstance()->oauth_wrap_bridge_url(), 152 parameters, 153 OAuthRequestSigner::HMAC_SHA1_SIGNATURE, 154 OAuthRequestSigner::POST_METHOD, 155 "anonymous", // oauth_consumer_key 156 "anonymous", // consumer secret 157 oauth1_access_token, // oauth_token 158 oauth1_access_token_secret, // token secret 159 &signed_request); 160 DCHECK(is_signed); 161 return signed_request; 162 } 163 164 // Helper method that extracts tokens from a successful reply. 165 // static 166 void GaiaOAuthFetcher::ParseOAuthLoginResponse( 167 const std::string& data, 168 std::string* sid, 169 std::string* lsid, 170 std::string* auth) { 171 using std::vector; 172 using std::pair; 173 using std::string; 174 vector<pair<string, string> > tokens; 175 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); 176 for (vector<pair<string, string> >::iterator i = tokens.begin(); 177 i != tokens.end(); ++i) { 178 if (i->first == "SID") { 179 *sid = i->second; 180 } else if (i->first == "LSID") { 181 *lsid = i->second; 182 } else if (i->first == "Auth") { 183 *auth = i->second; 184 } 185 } 186 } 187 188 // Helper method that extracts tokens from a successful reply. 189 // static 190 void GaiaOAuthFetcher::ParseOAuthGetAccessTokenResponse( 191 const std::string& data, 192 std::string* token, 193 std::string* secret) { 194 using std::vector; 195 using std::pair; 196 using std::string; 197 198 vector<pair<string, string> > tokens; 199 base::SplitStringIntoKeyValuePairs(data, '=', '&', &tokens); 200 for (vector<pair<string, string> >::iterator i = tokens.begin(); 201 i != tokens.end(); ++i) { 202 if (i->first == "oauth_token") { 203 std::string decoded; 204 if (OAuthRequestSigner::Decode(i->second, &decoded)) 205 token->assign(decoded); 206 } else if (i->first == "oauth_token_secret") { 207 std::string decoded; 208 if (OAuthRequestSigner::Decode(i->second, &decoded)) 209 secret->assign(decoded); 210 } 211 } 212 } 213 214 // Helper method that extracts tokens from a successful reply. 215 // static 216 void GaiaOAuthFetcher::ParseOAuthWrapBridgeResponse(const std::string& data, 217 std::string* token, 218 std::string* expires_in) { 219 using std::vector; 220 using std::pair; 221 using std::string; 222 223 vector<pair<string, string> > tokens; 224 base::SplitStringIntoKeyValuePairs(data, '=', '&', &tokens); 225 for (vector<pair<string, string> >::iterator i = tokens.begin(); 226 i != tokens.end(); ++i) { 227 if (i->first == "wrap_access_token") { 228 std::string decoded; 229 if (OAuthRequestSigner::Decode(i->second, &decoded)) 230 token->assign(decoded); 231 } else if (i->first == "wrap_access_token_expires_in") { 232 std::string decoded; 233 if (OAuthRequestSigner::Decode(i->second, &decoded)) 234 expires_in->assign(decoded); 235 } 236 } 237 } 238 239 // Helper method that extracts tokens from a successful reply. 240 // static 241 void GaiaOAuthFetcher::ParseUserInfoResponse(const std::string& data, 242 std::string* email_result) { 243 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 244 if (value->GetType() == base::Value::TYPE_DICTIONARY) { 245 Value* email_value; 246 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); 247 if (dict->Get("email", &email_value)) { 248 if (email_value->GetType() == base::Value::TYPE_STRING) { 249 email_value->GetAsString(email_result); 250 } 251 } 252 } 253 } 254 255 void GaiaOAuthFetcher::StartOAuthLogin( 256 const char* source, 257 const char* service, 258 const std::string& oauth1_access_token, 259 const std::string& oauth1_access_token_secret) { 260 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 261 262 request_type_ = OAUTH1_LOGIN; 263 // Must outlive fetcher_. 264 request_body_ = MakeOAuthLoginBody(source, service, oauth1_access_token, 265 oauth1_access_token_secret); 266 request_headers_ = ""; 267 GURL url(GaiaUrls::GetInstance()->oauth1_login_url()); 268 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 269 request_headers_, false, this)); 270 fetch_pending_ = true; 271 fetcher_->Start(); 272 } 273 274 void GaiaOAuthFetcher::StartGetOAuthTokenRequest() { 275 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 276 277 request_type_ = OAUTH1_REQUEST_TOKEN; 278 // Must outlive fetcher_. 279 request_body_ = ""; 280 request_headers_ = ""; 281 fetcher_.reset(CreateGaiaFetcher(getter_, 282 MakeGetOAuthTokenUrl(GaiaUrls::GetInstance()->oauth1_login_scope(), 283 l10n_util::GetStringUTF8(IDS_PRODUCT_NAME)), 284 std::string(), 285 std::string(), 286 true, // send_cookies 287 this)); 288 fetch_pending_ = true; 289 fetcher_->Start(); 290 } 291 292 void GaiaOAuthFetcher::StartOAuthGetAccessToken( 293 const std::string& oauth1_request_token) { 294 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 295 296 request_type_ = OAUTH1_ALL_ACCESS_TOKEN; 297 // Must outlive fetcher_. 298 request_body_ = MakeOAuthGetAccessTokenBody(oauth1_request_token); 299 request_headers_ = ""; 300 GURL url(GaiaUrls::GetInstance()->oauth_get_access_token_url()); 301 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 302 request_headers_, false, this)); 303 fetch_pending_ = true; 304 fetcher_->Start(); 305 } 306 307 void GaiaOAuthFetcher::StartOAuthWrapBridge( 308 const std::string& oauth1_access_token, 309 const std::string& oauth1_access_token_secret, 310 const std::string& wrap_token_duration, 311 const std::string& service_scope) { 312 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 313 314 request_type_ = OAUTH2_SERVICE_ACCESS_TOKEN; 315 VLOG(1) << "Starting OAuthWrapBridge for: " << service_scope; 316 std::string combined_scope = service_scope + " " + 317 GaiaUrls::GetInstance()->oauth_wrap_bridge_user_info_scope(); 318 service_scope_ = service_scope; 319 320 // Must outlive fetcher_. 321 request_body_ = MakeOAuthWrapBridgeBody( 322 oauth1_access_token, 323 oauth1_access_token_secret, 324 wrap_token_duration, 325 combined_scope); 326 327 request_headers_ = ""; 328 GURL url(GaiaUrls::GetInstance()->oauth_wrap_bridge_url()); 329 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 330 request_headers_, false, this)); 331 fetch_pending_ = true; 332 fetcher_->Start(); 333 } 334 335 void GaiaOAuthFetcher::StartUserInfo(const std::string& oauth2_access_token) { 336 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 337 338 request_type_ = USER_INFO; 339 // Must outlive fetcher_. 340 request_body_ = ""; 341 request_headers_ = "Authorization: OAuth " + oauth2_access_token; 342 GURL url(GaiaUrls::GetInstance()->oauth_user_info_url()); 343 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 344 request_headers_, false, this)); 345 fetch_pending_ = true; 346 fetcher_->Start(); 347 } 348 349 void GaiaOAuthFetcher::StartOAuthRevokeAccessToken(const std::string& token, 350 const std::string& secret) { 351 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 352 353 request_type_ = OAUTH2_REVOKE_TOKEN; 354 // Must outlive fetcher_. 355 request_body_ = ""; 356 357 OAuthRequestSigner::Parameters empty_parameters; 358 std::string auth_header; 359 bool is_signed = OAuthRequestSigner::SignAuthHeader( 360 GaiaUrls::GetInstance()->oauth_revoke_token_url(), 361 empty_parameters, 362 OAuthRequestSigner::HMAC_SHA1_SIGNATURE, 363 OAuthRequestSigner::GET_METHOD, 364 "anonymous", 365 "anonymous", 366 token, 367 secret, 368 &auth_header); 369 DCHECK(is_signed); 370 request_headers_ = "Authorization: " + auth_header; 371 GURL url(GaiaUrls::GetInstance()->oauth_revoke_token_url()); 372 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 373 request_headers_, false, this)); 374 fetch_pending_ = true; 375 fetcher_->Start(); 376 } 377 378 void GaiaOAuthFetcher::StartOAuthRevokeWrapToken(const std::string& token) { 379 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 380 381 request_type_ = OAUTH2_REVOKE_TOKEN; 382 // Must outlive fetcher_. 383 request_body_ = ""; 384 385 request_headers_ = "Authorization: Bearer " + token; 386 GURL url(GaiaUrls::GetInstance()->oauth_revoke_token_url()); 387 fetcher_.reset(CreateGaiaFetcher(getter_, url, request_body_, 388 request_headers_, false, this)); 389 fetch_pending_ = true; 390 fetcher_->Start(); 391 } 392 393 // static 394 GoogleServiceAuthError GaiaOAuthFetcher::GenerateAuthError( 395 const std::string& data, 396 const net::URLRequestStatus& status, 397 int response_code) { 398 if (!status.is_success()) { 399 if (status.status() == net::URLRequestStatus::CANCELED) { 400 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); 401 } else { 402 LOG(WARNING) << "Could not reach Google Accounts servers: errno " 403 << status.error(); 404 return GoogleServiceAuthError::FromConnectionError(status.error()); 405 } 406 } else { 407 LOG(WARNING) << "Unrecognized response from Google Accounts servers " 408 << "code " << response_code << " data " << data; 409 return GoogleServiceAuthError( 410 GoogleServiceAuthError::SERVICE_UNAVAILABLE); 411 } 412 413 NOTREACHED(); 414 return GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE); 415 } 416 417 void GaiaOAuthFetcher::OnGetOAuthTokenUrlFetched( 418 const net::ResponseCookies& cookies, 419 const net::URLRequestStatus& status, 420 int response_code) { 421 if (status.is_success() && response_code == net::HTTP_OK) { 422 for (net::ResponseCookies::const_iterator iter = cookies.begin(); 423 iter != cookies.end(); ++iter) { 424 net::ParsedCookie cookie(*iter); 425 if (cookie.Name() == kOAuthTokenCookie) { 426 std::string token = cookie.Value(); 427 consumer_->OnGetOAuthTokenSuccess(token); 428 if (ShouldAutoFetch(OAUTH1_ALL_ACCESS_TOKEN)) 429 StartOAuthGetAccessToken(token); 430 return; 431 } 432 } 433 } 434 consumer_->OnGetOAuthTokenFailure( 435 GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE)); 436 } 437 438 void GaiaOAuthFetcher::OnOAuthLoginFetched( 439 const std::string& data, 440 const net::URLRequestStatus& status, 441 int response_code) { 442 if (status.is_success() && response_code == net::HTTP_OK) { 443 std::string sid; 444 std::string lsid; 445 std::string auth; 446 ParseOAuthLoginResponse(data, &sid, &lsid, &auth); 447 if (!sid.empty() && !lsid.empty() && !auth.empty()) { 448 consumer_->OnOAuthLoginSuccess(sid, lsid, auth); 449 return; 450 } 451 } 452 // OAuthLogin returns error messages that are identical to ClientLogin, 453 // so we use GaiaAuthFetcher::GenerateAuthError to parse the response 454 // instead. 455 consumer_->OnOAuthLoginFailure( 456 GaiaAuthFetcher::GenerateOAuthLoginError(data, status)); 457 } 458 459 void GaiaOAuthFetcher::OnOAuthGetAccessTokenFetched( 460 const std::string& data, 461 const net::URLRequestStatus& status, 462 int response_code) { 463 if (status.is_success() && response_code == net::HTTP_OK) { 464 VLOG(1) << "OAuth1 access token fetched."; 465 std::string secret; 466 std::string token; 467 ParseOAuthGetAccessTokenResponse(data, &token, &secret); 468 if (!token.empty() && !secret.empty()) { 469 consumer_->OnOAuthGetAccessTokenSuccess(token, secret); 470 if (ShouldAutoFetch(OAUTH2_SERVICE_ACCESS_TOKEN)) 471 StartOAuthWrapBridge( 472 token, secret, GaiaConstants::kGaiaOAuthDuration, service_scope_); 473 return; 474 } 475 } 476 consumer_->OnOAuthGetAccessTokenFailure(GenerateAuthError(data, status, 477 response_code)); 478 } 479 480 void GaiaOAuthFetcher::OnOAuthWrapBridgeFetched( 481 const std::string& data, 482 const net::URLRequestStatus& status, 483 int response_code) { 484 if (status.is_success() && response_code == net::HTTP_OK) { 485 VLOG(1) << "OAuth2 access token fetched."; 486 std::string token; 487 std::string expires_in; 488 ParseOAuthWrapBridgeResponse(data, &token, &expires_in); 489 if (!token.empty() && !expires_in.empty()) { 490 consumer_->OnOAuthWrapBridgeSuccess(service_scope_, token, expires_in); 491 if (ShouldAutoFetch(USER_INFO)) 492 StartUserInfo(token); 493 return; 494 } 495 } 496 consumer_->OnOAuthWrapBridgeFailure(service_scope_, 497 GenerateAuthError(data, status, 498 response_code)); 499 } 500 501 void GaiaOAuthFetcher::OnOAuthRevokeTokenFetched( 502 const std::string& data, 503 const net::URLRequestStatus& status, 504 int response_code) { 505 if (status.is_success() && response_code == net::HTTP_OK) { 506 consumer_->OnOAuthRevokeTokenSuccess(); 507 } else { 508 LOG(ERROR) << "Token revocation failure " << response_code << ": " << data; 509 consumer_->OnOAuthRevokeTokenFailure(GenerateAuthError(data, status, 510 response_code)); 511 } 512 } 513 514 void GaiaOAuthFetcher::OnUserInfoFetched( 515 const std::string& data, 516 const net::URLRequestStatus& status, 517 int response_code) { 518 if (status.is_success() && response_code == net::HTTP_OK) { 519 std::string email; 520 ParseUserInfoResponse(data, &email); 521 if (!email.empty()) { 522 VLOG(1) << "GAIA user info fetched for " << email << "."; 523 consumer_->OnUserInfoSuccess(email); 524 return; 525 } 526 } 527 consumer_->OnUserInfoFailure(GenerateAuthError(data, status, 528 response_code)); 529 } 530 531 void GaiaOAuthFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 532 // Keep |fetcher_| around to avoid invalidating its |status| (accessed below). 533 scoped_ptr<net::URLFetcher> current_fetcher(fetcher_.release()); 534 fetch_pending_ = false; 535 std::string data; 536 source->GetResponseAsString(&data); 537 net::URLRequestStatus status = source->GetStatus(); 538 int response_code = source->GetResponseCode(); 539 540 switch (request_type_) { 541 case OAUTH1_LOGIN: 542 OnOAuthLoginFetched(data, status, response_code); 543 break; 544 case OAUTH1_REQUEST_TOKEN: 545 OnGetOAuthTokenUrlFetched(source->GetCookies(), status, response_code); 546 break; 547 case OAUTH1_ALL_ACCESS_TOKEN: 548 OnOAuthGetAccessTokenFetched(data, status, response_code); 549 break; 550 case OAUTH2_SERVICE_ACCESS_TOKEN: 551 OnOAuthWrapBridgeFetched(data, status, response_code); 552 break; 553 case USER_INFO: 554 OnUserInfoFetched(data, status, response_code); 555 break; 556 case OAUTH2_REVOKE_TOKEN: 557 OnOAuthRevokeTokenFetched(data, status, response_code); 558 break; 559 } 560 } 561 562 bool GaiaOAuthFetcher::ShouldAutoFetch(RequestType fetch_step) { 563 return fetch_step <= auto_fetch_limit_; 564 } 565