gaia_auth_fetcher.cc 45 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183
  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. #include "google_apis/gaia/gaia_auth_fetcher.h"
  5. #include <string>
  6. #include <utility>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/json/json_reader.h"
  10. #include "base/json/json_writer.h"
  11. #include "base/metrics/histogram_functions.h"
  12. #include "base/metrics/histogram_macros.h"
  13. #include "base/metrics/user_metrics.h"
  14. #include "base/metrics/user_metrics_action.h"
  15. #include "base/stl_util.h"
  16. #include "base/strings/escape.h"
  17. #include "base/strings/string_split.h"
  18. #include "base/strings/string_util.h"
  19. #include "base/strings/stringprintf.h"
  20. #include "base/system/sys_info.h"
  21. #include "base/values.h"
  22. #include "google_apis/gaia/gaia_auth_consumer.h"
  23. #include "google_apis/gaia/gaia_auth_util.h"
  24. #include "google_apis/gaia/gaia_constants.h"
  25. #include "google_apis/gaia/gaia_urls.h"
  26. #include "google_apis/gaia/google_service_auth_error.h"
  27. #include "google_apis/gaia/oauth2_id_token_decoder.h"
  28. #include "google_apis/gaia/oauth_multilogin_result.h"
  29. #include "net/base/isolation_info.h"
  30. #include "net/http/http_response_headers.h"
  31. #include "net/http/http_status_code.h"
  32. #include "services/network/public/cpp/resource_request.h"
  33. #include "services/network/public/cpp/shared_url_loader_factory.h"
  34. #include "services/network/public/cpp/simple_url_loader.h"
  35. #include "services/network/public/mojom/url_response_head.mojom.h"
  36. #include "third_party/abseil-cpp/absl/types/optional.h"
  37. namespace {
  38. const size_t kMaxMessageSize = 1024 * 1024; // 1MB
  39. constexpr char kBadAuthenticationError[] = "BadAuthentication";
  40. constexpr char kBadAuthenticationShortError[] = "badauth";
  41. constexpr char kServiceUnavailableError[] = "ServiceUnavailable";
  42. constexpr char kServiceUnavailableShortError[] = "ire";
  43. constexpr char kFormEncodedContentType[] = "application/x-www-form-urlencoded";
  44. constexpr char kJsonContentType[] = "application/json;charset=UTF-8";
  45. std::unique_ptr<const GaiaAuthConsumer::ClientOAuthResult>
  46. ExtractOAuth2TokenPairResponse(const std::string& data) {
  47. std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data);
  48. if (!value.get() || value->type() != base::Value::Type::DICTIONARY)
  49. return nullptr;
  50. base::DictionaryValue* dict =
  51. static_cast<base::DictionaryValue*>(value.get());
  52. std::string* refresh_token = dict->FindStringKey("refresh_token");
  53. std::string* access_token = dict->FindStringKey("access_token");
  54. absl::optional<int> expires_in_secs = dict->FindIntKey("expires_in");
  55. if (!refresh_token || !access_token || !expires_in_secs.has_value())
  56. return nullptr;
  57. // Extract ID token when obtaining refresh token. Do not fail if absent,
  58. // but log to keep track.
  59. std::string* id_token = dict->FindStringKey("id_token");
  60. if (!id_token)
  61. LOG(ERROR) << "Missing ID token on refresh token fetch response.";
  62. gaia::TokenServiceFlags service_flags =
  63. gaia::ParseServiceFlags(id_token ? *id_token : std::string());
  64. return std::make_unique<const GaiaAuthConsumer::ClientOAuthResult>(
  65. *refresh_token, *access_token, expires_in_secs.value(),
  66. service_flags.is_child_account,
  67. service_flags.is_under_advanced_protection);
  68. }
  69. // Parses server responses for token revocation.
  70. GaiaAuthConsumer::TokenRevocationStatus
  71. GetTokenRevocationStatusFromResponseData(const std::string& data,
  72. int response_code) {
  73. if (response_code == net::HTTP_OK)
  74. return GaiaAuthConsumer::TokenRevocationStatus::kSuccess;
  75. if (response_code == net::HTTP_INTERNAL_SERVER_ERROR)
  76. return GaiaAuthConsumer::TokenRevocationStatus::kServerError;
  77. std::unique_ptr<base::Value> value = base::JSONReader::ReadDeprecated(data);
  78. if (!value.get() || value->type() != base::Value::Type::DICTIONARY)
  79. return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
  80. base::DictionaryValue* dict =
  81. static_cast<base::DictionaryValue*>(value.get());
  82. std::string* error = dict->FindStringKey("error");
  83. if (!error)
  84. return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
  85. if (*error == "invalid_token")
  86. return GaiaAuthConsumer::TokenRevocationStatus::kInvalidToken;
  87. if (*error == "invalid_request")
  88. return GaiaAuthConsumer::TokenRevocationStatus::kInvalidRequest;
  89. return GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
  90. }
  91. std::unique_ptr<base::DictionaryValue> ParseJSONDict(const std::string& data) {
  92. std::unique_ptr<base::DictionaryValue> response_dict;
  93. absl::optional<base::Value> message_value = base::JSONReader::Read(data);
  94. if (message_value && message_value->is_dict()) {
  95. response_dict = std::make_unique<base::DictionaryValue>();
  96. response_dict->MergeDictionary(base::OptionalOrNullptr(message_value));
  97. }
  98. return response_dict;
  99. }
  100. GaiaAuthConsumer::ReAuthProofTokenStatus ErrorMessageToReAuthProofTokenStatus(
  101. const std::string& message) {
  102. if (message == "INVALID_REQUEST") {
  103. return GaiaAuthConsumer::ReAuthProofTokenStatus::kInvalidRequest;
  104. }
  105. if (message == "INVALID_GRANT") {
  106. return GaiaAuthConsumer::ReAuthProofTokenStatus::kInvalidGrant;
  107. }
  108. if (message == "UNAUTHORIZED_CLIENT") {
  109. return GaiaAuthConsumer::ReAuthProofTokenStatus::kUnauthorizedClient;
  110. }
  111. if (message == "INSUFFICIENT_SCOPE") {
  112. return GaiaAuthConsumer::ReAuthProofTokenStatus::kInsufficientScope;
  113. }
  114. if (message == "CREDENTIAL_NOT_SET") {
  115. return GaiaAuthConsumer::ReAuthProofTokenStatus::kCredentialNotSet;
  116. }
  117. DLOG(ERROR) << "Unrecognized ReauthAPI error message: " + message;
  118. return GaiaAuthConsumer::ReAuthProofTokenStatus::kUnknownError;
  119. }
  120. } // namespace
  121. namespace gaia {
  122. GaiaSource::GaiaSource(Type type) : type_(type) {}
  123. GaiaSource::GaiaSource(Type type, const std::string& suffix)
  124. : type_(type), suffix_(suffix) {}
  125. void GaiaSource::SetGaiaSourceSuffix(const std::string& suffix) {
  126. suffix_ = suffix;
  127. }
  128. std::string GaiaSource::ToString() {
  129. std::string source_string;
  130. switch (type_) {
  131. case Type::kChrome:
  132. source_string = GaiaConstants::kChromeSource;
  133. break;
  134. case Type::kChromeOS:
  135. source_string = GaiaConstants::kChromeOSSource;
  136. break;
  137. case Type::kAccountReconcilorDice:
  138. source_string = "ChromiumAccountReconcilorDice";
  139. break;
  140. case Type::kAccountReconcilorMirror:
  141. source_string = "ChromiumAccountReconcilor";
  142. break;
  143. case Type::kOAuth2LoginVerifier:
  144. source_string = "ChromiumOAuth2LoginVerifier";
  145. break;
  146. case Type::kPrimaryAccountManager:
  147. // Even though this string refers to an old name from the Chromium POV, it
  148. // should not be changed as it is passed server-side.
  149. source_string = "ChromiumSigninManager";
  150. break;
  151. }
  152. // All sources should start with Chromium or chromeos for better server logs.
  153. DCHECK(source_string == "chromeos" ||
  154. base::StartsWith(source_string, "Chromium",
  155. base::CompareCase::SENSITIVE));
  156. return source_string + suffix_;
  157. }
  158. } // namespace gaia
  159. // static
  160. const char GaiaAuthFetcher::kIssueAuthTokenFormat[] =
  161. "SID=%s&"
  162. "LSID=%s&"
  163. "service=%s&"
  164. "Session=%s";
  165. // static
  166. const char GaiaAuthFetcher::kOAuth2CodeToTokenPairBodyFormat[] =
  167. "scope=%s&"
  168. "grant_type=authorization_code&"
  169. "client_id=%s&"
  170. "client_secret=%s&"
  171. "code=%s";
  172. // static
  173. const char GaiaAuthFetcher::kOAuth2CodeToTokenPairDeviceIdParam[] =
  174. "device_id=%s&device_type=chrome";
  175. // static
  176. const char GaiaAuthFetcher::kOAuth2RevokeTokenBodyFormat[] =
  177. "token=%s";
  178. // static
  179. const char GaiaAuthFetcher::kMergeSessionFormat[] =
  180. "?uberauth=%s&"
  181. "continue=%s&"
  182. "source=%s";
  183. // static
  184. const char GaiaAuthFetcher::kUberAuthTokenURLFormat[] =
  185. "?source=%s&"
  186. "issueuberauth=1";
  187. const char GaiaAuthFetcher::kOAuthLoginFormat[] = "service=%s&source=%s";
  188. // static
  189. const char GaiaAuthFetcher::kErrorParam[] = "Error";
  190. // static
  191. const char GaiaAuthFetcher::kErrorUrlParam[] = "Url";
  192. // static
  193. const char GaiaAuthFetcher::kAuthHeaderFormat[] =
  194. "Authorization: GoogleLogin auth=%s";
  195. // static
  196. const char GaiaAuthFetcher::kOAuthHeaderFormat[] = "Authorization: OAuth %s";
  197. // static
  198. const char GaiaAuthFetcher::kOAuthMultiBearerHeaderFormat[] =
  199. "Authorization: MultiBearer %s";
  200. // static
  201. const char GaiaAuthFetcher::kOAuth2BearerHeaderFormat[] =
  202. "Authorization: Bearer %s";
  203. GaiaAuthFetcher::GaiaAuthFetcher(
  204. GaiaAuthConsumer* consumer,
  205. gaia::GaiaSource source,
  206. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
  207. : url_loader_factory_(url_loader_factory),
  208. consumer_(consumer),
  209. source_(source.ToString()),
  210. oauth2_token_gurl_(GaiaUrls::GetInstance()->oauth2_token_url()),
  211. oauth2_revoke_gurl_(GaiaUrls::GetInstance()->oauth2_revoke_url()),
  212. merge_session_gurl_(GaiaUrls::GetInstance()->merge_session_url()),
  213. uberauth_token_gurl_(GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
  214. base::StringPrintf(kUberAuthTokenURLFormat, source_.c_str()))),
  215. oauth_login_gurl_(GaiaUrls::GetInstance()->oauth1_login_url()),
  216. oauth_multilogin_gurl_(GaiaUrls::GetInstance()->oauth_multilogin_url()),
  217. list_accounts_gurl_(
  218. GaiaUrls::GetInstance()->ListAccountsURLWithSource(source_)),
  219. logout_gurl_(GaiaUrls::GetInstance()->LogOutURLWithSource(source_)),
  220. get_check_connection_info_url_(
  221. GaiaUrls::GetInstance()->GetCheckConnectionInfoURLWithSource(
  222. source_)),
  223. reauth_api_url_(GaiaUrls::GetInstance()->reauth_api_url()) {}
  224. GaiaAuthFetcher::~GaiaAuthFetcher() {}
  225. bool GaiaAuthFetcher::HasPendingFetch() {
  226. return fetch_pending_;
  227. }
  228. void GaiaAuthFetcher::SetPendingFetch(bool pending_fetch) {
  229. fetch_pending_ = pending_fetch;
  230. }
  231. void GaiaAuthFetcher::CancelRequest() {
  232. url_loader_.reset();
  233. original_url_ = GURL();
  234. fetch_pending_ = false;
  235. }
  236. bool GaiaAuthFetcher::IsMultiloginUrl(const GURL& url) {
  237. return base::StartsWith(url.spec(), oauth_multilogin_gurl_.spec(),
  238. base::CompareCase::SENSITIVE);
  239. }
  240. bool GaiaAuthFetcher::IsReAuthApiUrl(const GURL& url) {
  241. return base::StartsWith(url.spec(), reauth_api_url_.spec(),
  242. base::CompareCase::SENSITIVE);
  243. }
  244. void GaiaAuthFetcher::CreateAndStartGaiaFetcher(
  245. const std::string& body,
  246. const std::string& body_content_type,
  247. const std::string& headers,
  248. const GURL& gaia_gurl,
  249. network::mojom::CredentialsMode credentials_mode,
  250. const net::NetworkTrafficAnnotationTag& traffic_annotation) {
  251. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  252. auto resource_request = std::make_unique<network::ResourceRequest>();
  253. resource_request->url = gaia_gurl;
  254. original_url_ = gaia_gurl;
  255. if (credentials_mode != network::mojom::CredentialsMode::kOmit) {
  256. CHECK(gaia::HasGaiaSchemeHostPort(gaia_gurl)) << gaia_gurl;
  257. url::Origin origin = GaiaUrls::GetInstance()->gaia_origin();
  258. resource_request->site_for_cookies =
  259. net::SiteForCookies::FromOrigin(origin);
  260. resource_request->trusted_params =
  261. network::ResourceRequest::TrustedParams();
  262. resource_request->trusted_params->isolation_info =
  263. net::IsolationInfo::CreateForInternalRequest(origin);
  264. }
  265. if (!body.empty())
  266. resource_request->method = "POST";
  267. if (!headers.empty())
  268. resource_request->headers.AddHeadersFromString(headers);
  269. // The Gaia token exchange requests do not require any cookie-based
  270. // identification as part of requests. We suppress sending any cookies to
  271. // maintain a separation between the user's browsing and Chrome's internal
  272. // services. Where such mixing is desired (MergeSession or OAuthLogin), it
  273. // will be done explicitly.
  274. resource_request->credentials_mode = credentials_mode;
  275. url_loader_ = network::SimpleURLLoader::Create(std::move(resource_request),
  276. traffic_annotation);
  277. if (!body.empty()) {
  278. DCHECK(!body_content_type.empty());
  279. url_loader_->AttachStringForUpload(body, body_content_type);
  280. }
  281. url_loader_->SetAllowHttpErrorResults(true);
  282. VLOG(2) << "Gaia fetcher URL: " << gaia_gurl.spec();
  283. VLOG(2) << "Gaia fetcher headers: " << headers;
  284. VLOG(2) << "Gaia fetcher body: " << body;
  285. // Fetchers are sometimes cancelled because a network change was detected,
  286. // especially at startup and after sign-in on ChromeOS. Retrying once should
  287. // be enough in those cases; let the fetcher retry up to 3 times just in case.
  288. // http://crbug.com/163710
  289. url_loader_->SetRetryOptions(
  290. 3, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
  291. fetch_pending_ = true;
  292. // Unretained is OK below as |url_loader_| is owned by this.
  293. url_loader_->DownloadToString(
  294. url_loader_factory_.get(),
  295. base::BindOnce(&GaiaAuthFetcher::OnURLLoadComplete,
  296. base::Unretained(this)),
  297. kMaxMessageSize);
  298. }
  299. // static
  300. std::string GaiaAuthFetcher::MakeIssueAuthTokenBody(
  301. const std::string& sid,
  302. const std::string& lsid,
  303. const char* const service) {
  304. std::string encoded_sid = base::EscapeUrlEncodedData(sid, true);
  305. std::string encoded_lsid = base::EscapeUrlEncodedData(lsid, true);
  306. // All tokens should be session tokens except the gaia auth token.
  307. bool session = true;
  308. if (!strcmp(service, GaiaConstants::kGaiaService))
  309. session = false;
  310. return base::StringPrintf(kIssueAuthTokenFormat,
  311. encoded_sid.c_str(),
  312. encoded_lsid.c_str(),
  313. service,
  314. session ? "true" : "false");
  315. }
  316. // static
  317. std::string GaiaAuthFetcher::MakeGetTokenPairBody(
  318. const std::string& auth_code,
  319. const std::string& device_id) {
  320. std::string encoded_scope =
  321. base::EscapeUrlEncodedData(GaiaConstants::kOAuth1LoginScope, true);
  322. std::string encoded_client_id = base::EscapeUrlEncodedData(
  323. GaiaUrls::GetInstance()->oauth2_chrome_client_id(), true);
  324. std::string encoded_client_secret = base::EscapeUrlEncodedData(
  325. GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), true);
  326. std::string encoded_auth_code = base::EscapeUrlEncodedData(auth_code, true);
  327. std::string body = base::StringPrintf(
  328. kOAuth2CodeToTokenPairBodyFormat, encoded_scope.c_str(),
  329. encoded_client_id.c_str(), encoded_client_secret.c_str(),
  330. encoded_auth_code.c_str());
  331. if (!device_id.empty()) {
  332. body += "&" + base::StringPrintf(kOAuth2CodeToTokenPairDeviceIdParam,
  333. device_id.c_str());
  334. }
  335. return body;
  336. }
  337. // static
  338. std::string GaiaAuthFetcher::MakeRevokeTokenBody(
  339. const std::string& auth_token) {
  340. return base::StringPrintf(kOAuth2RevokeTokenBodyFormat, auth_token.c_str());
  341. }
  342. // static
  343. std::string GaiaAuthFetcher::MakeMergeSessionQuery(
  344. const std::string& auth_token,
  345. const std::string& external_cc_result,
  346. const std::string& continue_url,
  347. const std::string& source) {
  348. std::string encoded_auth_token = base::EscapeUrlEncodedData(auth_token, true);
  349. std::string encoded_continue_url =
  350. base::EscapeUrlEncodedData(continue_url, true);
  351. std::string encoded_source = base::EscapeUrlEncodedData(source, true);
  352. std::string result = base::StringPrintf(kMergeSessionFormat,
  353. encoded_auth_token.c_str(),
  354. encoded_continue_url.c_str(),
  355. encoded_source.c_str());
  356. if (!external_cc_result.empty()) {
  357. base::StringAppendF(
  358. &result, "&externalCcResult=%s",
  359. base::EscapeUrlEncodedData(external_cc_result, true).c_str());
  360. }
  361. return result;
  362. }
  363. // static
  364. std::string GaiaAuthFetcher::MakeGetAuthCodeHeader(
  365. const std::string& auth_token) {
  366. return base::StringPrintf(kAuthHeaderFormat, auth_token.c_str());
  367. }
  368. // Helper method that extracts tokens from a successful reply.
  369. // static
  370. void GaiaAuthFetcher::ParseClientLoginResponse(const std::string& data,
  371. std::string* sid,
  372. std::string* lsid,
  373. std::string* token) {
  374. using std::vector;
  375. using std::pair;
  376. using std::string;
  377. sid->clear();
  378. lsid->clear();
  379. token->clear();
  380. base::StringPairs tokens;
  381. base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens);
  382. for (base::StringPairs::iterator i = tokens.begin();
  383. i != tokens.end(); ++i) {
  384. if (i->first == "SID") {
  385. sid->assign(i->second);
  386. } else if (i->first == "LSID") {
  387. lsid->assign(i->second);
  388. } else if (i->first == "Auth") {
  389. token->assign(i->second);
  390. }
  391. }
  392. // If this was a request for uberauth token, then that's all we've got in
  393. // data.
  394. if (sid->empty() && lsid->empty() && token->empty())
  395. token->assign(data);
  396. }
  397. // static
  398. std::string GaiaAuthFetcher::MakeOAuthLoginBody(const std::string& service,
  399. const std::string& source) {
  400. std::string encoded_service = base::EscapeUrlEncodedData(service, true);
  401. std::string encoded_source = base::EscapeUrlEncodedData(source, true);
  402. return base::StringPrintf(kOAuthLoginFormat,
  403. encoded_service.c_str(),
  404. encoded_source.c_str());
  405. }
  406. // static
  407. void GaiaAuthFetcher::ParseClientLoginFailure(const std::string& data,
  408. std::string* error,
  409. std::string* error_url) {
  410. using std::vector;
  411. using std::pair;
  412. using std::string;
  413. base::StringPairs tokens;
  414. base::SplitStringIntoKeyValuePairs(data, '=', '\n', &tokens);
  415. for (base::StringPairs::iterator i = tokens.begin();
  416. i != tokens.end(); ++i) {
  417. if (i->first == kErrorParam) {
  418. error->assign(i->second);
  419. } else if (i->first == kErrorUrlParam) {
  420. error_url->assign(i->second);
  421. }
  422. }
  423. }
  424. void GaiaAuthFetcher::StartRevokeOAuth2Token(const std::string& auth_token) {
  425. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  426. VLOG(1) << "Starting OAuth2 token revocation";
  427. request_body_ = MakeRevokeTokenBody(auth_token);
  428. net::NetworkTrafficAnnotationTag traffic_annotation =
  429. net::DefineNetworkTrafficAnnotation("gaia_auth_revoke_token", R"(
  430. semantics {
  431. sender: "Chrome - Google authentication API"
  432. description: "This request revokes an OAuth 2.0 refresh token."
  433. trigger:
  434. "This request is part of Gaia Auth API, and is triggered whenever "
  435. "an OAuth 2.0 refresh token needs to be revoked."
  436. data: "The OAuth 2.0 refresh token that should be revoked."
  437. destination: GOOGLE_OWNED_SERVICE
  438. }
  439. policy {
  440. cookies_allowed: NO
  441. setting:
  442. "This feature cannot be disabled in settings, but if the user "
  443. "signs out of Chrome, this request would not be made."
  444. chrome_policy {
  445. SigninAllowed {
  446. policy_options {mode: MANDATORY}
  447. SigninAllowed: false
  448. }
  449. }
  450. })");
  451. CreateAndStartGaiaFetcher(request_body_, kFormEncodedContentType,
  452. std::string(), oauth2_revoke_gurl_,
  453. network::mojom::CredentialsMode::kOmit,
  454. traffic_annotation);
  455. }
  456. void GaiaAuthFetcher::StartAuthCodeForOAuth2TokenExchange(
  457. const std::string& auth_code) {
  458. StartAuthCodeForOAuth2TokenExchangeWithDeviceId(auth_code, std::string());
  459. }
  460. void GaiaAuthFetcher::StartAuthCodeForOAuth2TokenExchangeWithDeviceId(
  461. const std::string& auth_code,
  462. const std::string& device_id) {
  463. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  464. VLOG(1) << "Starting OAuth token pair fetch";
  465. request_body_ = MakeGetTokenPairBody(auth_code, device_id);
  466. net::NetworkTrafficAnnotationTag traffic_annotation =
  467. net::DefineNetworkTrafficAnnotation("gaia_auth_exchange_device_id", R"(
  468. semantics {
  469. sender: "Chrome - Google authentication API"
  470. description:
  471. "This request exchanges an authorization code for an OAuth 2.0 "
  472. "refresh token."
  473. trigger:
  474. "This request is part of Gaia Auth API, and may be triggered at "
  475. "the end of the Chrome sign-in flow."
  476. data:
  477. "The Google console client ID and client secret of the Chrome "
  478. "application, the OAuth 2.0 authorization code, and the ID of the "
  479. "device."
  480. destination: GOOGLE_OWNED_SERVICE
  481. }
  482. policy {
  483. cookies_allowed: NO
  484. setting:
  485. "This feature cannot be disabled in settings, but if the user "
  486. "signs out of Chrome, this request would not be made."
  487. chrome_policy {
  488. SigninAllowed {
  489. policy_options {mode: MANDATORY}
  490. SigninAllowed: false
  491. }
  492. }
  493. })");
  494. CreateAndStartGaiaFetcher(
  495. request_body_, kFormEncodedContentType, std::string(), oauth2_token_gurl_,
  496. network::mojom::CredentialsMode::kOmit, traffic_annotation);
  497. }
  498. void GaiaAuthFetcher::StartMergeSession(const std::string& uber_token,
  499. const std::string& external_cc_result) {
  500. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  501. VLOG(1) << "Starting MergeSession with uber_token=" << uber_token;
  502. // The continue URL is a required parameter of the MergeSession API, but in
  503. // this case we don't actually need or want to navigate to it. Setting it to
  504. // an arbitrary Google URL.
  505. //
  506. // In order for the new session to be merged correctly, the server needs to
  507. // know what sessions already exist in the browser. The fetcher needs to be
  508. // created such that it sends the cookies with the request, which is
  509. // different from all other requests the fetcher can make.
  510. std::string continue_url("http://www.google.com");
  511. std::string query = MakeMergeSessionQuery(uber_token, external_cc_result,
  512. continue_url, source_);
  513. net::NetworkTrafficAnnotationTag traffic_annotation =
  514. net::DefineNetworkTrafficAnnotation("gaia_auth_merge_sessions", R"(
  515. semantics {
  516. sender: "Chrome - Google authentication API"
  517. description:
  518. "This request adds an account to the Google authentication cookies."
  519. trigger:
  520. "This request is part of Gaia Auth API, and is triggered whenever "
  521. "a new Google account is added to the browser."
  522. data:
  523. "This request includes the user-auth token and sometimes a string "
  524. "containing the result of connection checks for various Google web "
  525. "properties."
  526. destination: GOOGLE_OWNED_SERVICE
  527. }
  528. policy {
  529. cookies_allowed: YES
  530. cookies_store: "user"
  531. setting:
  532. "This feature cannot be disabled in settings, but if the user "
  533. "signs out of Chrome, this request would not be made."
  534. chrome_policy {
  535. SigninAllowed {
  536. policy_options {mode: MANDATORY}
  537. SigninAllowed: false
  538. }
  539. }
  540. })");
  541. CreateAndStartGaiaFetcher(std::string(), std::string(), std::string(),
  542. merge_session_gurl_.Resolve(query),
  543. network::mojom::CredentialsMode::kInclude,
  544. traffic_annotation);
  545. }
  546. void GaiaAuthFetcher::StartTokenFetchForUberAuthExchange(
  547. const std::string& access_token) {
  548. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  549. VLOG(1) << "Starting StartTokenFetchForUberAuthExchange with access_token="
  550. << access_token;
  551. std::string authentication_header =
  552. base::StringPrintf(kOAuthHeaderFormat, access_token.c_str());
  553. net::NetworkTrafficAnnotationTag traffic_annotation =
  554. net::DefineNetworkTrafficAnnotation("gaia_auth_fetch_for_uber", R"(
  555. semantics {
  556. sender: "Chrome - Google authentication API"
  557. description:
  558. "This request exchanges an Oauth2 access token for an uber-auth "
  559. "token. This token may be used to add an account to the Google "
  560. "authentication cookies."
  561. trigger:
  562. "This request is part of Gaia Auth API, and is triggered whenever "
  563. "a new Google account is added to the browser."
  564. data: "This request contains an OAuth 2.0 access token. "
  565. destination: GOOGLE_OWNED_SERVICE
  566. }
  567. policy {
  568. cookies_allowed: YES
  569. cookies_store: "user"
  570. setting:
  571. "This feature cannot be disabled in settings, but if the user "
  572. "signs out of Chrome, this request would not be made."
  573. chrome_policy {
  574. SigninAllowed {
  575. policy_options {mode: MANDATORY}
  576. SigninAllowed: false
  577. }
  578. }
  579. })");
  580. CreateAndStartGaiaFetcher(
  581. std::string(), std::string(), authentication_header, uberauth_token_gurl_,
  582. network::mojom::CredentialsMode::kOmit, traffic_annotation);
  583. }
  584. void GaiaAuthFetcher::StartOAuthLogin(const std::string& access_token,
  585. const std::string& service) {
  586. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  587. request_body_ = MakeOAuthLoginBody(service, source_);
  588. std::string authentication_header =
  589. base::StringPrintf(kOAuth2BearerHeaderFormat, access_token.c_str());
  590. net::NetworkTrafficAnnotationTag traffic_annotation =
  591. net::DefineNetworkTrafficAnnotation("gaia_auth_login", R"(
  592. semantics {
  593. sender: "Chrome - Google authentication API"
  594. description:
  595. "This request exchanges an OAuthLogin-scoped OAuth 2.0 access "
  596. "token for a ClientLogin-style service tokens. The response to "
  597. "this request is the same as the response to a ClientLogin "
  598. "request, except that captcha challenges are never issued."
  599. trigger:
  600. "This request is part of Gaia Auth API, and is triggered after "
  601. "signing in with a child account."
  602. data:
  603. "This request contains an OAuth 2.0 access token and the service "
  604. "for which a ClientLogin-style should be delivered."
  605. destination: GOOGLE_OWNED_SERVICE
  606. }
  607. policy {
  608. cookies_allowed: YES
  609. cookies_store: "user"
  610. setting:
  611. "This feature cannot be disabled in settings, but if the user "
  612. "signs out of Chrome, this request would not be made."
  613. chrome_policy {
  614. SigninAllowed {
  615. policy_options {mode: MANDATORY}
  616. SigninAllowed: false
  617. }
  618. }
  619. })");
  620. CreateAndStartGaiaFetcher(request_body_, kFormEncodedContentType,
  621. authentication_header, oauth_login_gurl_,
  622. network::mojom::CredentialsMode::kInclude,
  623. traffic_annotation);
  624. }
  625. void GaiaAuthFetcher::StartListAccounts() {
  626. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  627. net::NetworkTrafficAnnotationTag traffic_annotation =
  628. net::DefineNetworkTrafficAnnotation("gaia_auth_list_accounts", R"(
  629. semantics {
  630. sender: "Chrome - Google authentication API"
  631. description:
  632. "This request is used to list the accounts in the Google "
  633. "authentication cookies."
  634. trigger:
  635. "This request is part of Gaia Auth API, and is triggered whenever "
  636. "the list of all available accounts in the Google authentication "
  637. "cookies is required."
  638. data: "None."
  639. destination: GOOGLE_OWNED_SERVICE
  640. }
  641. policy {
  642. cookies_allowed: YES
  643. cookies_store: "user"
  644. setting:
  645. "This feature cannot be disabled in settings, but if the user "
  646. "signs out of Chrome, this request would not be made."
  647. chrome_policy {
  648. SigninAllowed {
  649. policy_options {mode: MANDATORY}
  650. SigninAllowed: false
  651. }
  652. }
  653. })");
  654. CreateAndStartGaiaFetcher(
  655. " ", // To force an HTTP POST.
  656. kFormEncodedContentType, "Origin: https://www.google.com",
  657. list_accounts_gurl_, network::mojom::CredentialsMode::kInclude,
  658. traffic_annotation);
  659. }
  660. void GaiaAuthFetcher::StartOAuthMultilogin(
  661. gaia::MultiloginMode mode,
  662. const std::vector<MultiloginTokenIDPair>& accounts,
  663. const std::string& external_cc_result) {
  664. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  665. UMA_HISTOGRAM_COUNTS_100("Signin.Multilogin.NumberOfAccounts",
  666. accounts.size());
  667. std::vector<std::string> authorization_header_parts;
  668. for (const MultiloginTokenIDPair& account : accounts) {
  669. authorization_header_parts.push_back(base::StringPrintf(
  670. "%s:%s", account.token_.c_str(), account.gaia_id_.c_str()));
  671. }
  672. std::string authorization_header = base::StringPrintf(
  673. kOAuthMultiBearerHeaderFormat,
  674. base::JoinString(authorization_header_parts, ",").c_str());
  675. std::string source_string = base::EscapeUrlEncodedData(source_, true);
  676. std::string parameters = base::StringPrintf(
  677. "?source=%s&reuseCookies=%i", source_string.c_str(),
  678. mode == gaia::MultiloginMode::MULTILOGIN_PRESERVE_COOKIE_ACCOUNTS_ORDER
  679. ? 1
  680. : 0);
  681. if (!external_cc_result.empty()) {
  682. base::StringAppendF(
  683. &parameters, "&externalCcResult=%s",
  684. base::EscapeUrlEncodedData(external_cc_result, true).c_str());
  685. }
  686. net::NetworkTrafficAnnotationTag traffic_annotation =
  687. net::DefineNetworkTrafficAnnotation("gaia_auth_multilogin", R"(
  688. semantics {
  689. sender: "Chrome - Google authentication API"
  690. description:
  691. "This request is used to set chrome accounts in browser in the "
  692. "Google authentication cookies for several google websites "
  693. "(e.g. youtube)."
  694. trigger:
  695. "This request is part of Gaia Auth API, and is triggered whenever "
  696. "accounts in cookies are not consistent with accounts in browser."
  697. data:
  698. "This request includes the vector of account ids and auth-login "
  699. "tokens."
  700. destination: GOOGLE_OWNED_SERVICE
  701. }
  702. policy {
  703. cookies_allowed: YES
  704. cookies_store: "user"
  705. setting:
  706. "This feature cannot be disabled in settings, but if the user "
  707. "signs out of Chrome, this request would not be made."
  708. chrome_policy {
  709. SigninAllowed {
  710. SigninAllowed: false
  711. }
  712. }
  713. })");
  714. CreateAndStartGaiaFetcher(" ", // Non-empty to force a POST
  715. kFormEncodedContentType, authorization_header,
  716. oauth_multilogin_gurl_.Resolve(parameters),
  717. network::mojom::CredentialsMode::kInclude,
  718. traffic_annotation);
  719. }
  720. void GaiaAuthFetcher::StartLogOut() {
  721. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  722. net::NetworkTrafficAnnotationTag traffic_annotation =
  723. net::DefineNetworkTrafficAnnotation("gaia_auth_log_out", R"(
  724. semantics {
  725. sender: "Chrome - Google authentication API"
  726. description:
  727. "This request is part of the Chrome - Google authentication API "
  728. "and allows its callers to sign out all Google accounts from the "
  729. "content area."
  730. trigger:
  731. "This request is part of Gaia Auth API, and is triggered whenever "
  732. "signing out of all Google accounts is required."
  733. data: "None."
  734. destination: GOOGLE_OWNED_SERVICE
  735. }
  736. policy {
  737. cookies_allowed: YES
  738. cookies_store: "user"
  739. setting:
  740. "This feature cannot be disabled in settings, but if the user "
  741. "signs out of Chrome, this request would not be made."
  742. chrome_policy {
  743. SigninAllowed {
  744. policy_options {mode: MANDATORY}
  745. SigninAllowed: false
  746. }
  747. }
  748. })");
  749. CreateAndStartGaiaFetcher(
  750. std::string(), std::string(), std::string(), logout_gurl_,
  751. network::mojom::CredentialsMode::kInclude, traffic_annotation);
  752. }
  753. void GaiaAuthFetcher::StartCreateReAuthProofTokenForParent(
  754. const std::string& child_oauth_access_token,
  755. const std::string& parent_obfuscated_gaia_id,
  756. const std::string& parent_credential) {
  757. // Create the post body.
  758. base::DictionaryValue post_body_value;
  759. post_body_value.SetString("credentialType", "password");
  760. post_body_value.SetString("credential", parent_credential);
  761. std::string post_body;
  762. bool write_success = base::JSONWriter::Write(post_body_value, &post_body);
  763. DCHECK(write_success);
  764. // Create the Authorization header.
  765. std::string auth_header = "Bearer " + child_oauth_access_token;
  766. std::string headers = "Authorization: " + auth_header + "\r\n" +
  767. "Content-Type: " + kJsonContentType;
  768. // Create the traffic annotation.
  769. net::NetworkTrafficAnnotationTag traffic_annotation(
  770. net::DefineNetworkTrafficAnnotation(
  771. "gaia_create_reauth_proof_token_for_parent", R"(
  772. semantics {
  773. sender: "Chrome - Google authentication API"
  774. description:
  775. "This request exchanges a set of credentials for a parent-type "
  776. "Google Family Link user for a ReAuth Proof Token (RAPT), "
  777. "the successful receipt of which re-authorizes (but explicitly "
  778. "does not authenticate) the parent user."
  779. trigger:
  780. "This request is triggered when a Chrome service needs to "
  781. "re-authorize a parent-type Google Family Link user given the "
  782. "parent's login credential."
  783. data:
  784. "The obfuscated GAIA id of the parent, the Google OAuth access "
  785. "token of the child account, and the credential to be used to "
  786. "reauthorize the user."
  787. destination: GOOGLE_OWNED_SERVICE
  788. }
  789. policy {
  790. cookies_allowed: NO
  791. setting:
  792. "This feature cannot be disabled in settings, but if the user "
  793. "signs out of Chrome, this request would not be made."
  794. chrome_policy {
  795. SigninAllowed {
  796. policy_options {mode: MANDATORY}
  797. SigninAllowed: false
  798. }
  799. }
  800. })"));
  801. // Create the ReAuth URL.
  802. GURL reauth_url = GaiaUrls::GetInstance()->reauth_api_url().Resolve(
  803. parent_obfuscated_gaia_id + "/reauthProofTokens?delegationType=unicorn");
  804. DCHECK(reauth_url.is_valid());
  805. // Start the request.
  806. CreateAndStartGaiaFetcher(post_body, kJsonContentType, headers, reauth_url,
  807. network::mojom::CredentialsMode::kOmit,
  808. traffic_annotation);
  809. }
  810. void GaiaAuthFetcher::StartGetCheckConnectionInfo() {
  811. DCHECK(!fetch_pending_) << "Tried to fetch two things at once!";
  812. net::NetworkTrafficAnnotationTag traffic_annotation =
  813. net::DefineNetworkTrafficAnnotation("gaia_auth_check_connection_info", R"(
  814. semantics {
  815. sender: "Chrome - Google authentication API"
  816. description:
  817. "This request is used to fetch from the Google authentication "
  818. "server the the list of URLs to check its connection info."
  819. trigger:
  820. "This request is part of Gaia Auth API, and is triggered once "
  821. "after a Google account is added to the browser."
  822. data: "None."
  823. destination: GOOGLE_OWNED_SERVICE
  824. }
  825. policy {
  826. cookies_allowed: NO
  827. setting:
  828. "This feature cannot be disabled in settings, but if the user "
  829. "signs out of Chrome, this request would not be made."
  830. chrome_policy {
  831. SigninAllowed {
  832. policy_options {mode: MANDATORY}
  833. SigninAllowed: false
  834. }
  835. }
  836. })");
  837. CreateAndStartGaiaFetcher(std::string(), std::string(), std::string(),
  838. get_check_connection_info_url_,
  839. network::mojom::CredentialsMode::kOmit,
  840. traffic_annotation);
  841. }
  842. // static
  843. GoogleServiceAuthError GaiaAuthFetcher::GenerateAuthError(
  844. const std::string& data,
  845. net::Error net_error) {
  846. VLOG(1) << "Got authentication error";
  847. VLOG(1) << "net_error: " << net::ErrorToString(net_error);
  848. VLOG(1) << "response body: " << data;
  849. if (net_error != net::OK) {
  850. if (net_error == net::ERR_ABORTED) {
  851. return GoogleServiceAuthError(GoogleServiceAuthError::REQUEST_CANCELED);
  852. }
  853. DVLOG(1) << "Could not reach Google Accounts servers: errno " << net_error;
  854. return GoogleServiceAuthError::FromConnectionError(net_error);
  855. }
  856. std::string error;
  857. std::string url;
  858. ParseClientLoginFailure(data, &error, &url);
  859. DLOG(WARNING) << "ClientLogin failed with " << error;
  860. if (error == kBadAuthenticationShortError ||
  861. error == kBadAuthenticationError) {
  862. return GoogleServiceAuthError(
  863. GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
  864. GoogleServiceAuthError::InvalidGaiaCredentialsReason::
  865. CREDENTIALS_REJECTED_BY_SERVER));
  866. }
  867. if (error == kServiceUnavailableShortError ||
  868. error == kServiceUnavailableError) {
  869. return GoogleServiceAuthError(
  870. GoogleServiceAuthError::SERVICE_UNAVAILABLE);
  871. }
  872. DLOG(WARNING) << "Incomprehensible response from Google Accounts servers.";
  873. return GoogleServiceAuthError(GoogleServiceAuthError::SERVICE_UNAVAILABLE);
  874. }
  875. void GaiaAuthFetcher::OnOAuth2TokenPairFetched(const std::string& data,
  876. net::Error net_error,
  877. int response_code) {
  878. std::unique_ptr<const GaiaAuthConsumer::ClientOAuthResult> result;
  879. if (net_error == net::OK && response_code == net::HTTP_OK) {
  880. result = ExtractOAuth2TokenPairResponse(data);
  881. }
  882. if (result) {
  883. consumer_->OnClientOAuthSuccess(*result);
  884. } else {
  885. consumer_->OnClientOAuthFailure(GenerateAuthError(data, net_error));
  886. }
  887. }
  888. void GaiaAuthFetcher::OnOAuth2RevokeTokenFetched(const std::string& data,
  889. net::Error net_error,
  890. int response_code) {
  891. GaiaAuthConsumer::TokenRevocationStatus revocation_status =
  892. GaiaAuthConsumer::TokenRevocationStatus::kUnknownError;
  893. switch (net_error) {
  894. case net::OK:
  895. revocation_status =
  896. GetTokenRevocationStatusFromResponseData(data, response_code);
  897. break;
  898. case net::ERR_IO_PENDING:
  899. NOTREACHED();
  900. break;
  901. case net::ERR_ABORTED:
  902. revocation_status =
  903. GaiaAuthConsumer::TokenRevocationStatus::kConnectionCanceled;
  904. break;
  905. default:
  906. revocation_status =
  907. (net_error == net::ERR_TIMED_OUT)
  908. ? GaiaAuthConsumer::TokenRevocationStatus::kConnectionTimeout
  909. : GaiaAuthConsumer::TokenRevocationStatus::kConnectionFailed;
  910. break;
  911. }
  912. consumer_->OnOAuth2RevokeTokenCompleted(revocation_status);
  913. }
  914. void GaiaAuthFetcher::OnListAccountsFetched(const std::string& data,
  915. net::Error net_error,
  916. int response_code) {
  917. base::UmaHistogramSparse("Gaia.AuthFetcher.ListAccounts.NetErrorCodes",
  918. -net_error);
  919. if (net_error == net::OK && response_code == net::HTTP_OK)
  920. consumer_->OnListAccountsSuccess(data);
  921. else
  922. consumer_->OnListAccountsFailure(GenerateAuthError(data, net_error));
  923. }
  924. void GaiaAuthFetcher::OnLogOutFetched(const std::string& data,
  925. net::Error net_error,
  926. int response_code) {
  927. if (net_error == net::OK && response_code == net::HTTP_OK) {
  928. consumer_->OnLogOutSuccess();
  929. } else {
  930. consumer_->OnLogOutFailure(GenerateAuthError(data, net_error));
  931. }
  932. }
  933. void GaiaAuthFetcher::OnReAuthApiInfoFetched(const std::string& data,
  934. net::Error net_error,
  935. int response_code) {
  936. if (net_error == net::OK) {
  937. std::unique_ptr<base::DictionaryValue> response_dict = ParseJSONDict(data);
  938. if (response_code == net::HTTP_OK) {
  939. std::string rapt_token;
  940. response_dict->GetString("encodedRapt", &rapt_token);
  941. if (rapt_token.empty()) {
  942. // This should not happen unless there is a bug on the server,
  943. // since if we get HTTP_OK response, we should get a RAPT token.
  944. DLOG(ERROR) << "Got HTTP-OK ReauthAPI response with empty RAPT token";
  945. consumer_->OnReAuthProofTokenFailure(
  946. GaiaAuthConsumer::ReAuthProofTokenStatus::kUnknownError);
  947. return;
  948. }
  949. consumer_->OnReAuthProofTokenSuccess(rapt_token);
  950. } else {
  951. const std::string error_message =
  952. response_dict->FindPath({"error", "message"})->GetString();
  953. consumer_->OnReAuthProofTokenFailure(
  954. ErrorMessageToReAuthProofTokenStatus(error_message));
  955. }
  956. } else {
  957. consumer_->OnReAuthProofTokenFailure(
  958. GaiaAuthConsumer::ReAuthProofTokenStatus::kNetworkError);
  959. }
  960. }
  961. void GaiaAuthFetcher::OnMergeSessionFetched(const std::string& data,
  962. net::Error net_error,
  963. int response_code) {
  964. if (net_error == net::OK && response_code == net::HTTP_OK) {
  965. consumer_->OnMergeSessionSuccess(data);
  966. } else {
  967. consumer_->OnMergeSessionFailure(GenerateAuthError(data, net_error));
  968. }
  969. }
  970. void GaiaAuthFetcher::OnUberAuthTokenFetch(const std::string& data,
  971. net::Error net_error,
  972. int response_code) {
  973. if (net_error == net::OK && response_code == net::HTTP_OK) {
  974. consumer_->OnUberAuthTokenSuccess(data);
  975. } else {
  976. consumer_->OnUberAuthTokenFailure(GenerateAuthError(data, net_error));
  977. }
  978. }
  979. void GaiaAuthFetcher::OnOAuthLoginFetched(const std::string& data,
  980. net::Error net_error,
  981. int response_code) {
  982. if (net_error == net::OK && response_code == net::HTTP_OK) {
  983. VLOG(1) << "ClientLogin successful!";
  984. std::string sid;
  985. std::string lsid;
  986. std::string token;
  987. ParseClientLoginResponse(data, &sid, &lsid, &token);
  988. consumer_->OnClientLoginSuccess(
  989. GaiaAuthConsumer::ClientLoginResult(sid, lsid, token, data));
  990. } else {
  991. consumer_->OnClientLoginFailure(GenerateAuthError(data, net_error));
  992. }
  993. }
  994. void GaiaAuthFetcher::OnGetCheckConnectionInfoFetched(const std::string& data,
  995. net::Error net_error,
  996. int response_code) {
  997. if (net_error == net::Error::OK && response_code == net::HTTP_OK) {
  998. consumer_->OnGetCheckConnectionInfoSuccess(data);
  999. } else {
  1000. consumer_->OnGetCheckConnectionInfoError(
  1001. GenerateAuthError(data, net_error));
  1002. }
  1003. }
  1004. void GaiaAuthFetcher::OnOAuthMultiloginFetched(const std::string& data,
  1005. net::Error net_error,
  1006. int response_code) {
  1007. OAuthMultiloginResult result =
  1008. (net_error == net::Error::OK)
  1009. ? OAuthMultiloginResult(data)
  1010. : OAuthMultiloginResult(OAuthMultiloginResponseStatus::kRetry);
  1011. consumer_->OnOAuthMultiloginFinished(result);
  1012. }
  1013. void GaiaAuthFetcher::OnURLLoadComplete(
  1014. std::unique_ptr<std::string> response_body) {
  1015. net::Error net_error = static_cast<net::Error>(url_loader_->NetError());
  1016. std::string data = response_body ? std::move(*response_body) : "";
  1017. int response_code = 0;
  1018. if (url_loader_->ResponseInfo()) {
  1019. if (url_loader_->ResponseInfo()->headers)
  1020. response_code = url_loader_->ResponseInfo()->headers->response_code();
  1021. }
  1022. OnURLLoadCompleteInternal(net_error, response_code, data);
  1023. }
  1024. void GaiaAuthFetcher::OnURLLoadCompleteInternal(
  1025. net::Error net_error,
  1026. int response_code,
  1027. std::string data) {
  1028. fetch_pending_ = false;
  1029. // Some of the GAIA requests perform redirects, which results in the final URL
  1030. // of the fetcher not being the original URL requested. Therefore use the
  1031. // original URL when determining which OnXXX function to call.
  1032. GURL url = original_url_;
  1033. original_url_ = GURL();
  1034. DispatchFetchedRequest(url, data, net_error, response_code);
  1035. }
  1036. void GaiaAuthFetcher::DispatchFetchedRequest(
  1037. const GURL& url,
  1038. const std::string& data,
  1039. net::Error net_error,
  1040. int response_code) {
  1041. if (url == oauth2_token_gurl_) {
  1042. OnOAuth2TokenPairFetched(data, net_error, response_code);
  1043. } else if (base::StartsWith(url.spec(), merge_session_gurl_.spec(),
  1044. base::CompareCase::SENSITIVE)) {
  1045. OnMergeSessionFetched(data, net_error, response_code);
  1046. } else if (url == uberauth_token_gurl_) {
  1047. OnUberAuthTokenFetch(data, net_error, response_code);
  1048. } else if (url == oauth_login_gurl_) {
  1049. OnOAuthLoginFetched(data, net_error, response_code);
  1050. } else if (IsMultiloginUrl(url)) {
  1051. OnOAuthMultiloginFetched(data, net_error, response_code);
  1052. } else if (url == oauth2_revoke_gurl_) {
  1053. OnOAuth2RevokeTokenFetched(data, net_error, response_code);
  1054. } else if (url == list_accounts_gurl_) {
  1055. OnListAccountsFetched(data, net_error, response_code);
  1056. } else if (url == logout_gurl_) {
  1057. OnLogOutFetched(data, net_error, response_code);
  1058. } else if (url == get_check_connection_info_url_) {
  1059. OnGetCheckConnectionInfoFetched(data, net_error, response_code);
  1060. } else if (IsReAuthApiUrl(url)) {
  1061. OnReAuthApiInfoFetched(data, net_error, response_code);
  1062. } else {
  1063. NOTREACHED() << "Unknown url: '" << url << "'";
  1064. }
  1065. }