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