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