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 "google_apis/gaia/gaia_auth_fetcher.h" 6 7 #include <string> 8 #include <utility> 9 #include <vector> 10 11 #include "base/json/json_reader.h" 12 #include "base/json/json_writer.h" 13 #include "base/strings/string_split.h" 14 #include "base/strings/string_util.h" 15 #include "base/strings/stringprintf.h" 16 #include "base/values.h" 17 #include "google_apis/gaia/gaia_auth_consumer.h" 18 #include "google_apis/gaia/gaia_constants.h" 19 #include "google_apis/gaia/gaia_urls.h" 20 #include "google_apis/gaia/google_service_auth_error.h" 21 #include "net/base/escape.h" 22 #include "net/base/load_flags.h" 23 #include "net/http/http_response_headers.h" 24 #include "net/http/http_status_code.h" 25 #include "net/url_request/url_fetcher.h" 26 #include "net/url_request/url_request_context_getter.h" 27 #include "net/url_request/url_request_status.h" 28 29 namespace { 30 const int kLoadFlagsIgnoreCookies = net::LOAD_DO_NOT_SEND_COOKIES | 31 net::LOAD_DO_NOT_SAVE_COOKIES; 32 33 static bool CookiePartsContains(const std::vector<std::string>& parts, 34 const char* part) { 35 for (std::vector<std::string>::const_iterator it = parts.begin(); 36 it != parts.end(); ++it) { 37 if (LowerCaseEqualsASCII(*it, part)) 38 return true; 39 } 40 return false; 41 } 42 43 bool ExtractOAuth2TokenPairResponse(base::DictionaryValue* dict, 44 std::string* refresh_token, 45 std::string* access_token, 46 int* expires_in_secs) { 47 DCHECK(refresh_token); 48 DCHECK(access_token); 49 DCHECK(expires_in_secs); 50 51 if (!dict->GetStringWithoutPathExpansion("refresh_token", refresh_token) || 52 !dict->GetStringWithoutPathExpansion("access_token", access_token) || 53 !dict->GetIntegerWithoutPathExpansion("expires_in", expires_in_secs)) { 54 return false; 55 } 56 57 return true; 58 } 59 60 } // namespace 61 62 // TODO(chron): Add sourceless version of this formatter. 63 // static 64 const char GaiaAuthFetcher::kClientLoginFormat[] = 65 "Email=%s&" 66 "Passwd=%s&" 67 "PersistentCookie=%s&" 68 "accountType=%s&" 69 "source=%s&" 70 "service=%s"; 71 // static 72 const char GaiaAuthFetcher::kClientLoginCaptchaFormat[] = 73 "Email=%s&" 74 "Passwd=%s&" 75 "PersistentCookie=%s&" 76 "accountType=%s&" 77 "source=%s&" 78 "service=%s&" 79 "logintoken=%s&" 80 "logincaptcha=%s"; 81 // static 82 const char GaiaAuthFetcher::kIssueAuthTokenFormat[] = 83 "SID=%s&" 84 "LSID=%s&" 85 "service=%s&" 86 "Session=%s"; 87 // static 88 const char GaiaAuthFetcher::kClientLoginToOAuth2BodyFormat[] = 89 "scope=%s&client_id=%s"; 90 // static 91 const char GaiaAuthFetcher::kClientLoginToOAuth2WithDeviceTypeBodyFormat[] = 92 "scope=%s&client_id=%s&device_type=chrome"; 93 // static 94 const char GaiaAuthFetcher::kOAuth2CodeToTokenPairBodyFormat[] = 95 "scope=%s&" 96 "grant_type=authorization_code&" 97 "client_id=%s&" 98 "client_secret=%s&" 99 "code=%s"; 100 // static 101 const char GaiaAuthFetcher::kOAuth2RevokeTokenBodyFormat[] = 102 "token=%s"; 103 // static 104 const char GaiaAuthFetcher::kGetUserInfoFormat[] = 105 "LSID=%s"; 106 // static 107 const char GaiaAuthFetcher::kMergeSessionFormat[] = 108 "uberauth=%s&" 109 "continue=%s&" 110 "source=%s"; 111 // static 112 const char GaiaAuthFetcher::kUberAuthTokenURLFormat[] = 113 "?source=%s&" 114 "issueuberauth=1"; 115 116 const char GaiaAuthFetcher::kOAuthLoginFormat[] = "service=%s&source=%s"; 117 118 // static 119 const char GaiaAuthFetcher::kAccountDeletedError[] = "AccountDeleted"; 120 // static 121 const char GaiaAuthFetcher::kAccountDisabledError[] = "AccountDisabled"; 122 // static 123 const char GaiaAuthFetcher::kBadAuthenticationError[] = "BadAuthentication"; 124 // static 125 const char GaiaAuthFetcher::kCaptchaError[] = "CaptchaRequired"; 126 // static 127 const char GaiaAuthFetcher::kServiceUnavailableError[] = 128 "ServiceUnavailable"; 129 // static 130 const char GaiaAuthFetcher::kErrorParam[] = "Error"; 131 // static 132 const char GaiaAuthFetcher::kErrorUrlParam[] = "Url"; 133 // static 134 const char GaiaAuthFetcher::kCaptchaUrlParam[] = "CaptchaUrl"; 135 // static 136 const char GaiaAuthFetcher::kCaptchaTokenParam[] = "CaptchaToken"; 137 138 // static 139 const char GaiaAuthFetcher::kCookiePersistence[] = "true"; 140 // static 141 // TODO(johnnyg): When hosted accounts are supported by sync, 142 // we can always use "HOSTED_OR_GOOGLE" 143 const char GaiaAuthFetcher::kAccountTypeHostedOrGoogle[] = 144 "HOSTED_OR_GOOGLE"; 145 const char GaiaAuthFetcher::kAccountTypeGoogle[] = 146 "GOOGLE"; 147 148 // static 149 const char GaiaAuthFetcher::kSecondFactor[] = "Info=InvalidSecondFactor"; 150 151 // static 152 const char GaiaAuthFetcher::kAuthHeaderFormat[] = 153 "Authorization: GoogleLogin auth=%s"; 154 // static 155 const char GaiaAuthFetcher::kOAuthHeaderFormat[] = "Authorization: OAuth %s"; 156 // static 157 const char GaiaAuthFetcher::kOAuth2BearerHeaderFormat[] = 158 "Authorization: Bearer %s"; 159 // static 160 const char GaiaAuthFetcher::kDeviceIdHeaderFormat[] = "X-Device-ID: %s"; 161 // static 162 const char GaiaAuthFetcher::kClientLoginToOAuth2CookiePartSecure[] = "secure"; 163 // static 164 const char GaiaAuthFetcher::kClientLoginToOAuth2CookiePartHttpOnly[] = 165 "httponly"; 166 // static 167 const char GaiaAuthFetcher::kClientLoginToOAuth2CookiePartCodePrefix[] = 168 "oauth_code="; 169 // static 170 const int GaiaAuthFetcher::kClientLoginToOAuth2CookiePartCodePrefixLength = 171 arraysize(GaiaAuthFetcher::kClientLoginToOAuth2CookiePartCodePrefix) - 1; 172 173 GaiaAuthFetcher::GaiaAuthFetcher(GaiaAuthConsumer* consumer, 174 const std::string& source, 175 net::URLRequestContextGetter* getter) 176 : consumer_(consumer), 177 getter_(getter), 178 source_(source), 179 client_login_gurl_(GaiaUrls::GetInstance()->client_login_url()), 180 issue_auth_token_gurl_(GaiaUrls::GetInstance()->issue_auth_token_url()), 181 oauth2_token_gurl_(GaiaUrls::GetInstance()->oauth2_token_url()), 182 oauth2_revoke_gurl_(GaiaUrls::GetInstance()->oauth2_revoke_url()), 183 get_user_info_gurl_(GaiaUrls::GetInstance()->get_user_info_url()), 184 merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()), 185 uberauth_token_gurl_(GaiaUrls::GetInstance()->oauth1_login_url().Resolve( 186 base::StringPrintf(kUberAuthTokenURLFormat, source.c_str()))), 187 oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()), 188 list_accounts_gurl_(GaiaUrls::GetInstance()->list_accounts_url()), 189 client_login_to_oauth2_gurl_( 190 GaiaUrls::GetInstance()->client_login_to_oauth2_url()), 191 fetch_pending_(false) {} 192 193 GaiaAuthFetcher::~GaiaAuthFetcher() {} 194 195 bool GaiaAuthFetcher::HasPendingFetch() { 196 return fetch_pending_; 197 } 198 199 void GaiaAuthFetcher::CancelRequest() { 200 fetcher_.reset(); 201 fetch_pending_ = false; 202 } 203 204 // static 205 net::URLFetcher* GaiaAuthFetcher::CreateGaiaFetcher( 206 net::URLRequestContextGetter* getter, 207 const std::string& body, 208 const std::string& headers, 209 const GURL& gaia_gurl, 210 int load_flags, 211 net::URLFetcherDelegate* delegate) { 212 net::URLFetcher* to_return = net::URLFetcher::Create( 213 0, gaia_gurl, 214 body == "" ? net::URLFetcher::GET : net::URLFetcher::POST, 215 delegate); 216 to_return->SetRequestContext(getter); 217 to_return->SetUploadData("application/x-www-form-urlencoded", body); 218 219 DVLOG(2) << "Gaia fetcher URL: " << gaia_gurl.spec(); 220 DVLOG(2) << "Gaia fetcher headers: " << headers; 221 DVLOG(2) << "Gaia fetcher body: " << body; 222 223 // The Gaia token exchange requests do not require any cookie-based 224 // identification as part of requests. We suppress sending any cookies to 225 // maintain a separation between the user's browsing and Chrome's internal 226 // services. Where such mixing is desired (MergeSession or OAuthLogin), it 227 // will be done explicitly. 228 to_return->SetLoadFlags(load_flags); 229 230 // Fetchers are sometimes cancelled because a network change was detected, 231 // especially at startup and after sign-in on ChromeOS. Retrying once should 232 // be enough in those cases; let the fetcher retry up to 3 times just in case. 233 // http://crbug.com/163710 234 to_return->SetAutomaticallyRetryOnNetworkChanges(3); 235 236 if (!headers.empty()) 237 to_return->SetExtraRequestHeaders(headers); 238 239 return to_return; 240 } 241 242 // static 243 std::string GaiaAuthFetcher::MakeClientLoginBody( 244 const std::string& username, 245 const std::string& password, 246 const std::string& source, 247 const char* service, 248 const std::string& login_token, 249 const std::string& login_captcha, 250 HostedAccountsSetting allow_hosted_accounts) { 251 std::string encoded_username = net::EscapeUrlEncodedData(username, true); 252 std::string encoded_password = net::EscapeUrlEncodedData(password, true); 253 std::string encoded_login_token = net::EscapeUrlEncodedData(login_token, 254 true); 255 std::string encoded_login_captcha = net::EscapeUrlEncodedData(login_captcha, 256 true); 257 258 const char* account_type = allow_hosted_accounts == HostedAccountsAllowed ? 259 kAccountTypeHostedOrGoogle : 260 kAccountTypeGoogle; 261 262 if (login_token.empty() || login_captcha.empty()) { 263 return base::StringPrintf(kClientLoginFormat, 264 encoded_username.c_str(), 265 encoded_password.c_str(), 266 kCookiePersistence, 267 account_type, 268 source.c_str(), 269 service); 270 } 271 272 return base::StringPrintf(kClientLoginCaptchaFormat, 273 encoded_username.c_str(), 274 encoded_password.c_str(), 275 kCookiePersistence, 276 account_type, 277 source.c_str(), 278 service, 279 encoded_login_token.c_str(), 280 encoded_login_captcha.c_str()); 281 } 282 283 // static 284 std::string GaiaAuthFetcher::MakeIssueAuthTokenBody( 285 const std::string& sid, 286 const std::string& lsid, 287 const char* const service) { 288 std::string encoded_sid = net::EscapeUrlEncodedData(sid, true); 289 std::string encoded_lsid = net::EscapeUrlEncodedData(lsid, true); 290 291 // All tokens should be session tokens except the gaia auth token. 292 bool session = true; 293 if (!strcmp(service, GaiaConstants::kGaiaService)) 294 session = false; 295 296 return base::StringPrintf(kIssueAuthTokenFormat, 297 encoded_sid.c_str(), 298 encoded_lsid.c_str(), 299 service, 300 session ? "true" : "false"); 301 } 302 303 // static 304 std::string GaiaAuthFetcher::MakeGetAuthCodeBody(bool include_device_type) { 305 std::string encoded_scope = net::EscapeUrlEncodedData( 306 GaiaConstants::kOAuth1LoginScope, true); 307 std::string encoded_client_id = net::EscapeUrlEncodedData( 308 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true); 309 if (include_device_type) { 310 return base::StringPrintf(kClientLoginToOAuth2WithDeviceTypeBodyFormat, 311 encoded_scope.c_str(), 312 encoded_client_id.c_str()); 313 } else { 314 return base::StringPrintf(kClientLoginToOAuth2BodyFormat, 315 encoded_scope.c_str(), 316 encoded_client_id.c_str()); 317 } 318 } 319 320 // static 321 std::string GaiaAuthFetcher::MakeGetTokenPairBody( 322 const std::string& auth_code) { 323 std::string encoded_scope = net::EscapeUrlEncodedData( 324 GaiaConstants::kOAuth1LoginScope, true); 325 std::string encoded_client_id = net::EscapeUrlEncodedData( 326 GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true); 327 std::string encoded_client_secret = net::EscapeUrlEncodedData( 328 GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), true); 329 std::string encoded_auth_code = net::EscapeUrlEncodedData(auth_code, true); 330 return base::StringPrintf(kOAuth2CodeToTokenPairBodyFormat, 331 encoded_scope.c_str(), 332 encoded_client_id.c_str(), 333 encoded_client_secret.c_str(), 334 encoded_auth_code.c_str()); 335 } 336 337 // static 338 std::string GaiaAuthFetcher::MakeRevokeTokenBody( 339 const std::string& auth_token) { 340 return base::StringPrintf(kOAuth2RevokeTokenBodyFormat, auth_token.c_str()); 341 } 342 343 // static 344 std::string GaiaAuthFetcher::MakeGetUserInfoBody(const std::string& lsid) { 345 std::string encoded_lsid = net::EscapeUrlEncodedData(lsid, true); 346 return base::StringPrintf(kGetUserInfoFormat, encoded_lsid.c_str()); 347 } 348 349 // static 350 std::string GaiaAuthFetcher::MakeMergeSessionBody( 351 const std::string& auth_token, 352 const std::string& continue_url, 353 const std::string& source) { 354 std::string encoded_auth_token = net::EscapeUrlEncodedData(auth_token, true); 355 std::string encoded_continue_url = net::EscapeUrlEncodedData(continue_url, 356 true); 357 std::string encoded_source = net::EscapeUrlEncodedData(source, true); 358 return base::StringPrintf(kMergeSessionFormat, 359 encoded_auth_token.c_str(), 360 encoded_continue_url.c_str(), 361 encoded_source.c_str()); 362 } 363 364 // static 365 std::string GaiaAuthFetcher::MakeGetAuthCodeHeader( 366 const std::string& auth_token) { 367 return base::StringPrintf(kAuthHeaderFormat, auth_token.c_str()); 368 } 369 370 // Helper method that extracts tokens from a successful reply. 371 // static 372 void GaiaAuthFetcher::ParseClientLoginResponse(const std::string& data, 373 std::string* sid, 374 std::string* lsid, 375 std::string* token) { 376 using std::vector; 377 using std::pair; 378 using std::string; 379 sid->clear(); 380 lsid->clear(); 381 token->clear(); 382 vector<pair<string, string> > tokens; 383 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); 384 for (vector<pair<string, string> >::iterator i = tokens.begin(); 385 i != tokens.end(); ++i) { 386 if (i->first == "SID") { 387 sid->assign(i->second); 388 } else if (i->first == "LSID") { 389 lsid->assign(i->second); 390 } else if (i->first == "Auth") { 391 token->assign(i->second); 392 } 393 } 394 // If this was a request for uberauth token, then that's all we've got in 395 // data. 396 if (sid->empty() && lsid->empty() && token->empty()) 397 token->assign(data); 398 } 399 400 // static 401 std::string GaiaAuthFetcher::MakeOAuthLoginBody(const std::string& service, 402 const std::string& source) { 403 std::string encoded_service = net::EscapeUrlEncodedData(service, true); 404 std::string encoded_source = net::EscapeUrlEncodedData(source, true); 405 return base::StringPrintf(kOAuthLoginFormat, 406 encoded_service.c_str(), 407 encoded_source.c_str()); 408 } 409 410 // static 411 void GaiaAuthFetcher::ParseClientLoginFailure(const std::string& data, 412 std::string* error, 413 std::string* error_url, 414 std::string* captcha_url, 415 std::string* captcha_token) { 416 using std::vector; 417 using std::pair; 418 using std::string; 419 420 vector<pair<string, string> > tokens; 421 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); 422 for (vector<pair<string, string> >::iterator i = tokens.begin(); 423 i != tokens.end(); ++i) { 424 if (i->first == kErrorParam) { 425 error->assign(i->second); 426 } else if (i->first == kErrorUrlParam) { 427 error_url->assign(i->second); 428 } else if (i->first == kCaptchaUrlParam) { 429 captcha_url->assign(i->second); 430 } else if (i->first == kCaptchaTokenParam) { 431 captcha_token->assign(i->second); 432 } 433 } 434 } 435 436 // static 437 bool GaiaAuthFetcher::ParseClientLoginToOAuth2Response( 438 const net::ResponseCookies& cookies, 439 std::string* auth_code) { 440 DCHECK(auth_code); 441 net::ResponseCookies::const_iterator iter; 442 for (iter = cookies.begin(); iter != cookies.end(); ++iter) { 443 if (ParseClientLoginToOAuth2Cookie(*iter, auth_code)) 444 return true; 445 } 446 return false; 447 } 448 449 // static 450 bool GaiaAuthFetcher::ParseClientLoginToOAuth2Cookie(const std::string& cookie, 451 std::string* auth_code) { 452 std::vector<std::string> parts; 453 base::SplitString(cookie, ';', &parts); 454 // Per documentation, the cookie should have Secure and HttpOnly. 455 if (!CookiePartsContains(parts, kClientLoginToOAuth2CookiePartSecure) || 456 !CookiePartsContains(parts, kClientLoginToOAuth2CookiePartHttpOnly)) { 457 return false; 458 } 459 460 std::vector<std::string>::const_iterator iter; 461 for (iter = parts.begin(); iter != parts.end(); ++iter) { 462 const std::string& part = *iter; 463 if (StartsWithASCII( 464 part, kClientLoginToOAuth2CookiePartCodePrefix, false)) { 465 auth_code->assign(part.substr( 466 kClientLoginToOAuth2CookiePartCodePrefixLength)); 467 return true; 468 } 469 } 470 return false; 471 } 472 473 void GaiaAuthFetcher::StartClientLogin( 474 const std::string& username, 475 const std::string& password, 476 const char* const service, 477 const std::string& login_token, 478 const std::string& login_captcha, 479 HostedAccountsSetting allow_hosted_accounts) { 480 481 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 482 483 // This class is thread agnostic, so be sure to call this only on the 484 // same thread each time. 485 DVLOG(1) << "Starting new ClientLogin fetch for:" << username; 486 487 // Must outlive fetcher_. 488 request_body_ = MakeClientLoginBody(username, 489 password, 490 source_, 491 service, 492 login_token, 493 login_captcha, 494 allow_hosted_accounts); 495 fetcher_.reset(CreateGaiaFetcher(getter_, 496 request_body_, 497 std::string(), 498 client_login_gurl_, 499 kLoadFlagsIgnoreCookies, 500 this)); 501 fetch_pending_ = true; 502 fetcher_->Start(); 503 } 504 505 void GaiaAuthFetcher::StartIssueAuthToken(const std::string& sid, 506 const std::string& lsid, 507 const char* const service) { 508 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 509 510 DVLOG(1) << "Starting IssueAuthToken for: " << service; 511 requested_service_ = service; 512 request_body_ = MakeIssueAuthTokenBody(sid, lsid, service); 513 fetcher_.reset(CreateGaiaFetcher(getter_, 514 request_body_, 515 std::string(), 516 issue_auth_token_gurl_, 517 kLoadFlagsIgnoreCookies, 518 this)); 519 fetch_pending_ = true; 520 fetcher_->Start(); 521 } 522 523 void GaiaAuthFetcher::StartLsoForOAuthLoginTokenExchange( 524 const std::string& auth_token) { 525 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 526 527 DVLOG(1) << "Starting OAuth login token exchange with auth_token"; 528 request_body_ = MakeGetAuthCodeBody(false); 529 client_login_to_oauth2_gurl_ = 530 GaiaUrls::GetInstance()->client_login_to_oauth2_url(); 531 532 fetcher_.reset(CreateGaiaFetcher(getter_, 533 request_body_, 534 MakeGetAuthCodeHeader(auth_token), 535 client_login_to_oauth2_gurl_, 536 kLoadFlagsIgnoreCookies, 537 this)); 538 fetch_pending_ = true; 539 fetcher_->Start(); 540 } 541 542 void GaiaAuthFetcher::StartRevokeOAuth2Token(const std::string& auth_token) { 543 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 544 545 DVLOG(1) << "Starting OAuth2 token revocation"; 546 request_body_ = MakeRevokeTokenBody(auth_token); 547 fetcher_.reset(CreateGaiaFetcher(getter_, 548 request_body_, 549 std::string(), 550 oauth2_revoke_gurl_, 551 kLoadFlagsIgnoreCookies, 552 this)); 553 fetch_pending_ = true; 554 fetcher_->Start(); 555 } 556 557 void GaiaAuthFetcher::StartCookieForOAuthLoginTokenExchange( 558 const std::string& session_index) { 559 StartCookieForOAuthLoginTokenExchangeWithDeviceId(session_index, 560 std::string()); 561 } 562 563 void GaiaAuthFetcher::StartCookieForOAuthLoginTokenExchangeWithDeviceId( 564 const std::string& session_index, 565 const std::string& device_id) { 566 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 567 568 DVLOG(1) << "Starting OAuth login token fetch with cookie jar"; 569 request_body_ = MakeGetAuthCodeBody(!device_id.empty()); 570 571 client_login_to_oauth2_gurl_ = 572 GaiaUrls::GetInstance()->client_login_to_oauth2_url(); 573 if (!session_index.empty()) { 574 client_login_to_oauth2_gurl_ = 575 client_login_to_oauth2_gurl_.Resolve("?authuser=" + session_index); 576 } 577 578 std::string device_id_header; 579 if (!device_id.empty()) { 580 device_id_header = 581 base::StringPrintf(kDeviceIdHeaderFormat, device_id.c_str()); 582 } 583 584 fetcher_.reset(CreateGaiaFetcher(getter_, 585 request_body_, 586 device_id_header, 587 client_login_to_oauth2_gurl_, 588 net::LOAD_NORMAL, 589 this)); 590 fetch_pending_ = true; 591 fetcher_->Start(); 592 } 593 594 void GaiaAuthFetcher::StartAuthCodeForOAuth2TokenExchange( 595 const std::string& auth_code) { 596 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 597 598 DVLOG(1) << "Starting OAuth token pair fetch"; 599 request_body_ = MakeGetTokenPairBody(auth_code); 600 fetcher_.reset(CreateGaiaFetcher(getter_, 601 request_body_, 602 std::string(), 603 oauth2_token_gurl_, 604 kLoadFlagsIgnoreCookies, 605 this)); 606 fetch_pending_ = true; 607 fetcher_->Start(); 608 } 609 610 void GaiaAuthFetcher::StartGetUserInfo(const std::string& lsid) { 611 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 612 613 DVLOG(1) << "Starting GetUserInfo for lsid=" << lsid; 614 request_body_ = MakeGetUserInfoBody(lsid); 615 fetcher_.reset(CreateGaiaFetcher(getter_, 616 request_body_, 617 std::string(), 618 get_user_info_gurl_, 619 kLoadFlagsIgnoreCookies, 620 this)); 621 fetch_pending_ = true; 622 fetcher_->Start(); 623 } 624 625 void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token) { 626 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 627 628 DVLOG(1) << "Starting MergeSession with uber_token=" << uber_token; 629 630 // The continue URL is a required parameter of the MergeSession API, but in 631 // this case we don't actually need or want to navigate to it. Setting it to 632 // an arbitrary Google URL. 633 // 634 // In order for the new session to be merged correctly, the server needs to 635 // know what sessions already exist in the browser. The fetcher needs to be 636 // created such that it sends the cookies with the request, which is 637 // different from all other requests the fetcher can make. 638 std::string continue_url("http://www.google.com"); 639 request_body_ = MakeMergeSessionBody(uber_token, continue_url, source_); 640 fetcher_.reset(CreateGaiaFetcher(getter_, 641 request_body_, 642 std::string(), 643 merge_session_gurl_, 644 net::LOAD_NORMAL, 645 this)); 646 fetch_pending_ = true; 647 fetcher_->Start(); 648 } 649 650 void GaiaAuthFetcher::StartTokenFetchForUberAuthExchange( 651 const std::string& access_token) { 652 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 653 654 DVLOG(1) << "Starting StartTokenFetchForUberAuthExchange with access_token=" 655 << access_token; 656 std::string authentication_header = 657 base::StringPrintf(kOAuthHeaderFormat, access_token.c_str()); 658 fetcher_.reset(CreateGaiaFetcher(getter_, 659 std::string(), 660 authentication_header, 661 uberauth_token_gurl_, 662 net::LOAD_NORMAL, 663 this)); 664 fetch_pending_ = true; 665 fetcher_->Start(); 666 } 667 668 void GaiaAuthFetcher::StartOAuthLogin(const std::string& access_token, 669 const std::string& service) { 670 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 671 672 request_body_ = MakeOAuthLoginBody(service, source_); 673 std::string authentication_header = 674 base::StringPrintf(kOAuth2BearerHeaderFormat, access_token.c_str()); 675 fetcher_.reset(CreateGaiaFetcher(getter_, 676 request_body_, 677 authentication_header, 678 oauth_login_gurl_, 679 net::LOAD_NORMAL, 680 this)); 681 fetch_pending_ = true; 682 fetcher_->Start(); 683 } 684 685 void GaiaAuthFetcher::StartListAccounts() { 686 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; 687 688 fetcher_.reset(CreateGaiaFetcher(getter_, 689 " ", // To force an HTTP POST. 690 "Origin: https://www.google.com", 691 list_accounts_gurl_, 692 net::LOAD_NORMAL, 693 this)); 694 fetch_pending_ = true; 695 fetcher_->Start(); 696 } 697 698 // static 699 GoogleServiceAuthError GaiaAuthFetcher::GenerateAuthError( 700 const std::string& data, 701 const net::URLRequestStatus& status) { 702 if (!status.is_success()) { 703 if (status.status() == net::URLRequestStatus::CANCELED) { 704 return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED); 705 } 706 DLOG(WARNING) << "Could not reach Google Accounts servers: errno " 707 << status.error(); 708 return GoogleServiceAuthError::FromConnectionError(status.error()); 709 } 710 711 if (IsSecondFactorSuccess(data)) 712 return GoogleServiceAuthError(GoogleServiceAuthError::TWO_FACTOR); 713 714 std::string error; 715 std::string url; 716 std::string captcha_url; 717 std::string captcha_token; 718 ParseClientLoginFailure(data, &error, &url, &captcha_url, &captcha_token); 719 DLOG(WARNING) << "ClientLogin failed with " << error; 720 721 if (error == kCaptchaError) { 722 return GoogleServiceAuthError::FromClientLoginCaptchaChallenge( 723 captcha_token, 724 GURL(GaiaUrls::GetInstance()->captcha_base_url().Resolve(captcha_url)), 725 GURL(url)); 726 } 727 if (error == kAccountDeletedError) 728 return GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DELETED); 729 if (error == kAccountDisabledError) 730 return GoogleServiceAuthError(GoogleServiceAuthError::ACCOUNT_DISABLED); 731 if (error == kBadAuthenticationError) { 732 return GoogleServiceAuthError( 733 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS); 734 } 735 if (error == kServiceUnavailableError) { 736 return GoogleServiceAuthError( 737 GoogleServiceAuthError::SERVICE_UNAVAILABLE); 738 } 739 740 DLOG(WARNING) << "Incomprehensible response from Google Accounts servers."; 741 return GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE); 742 } 743 744 void GaiaAuthFetcher::OnClientLoginFetched(const std::string& data, 745 const net::URLRequestStatus& status, 746 int response_code) { 747 if (status.is_success() && response_code == net::HTTP_OK) { 748 DVLOG(1) << "ClientLogin successful!"; 749 std::string sid; 750 std::string lsid; 751 std::string token; 752 ParseClientLoginResponse(data, &sid, &lsid, &token); 753 consumer_->OnClientLoginSuccess( 754 GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data)); 755 } else { 756 consumer_->OnClientLoginFailure(GenerateAuthError(data, status)); 757 } 758 } 759 760 void GaiaAuthFetcher::OnIssueAuthTokenFetched( 761 const std::string& data, 762 const net::URLRequestStatus& status, 763 int response_code) { 764 if (status.is_success() && response_code == net::HTTP_OK) { 765 // Only the bare token is returned in the body of this Gaia call 766 // without any padding. 767 consumer_->OnIssueAuthTokenSuccess(requested_service_, data); 768 } else { 769 consumer_->OnIssueAuthTokenFailure(requested_service_, 770 GenerateAuthError(data, status)); 771 } 772 } 773 774 void GaiaAuthFetcher::OnClientLoginToOAuth2Fetched( 775 const std::string& data, 776 const net::ResponseCookies& cookies, 777 const net::URLRequestStatus& status, 778 int response_code) { 779 if (status.is_success() && response_code == net::HTTP_OK) { 780 std::string auth_code; 781 ParseClientLoginToOAuth2Response(cookies, &auth_code); 782 StartAuthCodeForOAuth2TokenExchange(auth_code); 783 } else { 784 GoogleServiceAuthError auth_error(GenerateAuthError(data, status)); 785 consumer_->OnClientOAuthFailure(auth_error); 786 } 787 } 788 789 void GaiaAuthFetcher::OnOAuth2TokenPairFetched( 790 const std::string& data, 791 const net::URLRequestStatus& status, 792 int response_code) { 793 std::string refresh_token; 794 std::string access_token; 795 int expires_in_secs = 0; 796 797 bool success = false; 798 if (status.is_success() && response_code == net::HTTP_OK) { 799 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); 800 if (value.get() && value->GetType() == base::Value::TYPE_DICTIONARY) { 801 base::DictionaryValue* dict = 802 static_cast<base::DictionaryValue*>(value.get()); 803 success = ExtractOAuth2TokenPairResponse(dict, &refresh_token, 804 &access_token, &expires_in_secs); 805 } 806 } 807 808 if (success) { 809 consumer_->OnClientOAuthSuccess( 810 GaiaAuthConsumer::ClientOAuthResult(refresh_token, access_token, 811 expires_in_secs)); 812 } else { 813 consumer_->OnClientOAuthFailure(GenerateAuthError(data, status)); 814 } 815 } 816 817 void GaiaAuthFetcher::OnOAuth2RevokeTokenFetched( 818 const std::string& data, 819 const net::URLRequestStatus& status, 820 int response_code) { 821 consumer_->OnOAuth2RevokeTokenCompleted(); 822 } 823 824 void GaiaAuthFetcher::OnListAccountsFetched(const std::string& data, 825 const net::URLRequestStatus& status, 826 int response_code) { 827 if (status.is_success() && response_code == net::HTTP_OK) { 828 consumer_->OnListAccountsSuccess(data); 829 } else { 830 consumer_->OnListAccountsFailure(GenerateAuthError(data, status)); 831 } 832 } 833 834 void GaiaAuthFetcher::OnGetUserInfoFetched( 835 const std::string& data, 836 const net::URLRequestStatus& status, 837 int response_code) { 838 if (status.is_success() && response_code == net::HTTP_OK) { 839 std::vector<std::pair<std::string, std::string> > tokens; 840 UserInfoMap matches; 841 base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens); 842 std::vector<std::pair<std::string, std::string> >::iterator i; 843 for (i = tokens.begin(); i != tokens.end(); ++i) { 844 matches[i->first] = i->second; 845 } 846 consumer_->OnGetUserInfoSuccess(matches); 847 } else { 848 consumer_->OnGetUserInfoFailure(GenerateAuthError(data, status)); 849 } 850 } 851 852 void GaiaAuthFetcher::OnMergeSessionFetched(const std::string& data, 853 const net::URLRequestStatus& status, 854 int response_code) { 855 if (status.is_success() && response_code == net::HTTP_OK) { 856 consumer_->OnMergeSessionSuccess(data); 857 } else { 858 consumer_->OnMergeSessionFailure(GenerateAuthError(data, status)); 859 } 860 } 861 862 void GaiaAuthFetcher::OnUberAuthTokenFetch(const std::string& data, 863 const net::URLRequestStatus& status, 864 int response_code) { 865 if (status.is_success() && response_code == net::HTTP_OK) { 866 consumer_->OnUberAuthTokenSuccess(data); 867 } else { 868 consumer_->OnUberAuthTokenFailure(GenerateAuthError(data, status)); 869 } 870 } 871 872 void GaiaAuthFetcher::OnOAuthLoginFetched(const std::string& data, 873 const net::URLRequestStatus& status, 874 int response_code) { 875 if (status.is_success() && response_code == net::HTTP_OK) { 876 DVLOG(1) << "ClientLogin successful!"; 877 std::string sid; 878 std::string lsid; 879 std::string token; 880 ParseClientLoginResponse(data, &sid, &lsid, &token); 881 consumer_->OnClientLoginSuccess( 882 GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data)); 883 } else { 884 consumer_->OnClientLoginFailure(GenerateAuthError(data, status)); 885 } 886 } 887 888 void GaiaAuthFetcher::OnURLFetchComplete(const net::URLFetcher* source) { 889 fetch_pending_ = false; 890 // Some of the GAIA requests perform redirects, which results in the final 891 // URL of the fetcher not being the original URL requested. Therefore use 892 // the original URL when determining which OnXXX function to call. 893 const GURL& url = source->GetOriginalURL(); 894 const net::URLRequestStatus& status = source->GetStatus(); 895 int response_code = source->GetResponseCode(); 896 std::string data; 897 source->GetResponseAsString(&data); 898 #ifndef NDEBUG 899 std::string headers; 900 if (source->GetResponseHeaders()) 901 source->GetResponseHeaders()->GetNormalizedHeaders(&headers); 902 DVLOG(2) << "Response " << url.spec() << ", code = " << response_code << "\n" 903 << headers << "\n"; 904 DVLOG(2) << "data: " << data << "\n"; 905 #endif 906 // Retrieve the response headers from the request. Must only be called after 907 // the OnURLFetchComplete callback has run. 908 if (url == client_login_gurl_) { 909 OnClientLoginFetched(data, status, response_code); 910 } else if (url == issue_auth_token_gurl_) { 911 OnIssueAuthTokenFetched(data, status, response_code); 912 } else if (url == client_login_to_oauth2_gurl_) { 913 OnClientLoginToOAuth2Fetched( 914 data, source->GetCookies(), status, response_code); 915 } else if (url == oauth2_token_gurl_) { 916 OnOAuth2TokenPairFetched(data, status, response_code); 917 } else if (url == get_user_info_gurl_) { 918 OnGetUserInfoFetched(data, status, response_code); 919 } else if (url == merge_session_gurl_) { 920 OnMergeSessionFetched(data, status, response_code); 921 } else if (url == uberauth_token_gurl_) { 922 OnUberAuthTokenFetch(data, status, response_code); 923 } else if (url == oauth_login_gurl_) { 924 OnOAuthLoginFetched(data, status, response_code); 925 } else if (url == oauth2_revoke_gurl_) { 926 OnOAuth2RevokeTokenFetched(data, status, response_code); 927 } else if (url == list_accounts_gurl_) { 928 OnListAccountsFetched(data, status, response_code); 929 } else { 930 NOTREACHED(); 931 } 932 } 933 934 // static 935 bool GaiaAuthFetcher::IsSecondFactorSuccess( 936 const std::string& alleged_error) { 937 return alleged_error.find(kSecondFactor) != 938 std::string::npos; 939 } 940