1 // Copyright 2013 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 "chrome/browser/ui/webui/signin/inline_login_handler.h"
6
7 #include <limits.h>
8 #include <string>
9 #include <utility>
10 #include <vector>
11
12 #include "base/bind.h"
13 #include "base/metrics/user_metrics.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/utf_string_conversions.h"
16 #include "base/values.h"
17 #include "build/build_config.h"
18 #include "chrome/browser/browser_process.h"
19 #include "chrome/browser/profiles/profile.h"
20 #include "chrome/browser/signin/signin_promo.h"
21 #include "chrome/browser/ui/browser_finder.h"
22 #include "chrome/browser/ui/browser_navigator.h"
23 #include "chrome/browser/ui/browser_navigator_params.h"
24 #include "chrome/browser/ui/user_manager.h"
25 #include "chrome/browser/ui/webui/signin/signin_utils.h"
26 #include "chrome/common/chrome_features.h"
27 #include "chrome/common/pref_names.h"
28 #include "components/metrics/metrics_pref_names.h"
29 #include "components/prefs/pref_service.h"
30 #include "components/signin/public/base/signin_pref_names.h"
31 #include "content/public/browser/storage_partition.h"
32 #include "content/public/browser/web_contents.h"
33 #include "content/public/browser/web_ui.h"
34 #include "google_apis/gaia/gaia_urls.h"
35 #include "net/base/url_util.h"
36 #include "services/network/public/mojom/cookie_manager.mojom.h"
37
38 const char kSignInPromoQueryKeyShowAccountManagement[] =
39 "showAccountManagement";
40
InlineLoginHandler()41 InlineLoginHandler::InlineLoginHandler() {}
42
~InlineLoginHandler()43 InlineLoginHandler::~InlineLoginHandler() {}
44
RegisterMessages()45 void InlineLoginHandler::RegisterMessages() {
46 web_ui()->RegisterMessageCallback(
47 "initialize",
48 base::BindRepeating(&InlineLoginHandler::HandleInitializeMessage,
49 base::Unretained(this)));
50 web_ui()->RegisterMessageCallback(
51 "authExtensionReady",
52 base::BindRepeating(&InlineLoginHandler::HandleAuthExtensionReadyMessage,
53 base::Unretained(this)));
54 web_ui()->RegisterMessageCallback(
55 "completeLogin",
56 base::BindRepeating(&InlineLoginHandler::HandleCompleteLoginMessage,
57 base::Unretained(this)));
58 web_ui()->RegisterMessageCallback(
59 "switchToFullTab",
60 base::BindRepeating(&InlineLoginHandler::HandleSwitchToFullTabMessage,
61 base::Unretained(this)));
62 web_ui()->RegisterMessageCallback(
63 "navigationButtonClicked",
64 base::BindRepeating(&InlineLoginHandler::HandleNavigationButtonClicked,
65 base::Unretained(this)));
66 web_ui()->RegisterMessageCallback(
67 "dialogClose", base::BindRepeating(&InlineLoginHandler::HandleDialogClose,
68 base::Unretained(this)));
69 }
70
HandleInitializeMessage(const base::ListValue * args)71 void InlineLoginHandler::HandleInitializeMessage(const base::ListValue* args) {
72 AllowJavascript();
73 content::WebContents* contents = web_ui()->GetWebContents();
74 content::StoragePartition* partition =
75 signin::GetSigninPartition(contents->GetBrowserContext());
76 if (partition) {
77 const GURL& current_url = web_ui()->GetWebContents()->GetURL();
78
79 // If the kSignInPromoQueryKeyForceKeepData param is missing, or if it is
80 // present and its value is zero, this means we don't want to keep the
81 // the data.
82 std::string value;
83 if (!net::GetValueForKeyInQuery(current_url,
84 signin::kSignInPromoQueryKeyForceKeepData,
85 &value) ||
86 value == "0") {
87 partition->ClearData(
88 content::StoragePartition::REMOVE_DATA_MASK_ALL,
89 content::StoragePartition::QUOTA_MANAGED_STORAGE_MASK_ALL,
90 GURL(),
91 base::Time(),
92 base::Time::Max(),
93 base::Bind(&InlineLoginHandler::ContinueHandleInitializeMessage,
94 weak_ptr_factory_.GetWeakPtr()));
95 } else {
96 ContinueHandleInitializeMessage();
97 }
98 }
99 }
100
ContinueHandleInitializeMessage()101 void InlineLoginHandler::ContinueHandleInitializeMessage() {
102 base::DictionaryValue params;
103
104 const std::string& app_locale = g_browser_process->GetApplicationLocale();
105 params.SetString("hl", app_locale);
106 GaiaUrls* gaiaUrls = GaiaUrls::GetInstance();
107 params.SetString("gaiaUrl", gaiaUrls->gaia_url().spec());
108 params.SetInteger("authMode", InlineLoginHandler::kDesktopAuthMode);
109
110 const GURL& current_url = web_ui()->GetWebContents()->GetURL();
111 signin_metrics::AccessPoint access_point =
112 signin::GetAccessPointForEmbeddedPromoURL(current_url);
113 signin_metrics::Reason reason =
114 signin::GetSigninReasonForEmbeddedPromoURL(current_url);
115
116 if (reason != signin_metrics::Reason::REASON_REAUTHENTICATION &&
117 reason != signin_metrics::Reason::REASON_UNLOCK &&
118 reason != signin_metrics::Reason::REASON_ADD_SECONDARY_ACCOUNT) {
119 signin_metrics::LogSigninAccessPointStarted(
120 access_point,
121 signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
122 signin_metrics::RecordSigninUserActionForAccessPoint(
123 access_point,
124 signin_metrics::PromoAction::PROMO_ACTION_NO_SIGNIN_PROMO);
125 base::RecordAction(base::UserMetricsAction("Signin_SigninPage_Loading"));
126 params.SetBoolean("isLoginPrimaryAccount", true);
127 }
128
129 Profile* profile = Profile::FromWebUI(web_ui());
130 std::string default_email;
131 if (reason == signin_metrics::Reason::REASON_SIGNIN_PRIMARY_ACCOUNT ||
132 reason == signin_metrics::Reason::REASON_FORCED_SIGNIN_PRIMARY_ACCOUNT) {
133 default_email =
134 profile->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
135 } else {
136 if (!net::GetValueForKeyInQuery(current_url, "email", &default_email))
137 default_email.clear();
138 }
139 if (!default_email.empty())
140 params.SetString("email", default_email);
141
142 // The legacy full-tab Chrome sign-in page is no longer used as it was relying
143 // on exchanging cookies for refresh tokens and that endpoint is no longer
144 // supported.
145 params.SetString("constrained", "1");
146
147 // TODO(rogerta): this needs to be passed on to gaia somehow.
148 std::string read_only_email;
149 net::GetValueForKeyInQuery(current_url, "readOnlyEmail", &read_only_email);
150 params.SetBoolean("readOnlyEmail", !read_only_email.empty());
151
152 SetExtraInitParams(params);
153 FireWebUIListener("load-auth-extension", params);
154 }
155
HandleCompleteLoginMessage(const base::ListValue * args)156 void InlineLoginHandler::HandleCompleteLoginMessage(
157 const base::ListValue* args) {
158 // When the network service is enabled, the webRequest API doesn't expose
159 // cookie headers. So manually fetch the cookies for the GAIA URL from the
160 // CookieManager.
161 content::WebContents* contents = web_ui()->GetWebContents();
162 content::StoragePartition* partition =
163 signin::GetSigninPartition(contents->GetBrowserContext());
164
165 partition->GetCookieManagerForBrowserProcess()->GetCookieList(
166 GaiaUrls::GetInstance()->gaia_url(),
167 net::CookieOptions::MakeAllInclusive(),
168 base::BindOnce(&InlineLoginHandler::HandleCompleteLoginMessageWithCookies,
169 weak_ptr_factory_.GetWeakPtr(),
170 base::ListValue(args->GetList())));
171 }
172
HandleCompleteLoginMessageWithCookies(const base::ListValue & args,const net::CookieStatusList & cookies,const net::CookieStatusList & excluded_cookies)173 void InlineLoginHandler::HandleCompleteLoginMessageWithCookies(
174 const base::ListValue& args,
175 const net::CookieStatusList& cookies,
176 const net::CookieStatusList& excluded_cookies) {
177 const base::Value& dict = args.GetList()[0];
178
179 const std::string& email = dict.FindKey("email")->GetString();
180 const std::string& password = dict.FindKey("password")->GetString();
181 const std::string& gaia_id = dict.FindKey("gaiaId")->GetString();
182
183 std::string auth_code;
184 for (const auto& cookie_with_status : cookies) {
185 if (cookie_with_status.cookie.Name() == "oauth_code")
186 auth_code = cookie_with_status.cookie.Value();
187 }
188
189 bool skip_for_now = dict.FindBoolKey("skipForNow").value_or(false);
190 base::Optional<bool> trusted = dict.FindBoolKey("trusted");
191 bool trusted_value = trusted.value_or(false);
192 bool trusted_found = trusted.has_value();
193
194 bool choose_what_to_sync =
195 dict.FindBoolKey("chooseWhatToSync").value_or(false);
196
197 base::Value edu_login_params;
198 if (args.GetList().size() > 1) {
199 edu_login_params = args.GetList()[1].Clone();
200 }
201
202 CompleteLogin(email, password, gaia_id, auth_code, skip_for_now,
203 trusted_value, trusted_found, choose_what_to_sync,
204 std::move(edu_login_params));
205 }
206
HandleSwitchToFullTabMessage(const base::ListValue * args)207 void InlineLoginHandler::HandleSwitchToFullTabMessage(
208 const base::ListValue* args) {
209 Browser* browser =
210 chrome::FindBrowserWithWebContents(web_ui()->GetWebContents());
211 if (browser) {
212 // |web_ui| is already presented in a full tab. Ignore this call.
213 return;
214 }
215
216 std::string url_str;
217 CHECK(args->GetString(0, &url_str));
218
219 Profile* profile = Profile::FromWebUI(web_ui());
220 GURL main_frame_url(web_ui()->GetWebContents()->GetURL());
221
222 // Adds extra parameters to the signin URL so that Chrome will close the tab
223 // and show the account management view of the avatar menu upon completion.
224 main_frame_url = net::AppendOrReplaceQueryParameter(
225 main_frame_url, signin::kSignInPromoQueryKeyAutoClose, "1");
226 main_frame_url = net::AppendOrReplaceQueryParameter(
227 main_frame_url, kSignInPromoQueryKeyShowAccountManagement, "1");
228 main_frame_url = net::AppendOrReplaceQueryParameter(
229 main_frame_url, signin::kSignInPromoQueryKeyForceKeepData, "1");
230
231 NavigateParams params(profile, main_frame_url,
232 ui::PAGE_TRANSITION_AUTO_TOPLEVEL);
233 Navigate(¶ms);
234
235 CloseDialogFromJavascript();
236 }
237
HandleNavigationButtonClicked(const base::ListValue * args)238 void InlineLoginHandler::HandleNavigationButtonClicked(
239 const base::ListValue* args) {
240 #if !defined(OS_CHROMEOS)
241 NOTREACHED() << "The inline login handler is no longer used in a browser "
242 "or tab modal dialog.";
243 #else
244 FireWebUIListener("navigate-back-in-webview");
245 #endif
246 }
247
HandleDialogClose(const base::ListValue * args)248 void InlineLoginHandler::HandleDialogClose(const base::ListValue* args) {
249 #if !defined(OS_CHROMEOS)
250 // Does nothing if user manager is not showing.
251 UserManagerProfileDialog::HideDialog();
252 #endif // !defined(OS_CHROMEOS)
253 }
254
CloseDialogFromJavascript()255 void InlineLoginHandler::CloseDialogFromJavascript() {
256 if (IsJavascriptAllowed())
257 FireWebUIListener("close-dialog");
258 }
259