OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "google_apis/gaia/gaia_auth_fetcher.h" | 5 #include "google_apis/gaia/gaia_auth_fetcher.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <string> | 8 #include <string> |
9 #include <utility> | 9 #include <utility> |
10 #include <vector> | 10 #include <vector> |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
128 // static | 128 // static |
129 const char GaiaAuthFetcher::kErrorParam[] = "Error"; | 129 const char GaiaAuthFetcher::kErrorParam[] = "Error"; |
130 // static | 130 // static |
131 const char GaiaAuthFetcher::kErrorUrlParam[] = "Url"; | 131 const char GaiaAuthFetcher::kErrorUrlParam[] = "Url"; |
132 // static | 132 // static |
133 const char GaiaAuthFetcher::kCaptchaUrlParam[] = "CaptchaUrl"; | 133 const char GaiaAuthFetcher::kCaptchaUrlParam[] = "CaptchaUrl"; |
134 // static | 134 // static |
135 const char GaiaAuthFetcher::kCaptchaTokenParam[] = "CaptchaToken"; | 135 const char GaiaAuthFetcher::kCaptchaTokenParam[] = "CaptchaToken"; |
136 | 136 |
137 // static | 137 // static |
138 const char GaiaAuthFetcher::kNeedsAdditional[] = "NeedsAdditional"; | |
139 // static | |
140 const char GaiaAuthFetcher::kCaptcha[] = "Captcha"; | |
141 // static | |
142 const char GaiaAuthFetcher::kTwoFactor[] = "TwoStep"; | |
143 | |
144 // static | |
145 const char GaiaAuthFetcher::kCookiePersistence[] = "true"; | 138 const char GaiaAuthFetcher::kCookiePersistence[] = "true"; |
146 // static | 139 // static |
147 // TODO(johnnyg): When hosted accounts are supported by sync, | 140 // TODO(johnnyg): When hosted accounts are supported by sync, |
148 // we can always use "HOSTED_OR_GOOGLE" | 141 // we can always use "HOSTED_OR_GOOGLE" |
149 const char GaiaAuthFetcher::kAccountTypeHostedOrGoogle[] = | 142 const char GaiaAuthFetcher::kAccountTypeHostedOrGoogle[] = |
150 "HOSTED_OR_GOOGLE"; | 143 "HOSTED_OR_GOOGLE"; |
151 const char GaiaAuthFetcher::kAccountTypeGoogle[] = | 144 const char GaiaAuthFetcher::kAccountTypeGoogle[] = |
152 "GOOGLE"; | 145 "GOOGLE"; |
153 | 146 |
154 // static | 147 // static |
(...skipping 26 matching lines...) Expand all Loading... |
181 getter_(getter), | 174 getter_(getter), |
182 source_(source), | 175 source_(source), |
183 client_login_gurl_(GaiaUrls::GetInstance()->client_login_url()), | 176 client_login_gurl_(GaiaUrls::GetInstance()->client_login_url()), |
184 issue_auth_token_gurl_(GaiaUrls::GetInstance()->issue_auth_token_url()), | 177 issue_auth_token_gurl_(GaiaUrls::GetInstance()->issue_auth_token_url()), |
185 oauth2_token_gurl_(GaiaUrls::GetInstance()->oauth2_token_url()), | 178 oauth2_token_gurl_(GaiaUrls::GetInstance()->oauth2_token_url()), |
186 oauth2_revoke_gurl_(GaiaUrls::GetInstance()->oauth2_revoke_url()), | 179 oauth2_revoke_gurl_(GaiaUrls::GetInstance()->oauth2_revoke_url()), |
187 get_user_info_gurl_(GaiaUrls::GetInstance()->get_user_info_url()), | 180 get_user_info_gurl_(GaiaUrls::GetInstance()->get_user_info_url()), |
188 merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()), | 181 merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()), |
189 uberauth_token_gurl_(base::StringPrintf(kUberAuthTokenURLFormat, | 182 uberauth_token_gurl_(base::StringPrintf(kUberAuthTokenURLFormat, |
190 GaiaUrls::GetInstance()->oauth1_login_url().c_str(), source.c_str())), | 183 GaiaUrls::GetInstance()->oauth1_login_url().c_str(), source.c_str())), |
191 client_oauth_gurl_(GaiaUrls::GetInstance()->client_oauth_url()), | |
192 oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()), | 184 oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()), |
193 client_login_to_oauth2_gurl_( | 185 client_login_to_oauth2_gurl_( |
194 GaiaUrls::GetInstance()->client_login_to_oauth2_url()), | 186 GaiaUrls::GetInstance()->client_login_to_oauth2_url()), |
195 fetch_pending_(false) {} | 187 fetch_pending_(false) {} |
196 | 188 |
197 GaiaAuthFetcher::~GaiaAuthFetcher() {} | 189 GaiaAuthFetcher::~GaiaAuthFetcher() {} |
198 | 190 |
199 bool GaiaAuthFetcher::HasPendingFetch() { | 191 bool GaiaAuthFetcher::HasPendingFetch() { |
200 return fetch_pending_; | 192 return fetch_pending_; |
201 } | 193 } |
(...skipping 187 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
389 token->assign(i->second); | 381 token->assign(i->second); |
390 } | 382 } |
391 } | 383 } |
392 // If this was a request for uberauth token, then that's all we've got in | 384 // If this was a request for uberauth token, then that's all we've got in |
393 // data. | 385 // data. |
394 if (sid->empty() && lsid->empty() && token->empty()) | 386 if (sid->empty() && lsid->empty() && token->empty()) |
395 token->assign(data); | 387 token->assign(data); |
396 } | 388 } |
397 | 389 |
398 // static | 390 // static |
399 std::string GaiaAuthFetcher::MakeClientOAuthBody( | |
400 const std::string& username, | |
401 const std::string& password, | |
402 const std::vector<std::string>& scopes, | |
403 const std::string& persistent_id, | |
404 const std::string& friendly_name, | |
405 const std::string& locale) { | |
406 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); | |
407 dict->SetString(GaiaConstants::kClientOAuthEmailKey, username); | |
408 dict->SetString(GaiaConstants::kClientOAuthPasswordKey, password); | |
409 | |
410 scoped_ptr<base::ListValue> scope_list(new base::ListValue); | |
411 for (size_t i = 0; i < scopes.size(); ++i) | |
412 scope_list->Append(base::Value::CreateStringValue(scopes[i])); | |
413 dict->Set(GaiaConstants::kClientOAuthScopesKey, scope_list.release()); | |
414 | |
415 dict->SetString(GaiaConstants::kClientOAuthOAuth2ClientIdKey, | |
416 GaiaUrls::GetInstance()->oauth2_chrome_client_id()); | |
417 // crbug.com/129600: use a less generic friendly name. | |
418 dict->SetString(GaiaConstants::kClientOAuthFriendlyDeviceNameKey, | |
419 friendly_name); | |
420 | |
421 scoped_ptr<base::ListValue> accepts_challenge_list(new base::ListValue); | |
422 accepts_challenge_list->Append(base::Value::CreateStringValue(kCaptcha)); | |
423 accepts_challenge_list->Append(base::Value::CreateStringValue(kTwoFactor)); | |
424 dict->Set(GaiaConstants::kClientOAuthAcceptsChallengesKey, | |
425 accepts_challenge_list.release()); | |
426 | |
427 dict->SetString(GaiaConstants::kClientOAuthLocaleKey, locale); | |
428 // Chrome presently does not not support a web-fallback for ClientOAuth, | |
429 // but need to hardcode an arbitrary one here since the endpoint expects it. | |
430 dict->SetString(GaiaConstants::kClientOAuthFallbackNameKey, "GetOAuth2Token"); | |
431 | |
432 std::string json_string; | |
433 base::JSONWriter::Write(dict.get(), &json_string); | |
434 return json_string; | |
435 } | |
436 | |
437 // static | |
438 std::string GaiaAuthFetcher::MakeClientOAuthChallengeResponseBody( | |
439 const std::string& name, | |
440 const std::string& token, | |
441 const std::string& solution) { | |
442 scoped_ptr<base::DictionaryValue> dict(new base::DictionaryValue); | |
443 std::string field_name = name == kTwoFactor ? "otp" : "solution"; | |
444 | |
445 scoped_ptr<base::DictionaryValue> challenge_reply(new base::DictionaryValue); | |
446 challenge_reply->SetString(GaiaConstants::kClientOAuthNameKey, name); | |
447 challenge_reply->SetString(GaiaConstants::kClientOAuthChallengeTokenKey, | |
448 token); | |
449 challenge_reply->SetString(field_name, solution); | |
450 dict->Set(GaiaConstants::kClientOAuthchallengeReplyKey, | |
451 challenge_reply.release()); | |
452 | |
453 std::string json_string; | |
454 base::JSONWriter::Write(dict.get(), &json_string); | |
455 return json_string; | |
456 } | |
457 | |
458 // static | |
459 std::string GaiaAuthFetcher::MakeOAuthLoginBody(const std::string& service, | 391 std::string GaiaAuthFetcher::MakeOAuthLoginBody(const std::string& service, |
460 const std::string& source) { | 392 const std::string& source) { |
461 std::string encoded_service = net::EscapeUrlEncodedData(service, true); | 393 std::string encoded_service = net::EscapeUrlEncodedData(service, true); |
462 std::string encoded_source = net::EscapeUrlEncodedData(source, true); | 394 std::string encoded_source = net::EscapeUrlEncodedData(source, true); |
463 return base::StringPrintf(kOAuthLoginFormat, | 395 return base::StringPrintf(kOAuthLoginFormat, |
464 encoded_service.c_str(), | 396 encoded_service.c_str(), |
465 encoded_source.c_str()); | 397 encoded_source.c_str()); |
466 } | 398 } |
467 | 399 |
468 // static | 400 // static |
(...skipping 52 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
521 if (StartsWithASCII( | 453 if (StartsWithASCII( |
522 part, kClientLoginToOAuth2CookiePartCodePrefix, false)) { | 454 part, kClientLoginToOAuth2CookiePartCodePrefix, false)) { |
523 auth_code->assign(part.substr( | 455 auth_code->assign(part.substr( |
524 kClientLoginToOAuth2CookiePartCodePrefixLength)); | 456 kClientLoginToOAuth2CookiePartCodePrefixLength)); |
525 return true; | 457 return true; |
526 } | 458 } |
527 } | 459 } |
528 return false; | 460 return false; |
529 } | 461 } |
530 | 462 |
531 // static | |
532 GoogleServiceAuthError | |
533 GaiaAuthFetcher::GenerateClientOAuthError(const std::string& data, | |
534 const net::URLRequestStatus& status) { | |
535 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); | |
536 if (!value.get() || value->GetType() != base::Value::TYPE_DICTIONARY) | |
537 return GenerateAuthError(data, status); | |
538 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); | |
539 | |
540 std::string cause; | |
541 if (!dict->GetStringWithoutPathExpansion("cause", &cause)) | |
542 return GoogleServiceAuthError::FromClientOAuthError(data); | |
543 | |
544 if (cause != kNeedsAdditional) | |
545 return GoogleServiceAuthError::FromClientOAuthError(data); | |
546 | |
547 DictionaryValue* challenge; | |
548 if (!dict->GetDictionaryWithoutPathExpansion("challenge", &challenge)) | |
549 return GoogleServiceAuthError::FromClientOAuthError(data); | |
550 | |
551 std::string name; | |
552 if (!challenge->GetStringWithoutPathExpansion("name", &name)) | |
553 return GoogleServiceAuthError::FromClientOAuthError(data); | |
554 | |
555 if (name == kCaptcha) { | |
556 std::string token; | |
557 std::string audio_url; | |
558 std::string image_url; | |
559 int image_width; | |
560 int image_height; | |
561 if (!challenge->GetStringWithoutPathExpansion("challenge_token", &token) || | |
562 !challenge->GetStringWithoutPathExpansion("audio_url", &audio_url) || | |
563 !challenge->GetStringWithoutPathExpansion("image_url", &image_url) || | |
564 !challenge->GetIntegerWithoutPathExpansion("image_width", | |
565 &image_width) || | |
566 !challenge->GetIntegerWithoutPathExpansion("image_height", | |
567 &image_height)) { | |
568 return GoogleServiceAuthError::FromClientOAuthError(data); | |
569 } | |
570 return GoogleServiceAuthError::FromCaptchaChallenge(token, GURL(audio_url), | |
571 GURL(image_url), | |
572 image_width, | |
573 image_height); | |
574 } else if (name == kTwoFactor) { | |
575 std::string token; | |
576 std::string prompt_text; | |
577 std::string alternate_text; | |
578 int field_length; | |
579 | |
580 // The protocol doc says these are required, but in practice they are not | |
581 // returned. So only a missing challenge token will cause an error here. | |
582 challenge->GetStringWithoutPathExpansion("prompt_text", &prompt_text); | |
583 challenge->GetStringWithoutPathExpansion("alternate_text", &alternate_text); | |
584 challenge->GetIntegerWithoutPathExpansion("field_length", &field_length); | |
585 if (!challenge->GetStringWithoutPathExpansion("challenge_token", &token)) | |
586 return GoogleServiceAuthError::FromClientOAuthError(data); | |
587 | |
588 return GoogleServiceAuthError::FromSecondFactorChallenge(token, prompt_text, | |
589 alternate_text, | |
590 field_length); | |
591 } | |
592 | |
593 return GoogleServiceAuthError::FromClientOAuthError(data); | |
594 } | |
595 | |
596 void GaiaAuthFetcher::StartClientLogin( | 463 void GaiaAuthFetcher::StartClientLogin( |
597 const std::string& username, | 464 const std::string& username, |
598 const std::string& password, | 465 const std::string& password, |
599 const char* const service, | 466 const char* const service, |
600 const std::string& login_token, | 467 const std::string& login_token, |
601 const std::string& login_captcha, | 468 const std::string& login_captcha, |
602 HostedAccountsSetting allow_hosted_accounts) { | 469 HostedAccountsSetting allow_hosted_accounts) { |
603 | 470 |
604 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; | 471 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
605 | 472 |
(...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
767 fetcher_.reset(CreateGaiaFetcher(getter_, | 634 fetcher_.reset(CreateGaiaFetcher(getter_, |
768 std::string(), | 635 std::string(), |
769 authentication_header, | 636 authentication_header, |
770 uberauth_token_gurl_, | 637 uberauth_token_gurl_, |
771 kLoadFlagsIgnoreCookies, | 638 kLoadFlagsIgnoreCookies, |
772 this)); | 639 this)); |
773 fetch_pending_ = true; | 640 fetch_pending_ = true; |
774 fetcher_->Start(); | 641 fetcher_->Start(); |
775 } | 642 } |
776 | 643 |
777 void GaiaAuthFetcher::StartClientOAuth(const std::string& username, | |
778 const std::string& password, | |
779 const std::vector<std::string>& scopes, | |
780 const std::string& persistent_id, | |
781 const std::string& locale) { | |
782 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; | |
783 | |
784 request_body_ = MakeClientOAuthBody(username, password, scopes, persistent_id, | |
785 source_, locale); | |
786 fetcher_.reset(CreateGaiaFetcher(getter_, | |
787 request_body_, | |
788 std::string(), | |
789 client_oauth_gurl_, | |
790 kLoadFlagsIgnoreCookies, | |
791 this)); | |
792 fetch_pending_ = true; | |
793 fetcher_->Start(); | |
794 } | |
795 | |
796 void GaiaAuthFetcher::StartClientOAuthChallengeResponse( | |
797 GoogleServiceAuthError::State type, | |
798 const std::string& token, | |
799 const std::string& solution) { | |
800 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; | |
801 | |
802 std::string name; | |
803 switch (type) { | |
804 case GoogleServiceAuthError::CAPTCHA_REQUIRED: | |
805 name = kCaptcha; | |
806 break; | |
807 case GoogleServiceAuthError::TWO_FACTOR: | |
808 name = kTwoFactor; | |
809 break; | |
810 default: | |
811 NOTREACHED(); | |
812 } | |
813 | |
814 request_body_ = MakeClientOAuthChallengeResponseBody(name, token, solution); | |
815 fetcher_.reset(CreateGaiaFetcher(getter_, | |
816 request_body_, | |
817 std::string(), | |
818 client_oauth_gurl_, | |
819 kLoadFlagsIgnoreCookies, | |
820 this)); | |
821 fetch_pending_ = true; | |
822 fetcher_->Start(); | |
823 } | |
824 | |
825 void GaiaAuthFetcher::StartOAuthLogin(const std::string& access_token, | 644 void GaiaAuthFetcher::StartOAuthLogin(const std::string& access_token, |
826 const std::string& service) { | 645 const std::string& service) { |
827 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; | 646 DCHECK(!fetch_pending_) << "Tried to fetch two things at once!"; |
828 | 647 |
829 request_body_ = MakeOAuthLoginBody(service, source_); | 648 request_body_ = MakeOAuthLoginBody(service, source_); |
830 std::string authentication_header = | 649 std::string authentication_header = |
831 base::StringPrintf(kOAuth2BearerHeaderFormat, access_token.c_str()); | 650 base::StringPrintf(kOAuth2BearerHeaderFormat, access_token.c_str()); |
832 fetcher_.reset(CreateGaiaFetcher(getter_, | 651 fetcher_.reset(CreateGaiaFetcher(getter_, |
833 request_body_, | 652 request_body_, |
834 authentication_header, | 653 authentication_header, |
(...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1054 void GaiaAuthFetcher::OnUberAuthTokenFetch(const std::string& data, | 873 void GaiaAuthFetcher::OnUberAuthTokenFetch(const std::string& data, |
1055 const net::URLRequestStatus& status, | 874 const net::URLRequestStatus& status, |
1056 int response_code) { | 875 int response_code) { |
1057 if (status.is_success() && response_code == net::HTTP_OK) { | 876 if (status.is_success() && response_code == net::HTTP_OK) { |
1058 consumer_->OnUberAuthTokenSuccess(data); | 877 consumer_->OnUberAuthTokenSuccess(data); |
1059 } else { | 878 } else { |
1060 consumer_->OnUberAuthTokenFailure(GenerateAuthError(data, status)); | 879 consumer_->OnUberAuthTokenFailure(GenerateAuthError(data, status)); |
1061 } | 880 } |
1062 } | 881 } |
1063 | 882 |
1064 void GaiaAuthFetcher::OnClientOAuthFetched(const std::string& data, | |
1065 const net::URLRequestStatus& status, | |
1066 int response_code) { | |
1067 std::string refresh_token; | |
1068 std::string access_token; | |
1069 int expires_in_secs = 0; | |
1070 | |
1071 bool success = false; | |
1072 if (status.is_success() && response_code == net::HTTP_OK) { | |
1073 scoped_ptr<base::Value> value(base::JSONReader::Read(data)); | |
1074 if (value.get() && value->GetType() == base::Value::TYPE_DICTIONARY) { | |
1075 DictionaryValue* dict = static_cast<DictionaryValue*>(value.get()); | |
1076 DictionaryValue* dict_oauth2; | |
1077 if (dict->GetDictionaryWithoutPathExpansion("oauth2", &dict_oauth2)) { | |
1078 success = ExtractOAuth2TokenPairResponse(dict_oauth2, &refresh_token, | |
1079 &access_token, | |
1080 &expires_in_secs); | |
1081 } | |
1082 } | |
1083 } | |
1084 | |
1085 // TODO(rogerta): for now this reuses the OnOAuthLoginTokenXXX callbacks | |
1086 // since the data is exactly the same. This ignores the optional | |
1087 // persistent_id data in the response, which we may need to handle. | |
1088 // If we do, we'll need to modify ExtractOAuth2TokenPairResponse() to parse | |
1089 // the optional data and declare new consumer callbacks to take it. | |
1090 if (success) { | |
1091 consumer_->OnClientOAuthSuccess( | |
1092 GaiaAuthConsumer::ClientOAuthResult(refresh_token, access_token, | |
1093 expires_in_secs)); | |
1094 } else { | |
1095 consumer_->OnClientOAuthFailure(GenerateClientOAuthError(data, status)); | |
1096 } | |
1097 } | |
1098 | |
1099 void GaiaAuthFetcher::OnOAuthLoginFetched(const std::string& data, | 883 void GaiaAuthFetcher::OnOAuthLoginFetched(const std::string& data, |
1100 const net::URLRequestStatus& status, | 884 const net::URLRequestStatus& status, |
1101 int response_code) { | 885 int response_code) { |
1102 if (status.is_success() && response_code == net::HTTP_OK) { | 886 if (status.is_success() && response_code == net::HTTP_OK) { |
1103 DVLOG(1) << "ClientLogin successful!"; | 887 DVLOG(1) << "ClientLogin successful!"; |
1104 std::string sid; | 888 std::string sid; |
1105 std::string lsid; | 889 std::string lsid; |
1106 std::string token; | 890 std::string token; |
1107 ParseClientLoginResponse(data, &sid, &lsid, &token); | 891 ParseClientLoginResponse(data, &sid, &lsid, &token); |
1108 consumer_->OnClientLoginSuccess( | 892 consumer_->OnClientLoginSuccess( |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1140 OnClientLoginToOAuth2Fetched( | 924 OnClientLoginToOAuth2Fetched( |
1141 data, source->GetCookies(), status, response_code); | 925 data, source->GetCookies(), status, response_code); |
1142 } else if (url == oauth2_token_gurl_) { | 926 } else if (url == oauth2_token_gurl_) { |
1143 OnOAuth2TokenPairFetched(data, status, response_code); | 927 OnOAuth2TokenPairFetched(data, status, response_code); |
1144 } else if (url == get_user_info_gurl_) { | 928 } else if (url == get_user_info_gurl_) { |
1145 OnGetUserInfoFetched(data, status, response_code); | 929 OnGetUserInfoFetched(data, status, response_code); |
1146 } else if (url == merge_session_gurl_) { | 930 } else if (url == merge_session_gurl_) { |
1147 OnMergeSessionFetched(data, status, response_code); | 931 OnMergeSessionFetched(data, status, response_code); |
1148 } else if (url == uberauth_token_gurl_) { | 932 } else if (url == uberauth_token_gurl_) { |
1149 OnUberAuthTokenFetch(data, status, response_code); | 933 OnUberAuthTokenFetch(data, status, response_code); |
1150 } else if (url == client_oauth_gurl_) { | |
1151 OnClientOAuthFetched(data, status, response_code); | |
1152 } else if (url == oauth_login_gurl_) { | 934 } else if (url == oauth_login_gurl_) { |
1153 OnOAuthLoginFetched(data, status, response_code); | 935 OnOAuthLoginFetched(data, status, response_code); |
1154 } else if (url == oauth2_revoke_gurl_) { | 936 } else if (url == oauth2_revoke_gurl_) { |
1155 OnOAuth2RevokeTokenFetched(data, status, response_code); | 937 OnOAuth2RevokeTokenFetched(data, status, response_code); |
1156 } else { | 938 } else { |
1157 NOTREACHED(); | 939 NOTREACHED(); |
1158 } | 940 } |
1159 } | 941 } |
1160 | 942 |
1161 // static | 943 // static |
1162 bool GaiaAuthFetcher::IsSecondFactorSuccess( | 944 bool GaiaAuthFetcher::IsSecondFactorSuccess( |
1163 const std::string& alleged_error) { | 945 const std::string& alleged_error) { |
1164 return alleged_error.find(kSecondFactor) != | 946 return alleged_error.find(kSecondFactor) != |
1165 std::string::npos; | 947 std::string::npos; |
1166 } | 948 } |
OLD | NEW |