oauth_multilogin_result.cc 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180
  1. // Copyright 2017 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/oauth_multilogin_result.h"
  5. #include <algorithm>
  6. #include "base/compiler_specific.h"
  7. #include "base/json/json_reader.h"
  8. #include "base/logging.h"
  9. #include "base/metrics/histogram_macros.h"
  10. #include "base/strings/string_number_conversions.h"
  11. #include "base/strings/string_piece.h"
  12. #include "third_party/abseil-cpp/absl/types/optional.h"
  13. namespace {
  14. void RecordMultiloginResponseStatus(OAuthMultiloginResponseStatus status) {
  15. UMA_HISTOGRAM_ENUMERATION("Signin.OAuthMultiloginResponseStatus", status);
  16. }
  17. } // namespace
  18. OAuthMultiloginResponseStatus ParseOAuthMultiloginResponseStatus(
  19. const std::string& status) {
  20. if (status == "OK")
  21. return OAuthMultiloginResponseStatus::kOk;
  22. if (status == "RETRY")
  23. return OAuthMultiloginResponseStatus::kRetry;
  24. if (status == "INVALID_TOKENS")
  25. return OAuthMultiloginResponseStatus::kInvalidTokens;
  26. if (status == "INVALID_INPUT")
  27. return OAuthMultiloginResponseStatus::kInvalidInput;
  28. if (status == "ERROR")
  29. return OAuthMultiloginResponseStatus::kError;
  30. return OAuthMultiloginResponseStatus::kUnknownStatus;
  31. }
  32. OAuthMultiloginResult::OAuthMultiloginResult(
  33. const OAuthMultiloginResult& other) {
  34. status_ = other.status();
  35. cookies_ = other.cookies();
  36. failed_gaia_ids_ = other.failed_gaia_ids();
  37. }
  38. OAuthMultiloginResult& OAuthMultiloginResult::operator=(
  39. const OAuthMultiloginResult& other) {
  40. status_ = other.status();
  41. cookies_ = other.cookies();
  42. failed_gaia_ids_ = other.failed_gaia_ids();
  43. return *this;
  44. }
  45. OAuthMultiloginResult::OAuthMultiloginResult(
  46. OAuthMultiloginResponseStatus status)
  47. : status_(status) {}
  48. // static
  49. base::StringPiece OAuthMultiloginResult::StripXSSICharacters(
  50. const std::string& raw_data) {
  51. base::StringPiece body(raw_data);
  52. return body.substr(std::min(body.find('\n'), body.size()));
  53. }
  54. void OAuthMultiloginResult::TryParseFailedAccountsFromValue(
  55. base::Value* json_value) {
  56. DCHECK(json_value);
  57. base::Value* failed_accounts = json_value->FindListKey("failed_accounts");
  58. if (failed_accounts == nullptr) {
  59. VLOG(1) << "No invalid accounts found in the response but error is set to "
  60. "INVALID_TOKENS";
  61. status_ = OAuthMultiloginResponseStatus::kUnknownStatus;
  62. return;
  63. }
  64. for (auto& account : failed_accounts->GetListDeprecated()) {
  65. const std::string* gaia_id = account.FindStringKey("obfuscated_id");
  66. const std::string* status = account.FindStringKey("status");
  67. if (status && gaia_id && *status != "OK")
  68. failed_gaia_ids_.push_back(*gaia_id);
  69. }
  70. if (failed_gaia_ids_.empty())
  71. status_ = OAuthMultiloginResponseStatus::kUnknownStatus;
  72. }
  73. void OAuthMultiloginResult::TryParseCookiesFromValue(base::Value* json_value) {
  74. DCHECK(json_value);
  75. base::Value* cookie_list = json_value->FindListKey("cookies");
  76. if (cookie_list == nullptr) {
  77. VLOG(1) << "No cookies found in the response.";
  78. status_ = OAuthMultiloginResponseStatus::kUnknownStatus;
  79. return;
  80. }
  81. for (const auto& cookie : cookie_list->GetListDeprecated()) {
  82. const std::string* name = cookie.FindStringKey("name");
  83. const std::string* value = cookie.FindStringKey("value");
  84. const std::string* domain = cookie.FindStringKey("domain");
  85. const std::string* host = cookie.FindStringKey("host");
  86. const std::string* path = cookie.FindStringKey("path");
  87. absl::optional<bool> is_secure = cookie.FindBoolKey("isSecure");
  88. absl::optional<bool> is_http_only = cookie.FindBoolKey("isHttpOnly");
  89. const std::string* priority = cookie.FindStringKey("priority");
  90. absl::optional<double> expiration_delta = cookie.FindDoubleKey("maxAge");
  91. const std::string* same_site = cookie.FindStringKey("sameSite");
  92. const std::string* same_party = cookie.FindStringKey("sameParty");
  93. base::Time now = base::Time::Now();
  94. // TODO(crbug.com/1264458) If CreateSanitizedCookie were used below, this
  95. // wouldn't be needed and ValidateAndAdjustExpiryDate could be moved back
  96. // into anon namespace instead of being exposed as a static function.
  97. // Alternatly, if we were sure GAIA cookies wouldn't try to expire more
  98. // than 400 days in the future we wouldn't need this either.
  99. base::Time expiration = net::CanonicalCookie::ValidateAndAdjustExpiryDate(
  100. now + base::Seconds(expiration_delta.value_or(0.0)), now);
  101. std::string cookie_domain = domain ? *domain : "";
  102. std::string cookie_host = host ? *host : "";
  103. if (cookie_domain.empty() && !cookie_host.empty() &&
  104. cookie_host[0] != '.') {
  105. // Host cookie case. If domain is empty but other conditions are not met,
  106. // there must be something wrong with the received cookie.
  107. cookie_domain = cookie_host;
  108. }
  109. net::CookieSameSite samesite_mode = net::CookieSameSite::UNSPECIFIED;
  110. net::CookieSameSiteString samesite_string =
  111. net::CookieSameSiteString::kUnspecified;
  112. if (same_site) {
  113. samesite_mode = net::StringToCookieSameSite(*same_site, &samesite_string);
  114. }
  115. net::RecordCookieSameSiteAttributeValueHistogram(samesite_string);
  116. bool same_party_bool = same_party && (*same_party == "1");
  117. // TODO(crbug.com/1155648) Consider using CreateSanitizedCookie instead.
  118. std::unique_ptr<net::CanonicalCookie> new_cookie =
  119. net::CanonicalCookie::FromStorage(
  120. name ? *name : "", value ? *value : "", cookie_domain,
  121. path ? *path : "", /*creation=*/now, expiration,
  122. /*last_access=*/now, /*last_update=*/now, is_secure.value_or(true),
  123. is_http_only.value_or(true), samesite_mode,
  124. net::StringToCookiePriority(priority ? *priority : "medium"),
  125. same_party_bool, /*partition_key=*/absl::nullopt,
  126. net::CookieSourceScheme::kUnset, url::PORT_UNSPECIFIED);
  127. // If the unique_ptr is null, it means the cookie was not canonical.
  128. // FromStorage() also uses a less strict version of IsCanonical(), we need
  129. // to check the stricter version as well here.
  130. if (new_cookie && new_cookie->IsCanonical()) {
  131. cookies_.push_back(std::move(*new_cookie));
  132. } else {
  133. LOG(ERROR) << "Non-canonical cookie found.";
  134. }
  135. }
  136. }
  137. OAuthMultiloginResult::OAuthMultiloginResult(const std::string& raw_data) {
  138. base::StringPiece data = StripXSSICharacters(raw_data);
  139. status_ = OAuthMultiloginResponseStatus::kUnknownStatus;
  140. absl::optional<base::Value> json_data = base::JSONReader::Read(data);
  141. if (!json_data) {
  142. RecordMultiloginResponseStatus(status_);
  143. return;
  144. }
  145. const std::string* status_string = json_data->FindStringKey("status");
  146. if (!status_string) {
  147. RecordMultiloginResponseStatus(status_);
  148. return;
  149. }
  150. status_ = ParseOAuthMultiloginResponseStatus(*status_string);
  151. if (status_ == OAuthMultiloginResponseStatus::kOk) {
  152. // Sets status_ to kUnknownStatus if cookies cannot be parsed.
  153. TryParseCookiesFromValue(&json_data.value());
  154. } else if (status_ == OAuthMultiloginResponseStatus::kInvalidTokens) {
  155. // Sets status_ to kUnknownStatus if failed accounts cannot be parsed.
  156. TryParseFailedAccountsFromValue(&json_data.value());
  157. }
  158. RecordMultiloginResponseStatus(status_);
  159. }
  160. OAuthMultiloginResult::~OAuthMultiloginResult() = default;