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