signin_url_loader_throttle.cc 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198
  1. // Copyright 2020 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 "weblayer/browser/signin_url_loader_throttle.h"
  5. #include "components/content_settings/core/browser/cookie_settings.h"
  6. #include "components/signin/core/browser/signin_header_helper.h"
  7. #include "components/signin/public/base/account_consistency_method.h"
  8. #include "components/signin/public/identity_manager/tribool.h"
  9. #include "content/public/browser/browser_context.h"
  10. #include "content/public/browser/browser_task_traits.h"
  11. #include "content/public/browser/browser_thread.h"
  12. #include "google_apis/gaia/gaia_auth_util.h"
  13. #include "net/base/url_util.h"
  14. #include "services/network/public/cpp/resource_request.h"
  15. #include "services/network/public/mojom/url_response_head.mojom.h"
  16. #include "third_party/blink/public/mojom/loader/resource_load_info.mojom.h"
  17. #include "weblayer/browser/cookie_settings_factory.h"
  18. #include "weblayer/browser/tab_impl.h"
  19. #include "weblayer/public/google_accounts_delegate.h"
  20. namespace weblayer {
  21. const char kSignOutPath[] = "/SignOutOptions";
  22. namespace {
  23. constexpr char kWebLayerMirrorHeaderSource[] = "WebLayer";
  24. GoogleAccountsDelegate* GetDelegate(
  25. const content::WebContents::Getter& web_contents_getter) {
  26. auto* web_contents = web_contents_getter.Run();
  27. if (!web_contents)
  28. return nullptr;
  29. auto* tab = TabImpl::FromWebContents(web_contents);
  30. if (!tab)
  31. return nullptr;
  32. return tab->google_accounts_delegate();
  33. }
  34. void ProcessMirrorHeader(content::WebContents::Getter web_contents_getter,
  35. const signin::ManageAccountsParams& params) {
  36. auto* delegate = GetDelegate(web_contents_getter);
  37. if (delegate)
  38. delegate->OnGoogleAccountsRequest(params);
  39. }
  40. void MaybeAddQueryParams(GURL* url) {
  41. // Add manage=true to query parameters for sign out URLs to make sure we
  42. // receive the Mirror response headers instead of the normal sign out page.
  43. if (gaia::HasGaiaSchemeHostPort(*url) && url->path_piece() == kSignOutPath) {
  44. *url = net::AppendOrReplaceQueryParameter(*url, "manage", "true");
  45. }
  46. }
  47. } // namespace
  48. SigninURLLoaderThrottle::~SigninURLLoaderThrottle() = default;
  49. // static
  50. std::unique_ptr<SigninURLLoaderThrottle> SigninURLLoaderThrottle::Create(
  51. content::BrowserContext* browser_context,
  52. content::WebContents::Getter web_contents_getter) {
  53. if (!GetDelegate(web_contents_getter))
  54. return nullptr;
  55. // Use base::WrapUnique + new because of the constructor is private.
  56. return base::WrapUnique(new SigninURLLoaderThrottle(
  57. browser_context, std::move(web_contents_getter)));
  58. }
  59. void SigninURLLoaderThrottle::WillStartRequest(
  60. network::ResourceRequest* request,
  61. bool* defer) {
  62. GoogleAccountsDelegate* delegate = GetDelegate(web_contents_getter_);
  63. if (!delegate)
  64. return;
  65. MaybeAddQueryParams(&request->url);
  66. request_url_ = request->url;
  67. is_main_frame_ =
  68. static_cast<blink::mojom::ResourceType>(request->resource_type) ==
  69. blink::mojom::ResourceType::kMainFrame;
  70. net::HttpRequestHeaders modified_request_headers;
  71. std::vector<std::string> to_be_removed_request_headers;
  72. ProcessRequest(GURL(), &request->headers, &to_be_removed_request_headers,
  73. &modified_request_headers);
  74. signin::RequestAdapter adapter(request_url_, request->headers,
  75. &modified_request_headers,
  76. &to_be_removed_request_headers);
  77. request_headers_.CopyFrom(request->headers);
  78. }
  79. void SigninURLLoaderThrottle::WillRedirectRequest(
  80. net::RedirectInfo* redirect_info,
  81. const network::mojom::URLResponseHead& response_head,
  82. bool* defer,
  83. std::vector<std::string>* headers_to_remove,
  84. net::HttpRequestHeaders* modified_headers,
  85. net::HttpRequestHeaders* modified_cors_exempt_request_headers) {
  86. if (!GetDelegate(web_contents_getter_))
  87. return;
  88. MaybeAddQueryParams(&redirect_info->new_url);
  89. ProcessRequest(redirect_info->new_url, &request_headers_, headers_to_remove,
  90. modified_headers);
  91. ProcessResponse(response_head.headers.get());
  92. request_url_ = redirect_info->new_url;
  93. }
  94. void SigninURLLoaderThrottle::WillProcessResponse(
  95. const GURL& response_url,
  96. network::mojom::URLResponseHead* response_head,
  97. bool* defer) {
  98. if (!GetDelegate(web_contents_getter_))
  99. return;
  100. ProcessResponse(response_head->headers.get());
  101. }
  102. SigninURLLoaderThrottle::SigninURLLoaderThrottle(
  103. content::BrowserContext* browser_context,
  104. content::WebContents::Getter web_contents_getter)
  105. : browser_context_(browser_context),
  106. web_contents_getter_(std::move(web_contents_getter)) {}
  107. void SigninURLLoaderThrottle::ProcessRequest(
  108. const GURL& new_url,
  109. net::HttpRequestHeaders* original_headers,
  110. std::vector<std::string>* headers_to_remove,
  111. net::HttpRequestHeaders* modified_headers) {
  112. GoogleAccountsDelegate* delegate = GetDelegate(web_contents_getter_);
  113. if (!delegate)
  114. return;
  115. signin::RequestAdapter request_adapter(request_url_, *original_headers,
  116. modified_headers, headers_to_remove);
  117. // Disable incognito and adding accounts for now. This shouldn't matter in
  118. // practice though since we are skipping the /SignOutOptions page completely
  119. // with the manage=true param.
  120. //
  121. // TODO(crbug.com/1134042): Check whether the child account status should also
  122. // be sent in the Mirror request header from WebLayer.
  123. signin::AppendOrRemoveMirrorRequestHeader(
  124. &request_adapter, new_url, delegate->GetGaiaId(),
  125. /*is_child_account=*/signin::Tribool::kUnknown,
  126. signin::AccountConsistencyMethod::kMirror,
  127. CookieSettingsFactory::GetForBrowserContext(browser_context_).get(),
  128. signin::PROFILE_MODE_INCOGNITO_DISABLED |
  129. signin::PROFILE_MODE_ADD_ACCOUNT_DISABLED,
  130. kWebLayerMirrorHeaderSource, /*force_account_consistency=*/true);
  131. original_headers->MergeFrom(*modified_headers);
  132. for (const std::string& name : *headers_to_remove)
  133. original_headers->RemoveHeader(name);
  134. }
  135. void SigninURLLoaderThrottle::ProcessResponse(
  136. const net::HttpResponseHeaders* headers) {
  137. if (!gaia::HasGaiaSchemeHostPort(request_url_) || !is_main_frame_ ||
  138. !headers) {
  139. return;
  140. }
  141. std::string header_value;
  142. if (!headers->GetNormalizedHeader(signin::kChromeManageAccountsHeader,
  143. &header_value)) {
  144. return;
  145. }
  146. signin::ManageAccountsParams params =
  147. signin::BuildManageAccountsParams(header_value);
  148. if (params.service_type == signin::GAIA_SERVICE_TYPE_NONE)
  149. return;
  150. // Only process one mirror header per request (multiple headers on the same
  151. // redirect chain are ignored).
  152. if (response_header_processed_) {
  153. LOG(ERROR) << "Multiple X-Chrome-Manage-Accounts headers on a redirect "
  154. << "chain, ignoring";
  155. return;
  156. }
  157. response_header_processed_ = true;
  158. // Post a task even if we are already on the UI thread to avoid making any
  159. // requests while processing a throttle event.
  160. content::GetUIThreadTaskRunner({})->PostTask(
  161. FROM_HERE,
  162. base::BindOnce(&ProcessMirrorHeader, web_contents_getter_, params));
  163. }
  164. } // namespace weblayer