Add new certificateProvider extension API.
[chromium-blink-merge.git] / chrome / browser / ui / webui / signin / inline_login_handler_impl.cc
blob5d2b39aed333385e8b00412f5b380a9838200e42
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.
5 #include "chrome/browser/ui/webui/signin/inline_login_handler_impl.h"
7 #include <string>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/location.h"
12 #include "base/metrics/histogram.h"
13 #include "base/prefs/pref_service.h"
14 #include "base/single_thread_task_runner.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/utf_string_conversions.h"
18 #include "base/thread_task_runner_handle.h"
19 #include "base/values.h"
20 #include "chrome/browser/browser_process.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/profiles/profile_window.h"
23 #include "chrome/browser/signin/about_signin_internals_factory.h"
24 #include "chrome/browser/signin/account_tracker_service_factory.h"
25 #include "chrome/browser/signin/chrome_signin_client_factory.h"
26 #include "chrome/browser/signin/local_auth.h"
27 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
28 #include "chrome/browser/signin/signin_error_controller_factory.h"
29 #include "chrome/browser/signin/signin_manager_factory.h"
30 #include "chrome/browser/signin/signin_promo.h"
31 #include "chrome/browser/sync/profile_sync_service.h"
32 #include "chrome/browser/sync/profile_sync_service_factory.h"
33 #include "chrome/browser/ui/browser_finder.h"
34 #include "chrome/browser/ui/browser_window.h"
35 #include "chrome/browser/ui/chrome_pages.h"
36 #include "chrome/browser/ui/tab_modal_confirm_dialog.h"
37 #include "chrome/browser/ui/tab_modal_confirm_dialog_delegate.h"
38 #include "chrome/browser/ui/tabs/tab_strip_model.h"
39 #include "chrome/browser/ui/user_manager.h"
40 #include "chrome/browser/ui/webui/signin/inline_login_ui.h"
41 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
42 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
43 #include "chrome/common/url_constants.h"
44 #include "chrome/grit/chromium_strings.h"
45 #include "chrome/grit/generated_resources.h"
46 #include "components/signin/core/browser/about_signin_internals.h"
47 #include "components/signin/core/browser/account_tracker_service.h"
48 #include "components/signin/core/browser/profile_oauth2_token_service.h"
49 #include "components/signin/core/browser/signin_error_controller.h"
50 #include "components/signin/core/browser/signin_header_helper.h"
51 #include "components/signin/core/browser/signin_metrics.h"
52 #include "components/signin/core/common/profile_management_switches.h"
53 #include "components/signin/core/common/signin_pref_names.h"
54 #include "content/public/browser/storage_partition.h"
55 #include "content/public/browser/web_ui.h"
56 #include "google_apis/gaia/gaia_auth_consumer.h"
57 #include "google_apis/gaia/gaia_auth_fetcher.h"
58 #include "google_apis/gaia/gaia_auth_util.h"
59 #include "google_apis/gaia/gaia_constants.h"
60 #include "google_apis/gaia/gaia_urls.h"
61 #include "grit/components_strings.h"
62 #include "net/base/url_util.h"
63 #include "ui/base/l10n/l10n_util.h"
65 namespace {
67 void LogHistogramValue(int action) {
68 UMA_HISTOGRAM_ENUMERATION("Signin.AllAccessPointActions", action,
69 signin_metrics::HISTOGRAM_MAX);
72 void RedirectToNtpOrAppsPage(content::WebContents* contents,
73 signin_metrics::Source source) {
74 // Do nothing if a navigation is pending, since this call can be triggered
75 // from DidStartLoading. This avoids deleting the pending entry while we are
76 // still navigating to it. See crbug/346632.
77 if (contents->GetController().GetPendingEntry())
78 return;
80 VLOG(1) << "RedirectToNtpOrAppsPage";
81 // Redirect to NTP/Apps page and display a confirmation bubble
82 GURL url(source == signin_metrics::SOURCE_APPS_PAGE_LINK ?
83 chrome::kChromeUIAppsURL : chrome::kChromeUINewTabURL);
84 content::OpenURLParams params(url,
85 content::Referrer(),
86 CURRENT_TAB,
87 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
88 false);
89 contents->OpenURL(params);
92 void RedirectToNtpOrAppsPageIfNecessary(content::WebContents* contents,
93 signin_metrics::Source source) {
94 if (source != signin_metrics::SOURCE_SETTINGS)
95 RedirectToNtpOrAppsPage(contents, source);
98 class ConfirmEmailDialogDelegate : public TabModalConfirmDialogDelegate {
99 public:
100 enum Action {
101 CREATE_NEW_USER,
102 START_SYNC,
103 CLOSE
106 // Callback indicating action performed by the user.
107 typedef base::Callback<void(Action)> Callback;
109 // Ask the user for confirmation before starting to sync.
110 static void AskForConfirmation(content::WebContents* contents,
111 const std::string& last_email,
112 const std::string& email,
113 Callback callback);
115 private:
116 ConfirmEmailDialogDelegate(content::WebContents* contents,
117 const std::string& last_email,
118 const std::string& email,
119 Callback callback);
120 ~ConfirmEmailDialogDelegate() override;
122 // TabModalConfirmDialogDelegate:
123 base::string16 GetTitle() override;
124 base::string16 GetDialogMessage() override;
125 base::string16 GetAcceptButtonTitle() override;
126 base::string16 GetCancelButtonTitle() override;
127 base::string16 GetLinkText() const override;
128 void OnAccepted() override;
129 void OnCanceled() override;
130 void OnClosed() override;
131 void OnLinkClicked(WindowOpenDisposition disposition) override;
133 std::string last_email_;
134 std::string email_;
135 Callback callback_;
137 // Web contents from which the "Learn more" link should be opened.
138 content::WebContents* web_contents_;
140 DISALLOW_COPY_AND_ASSIGN(ConfirmEmailDialogDelegate);
143 // static
144 void ConfirmEmailDialogDelegate::AskForConfirmation(
145 content::WebContents* contents,
146 const std::string& last_email,
147 const std::string& email,
148 Callback callback) {
149 TabModalConfirmDialog::Create(
150 new ConfirmEmailDialogDelegate(contents, last_email, email, callback),
151 contents);
154 ConfirmEmailDialogDelegate::ConfirmEmailDialogDelegate(
155 content::WebContents* contents,
156 const std::string& last_email,
157 const std::string& email,
158 Callback callback)
159 : TabModalConfirmDialogDelegate(contents),
160 last_email_(last_email),
161 email_(email),
162 callback_(callback),
163 web_contents_(contents) {
166 ConfirmEmailDialogDelegate::~ConfirmEmailDialogDelegate() {
169 base::string16 ConfirmEmailDialogDelegate::GetTitle() {
170 return l10n_util::GetStringUTF16(
171 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_TITLE);
174 base::string16 ConfirmEmailDialogDelegate::GetDialogMessage() {
175 return l10n_util::GetStringFUTF16(
176 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_MESSAGE,
177 base::UTF8ToUTF16(last_email_), base::UTF8ToUTF16(email_));
180 base::string16 ConfirmEmailDialogDelegate::GetAcceptButtonTitle() {
181 return l10n_util::GetStringUTF16(
182 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_OK_BUTTON);
185 base::string16 ConfirmEmailDialogDelegate::GetCancelButtonTitle() {
186 return l10n_util::GetStringUTF16(
187 IDS_ONE_CLICK_SIGNIN_CONFIRM_EMAIL_DIALOG_CANCEL_BUTTON);
190 base::string16 ConfirmEmailDialogDelegate::GetLinkText() const {
191 return l10n_util::GetStringUTF16(IDS_LEARN_MORE);
194 void ConfirmEmailDialogDelegate::OnAccepted() {
195 base::ResetAndReturn(&callback_).Run(CREATE_NEW_USER);
198 void ConfirmEmailDialogDelegate::OnCanceled() {
199 base::ResetAndReturn(&callback_).Run(START_SYNC);
202 void ConfirmEmailDialogDelegate::OnClosed() {
203 base::ResetAndReturn(&callback_).Run(CLOSE);
206 void ConfirmEmailDialogDelegate::OnLinkClicked(
207 WindowOpenDisposition disposition) {
208 content::OpenURLParams params(
209 GURL(chrome::kChromeSyncMergeTroubleshootingURL),
210 content::Referrer(),
211 NEW_POPUP,
212 ui::PAGE_TRANSITION_AUTO_TOPLEVEL,
213 false);
214 // It is guaranteed that |web_contents_| is valid here because when it's
215 // deleted, the dialog is immediately closed and no further action can be
216 // performed.
217 web_contents_->OpenURL(params);
220 class InlineSigninHelper : public GaiaAuthConsumer {
221 public:
222 InlineSigninHelper(
223 base::WeakPtr<InlineLoginHandlerImpl> handler,
224 net::URLRequestContextGetter* getter,
225 Profile* profile,
226 const GURL& current_url,
227 const std::string& email,
228 const std::string& gaia_id,
229 const std::string& password,
230 const std::string& session_index,
231 const std::string& signin_scoped_device_id,
232 bool choose_what_to_sync,
233 bool confirm_untrusted_signin);
235 private:
236 // Handles cross account sign in error. If the supplied |email| does not match
237 // the last signed in email of the current profile, then Chrome will show a
238 // confirmation dialog before starting sync. It returns true if there is a
239 // cross account error, and false otherwise.
240 bool HandleCrossAccountError(
241 const std::string& refresh_token,
242 signin_metrics::Source source,
243 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
244 OneClickSigninSyncStarter::StartSyncMode start_mode);
246 // Callback used with ConfirmEmailDialogDelegate.
247 void ConfirmEmailAction(
248 content::WebContents* web_contents,
249 const std::string& refresh_token,
250 signin_metrics::Source source,
251 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
252 OneClickSigninSyncStarter::StartSyncMode start_mode,
253 ConfirmEmailDialogDelegate::Action action);
255 // Overridden from GaiaAuthConsumer.
256 void OnClientOAuthSuccess(const ClientOAuthResult& result) override;
257 void OnClientOAuthFailure(const GoogleServiceAuthError& error)
258 override;
260 GaiaAuthFetcher gaia_auth_fetcher_;
261 base::WeakPtr<InlineLoginHandlerImpl> handler_;
262 Profile* profile_;
263 GURL current_url_;
264 std::string email_;
265 std::string gaia_id_;
266 std::string password_;
267 std::string session_index_;
268 bool choose_what_to_sync_;
269 bool confirm_untrusted_signin_;
271 DISALLOW_COPY_AND_ASSIGN(InlineSigninHelper);
274 InlineSigninHelper::InlineSigninHelper(
275 base::WeakPtr<InlineLoginHandlerImpl> handler,
276 net::URLRequestContextGetter* getter,
277 Profile* profile,
278 const GURL& current_url,
279 const std::string& email,
280 const std::string& gaia_id,
281 const std::string& password,
282 const std::string& session_index,
283 const std::string& signin_scoped_device_id,
284 bool choose_what_to_sync,
285 bool confirm_untrusted_signin)
286 : gaia_auth_fetcher_(this, GaiaConstants::kChromeSource, getter),
287 handler_(handler),
288 profile_(profile),
289 current_url_(current_url),
290 email_(email),
291 gaia_id_(gaia_id),
292 password_(password),
293 session_index_(session_index),
294 choose_what_to_sync_(choose_what_to_sync),
295 confirm_untrusted_signin_(confirm_untrusted_signin) {
296 DCHECK(profile_);
297 DCHECK(!email_.empty());
298 gaia_auth_fetcher_.StartCookieForOAuthLoginTokenExchangeWithDeviceId(
299 session_index, signin_scoped_device_id);
302 void InlineSigninHelper::OnClientOAuthSuccess(const ClientOAuthResult& result) {
303 content::WebContents* contents = NULL;
304 Browser* browser = NULL;
305 if (handler_) {
306 contents = handler_->web_ui()->GetWebContents();
307 browser = handler_->GetDesktopBrowser();
310 AboutSigninInternals* about_signin_internals =
311 AboutSigninInternalsFactory::GetForProfile(profile_);
312 about_signin_internals->OnRefreshTokenReceived("Successful");
314 // Prime the account tracker with this combination of gaia id/display email.
315 std::string account_id =
316 AccountTrackerServiceFactory::GetForProfile(profile_)
317 ->SeedAccountInfo(gaia_id_, email_);
319 signin_metrics::Source source = signin::GetSourceForPromoURL(current_url_);
321 SigninManager* signin_manager = SigninManagerFactory::GetForProfile(profile_);
322 std::string primary_email =
323 signin_manager->GetAuthenticatedAccountInfo().email;
324 if (gaia::AreEmailsSame(email_, primary_email) &&
325 source == signin_metrics::SOURCE_REAUTH &&
326 switches::IsNewProfileManagement() &&
327 !password_.empty() &&
328 profiles::IsLockAvailable(profile_)) {
329 LocalAuth::SetLocalAuthCredentials(profile_, password_);
332 if (source == signin_metrics::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT ||
333 source == signin_metrics::SOURCE_REAUTH) {
334 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_)->
335 UpdateCredentials(account_id, result.refresh_token);
337 if (signin::IsAutoCloseEnabledInURL(current_url_)) {
338 // Close the gaia sign in tab via a task to make sure we aren't in the
339 // middle of any webui handler code.
340 base::ThreadTaskRunnerHandle::Get()->PostTask(
341 FROM_HERE,
342 base::Bind(&InlineLoginHandlerImpl::CloseTab, handler_,
343 signin::ShouldShowAccountManagement(current_url_)));
346 if (source == signin_metrics::SOURCE_REAUTH)
347 signin_manager->MergeSigninCredentialIntoCookieJar();
348 } else {
349 ProfileSyncService* sync_service =
350 ProfileSyncServiceFactory::GetForProfile(profile_);
351 SigninErrorController* error_controller =
352 SigninErrorControllerFactory::GetForProfile(profile_);
354 bool is_new_avatar_menu = switches::IsNewAvatarMenu();
356 OneClickSigninSyncStarter::StartSyncMode start_mode;
357 if (source == signin_metrics::SOURCE_SETTINGS || choose_what_to_sync_) {
358 bool show_settings_without_configure =
359 error_controller->HasError() &&
360 sync_service &&
361 sync_service->HasSyncSetupCompleted();
362 start_mode = show_settings_without_configure ?
363 OneClickSigninSyncStarter::SHOW_SETTINGS_WITHOUT_CONFIGURE :
364 OneClickSigninSyncStarter::CONFIGURE_SYNC_FIRST;
365 } else {
366 start_mode = is_new_avatar_menu ?
367 OneClickSigninSyncStarter::CONFIRM_SYNC_SETTINGS_FIRST :
368 OneClickSigninSyncStarter::SYNC_WITH_DEFAULT_SETTINGS;
371 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required;
372 if (confirm_untrusted_signin_) {
373 confirmation_required =
374 OneClickSigninSyncStarter::CONFIRM_UNTRUSTED_SIGNIN;
375 } else if (is_new_avatar_menu) {
376 confirmation_required = OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
377 } else {
378 confirmation_required =
379 source == signin_metrics::SOURCE_SETTINGS ||
380 choose_what_to_sync_ ?
381 OneClickSigninSyncStarter::NO_CONFIRMATION :
382 OneClickSigninSyncStarter::CONFIRM_AFTER_SIGNIN;
385 bool start_signin = !HandleCrossAccountError(result.refresh_token, source,
386 confirmation_required, start_mode);
387 if (start_signin) {
388 // Call OneClickSigninSyncStarter to exchange oauth code for tokens.
389 // OneClickSigninSyncStarter will delete itself once the job is done.
390 new OneClickSigninSyncStarter(
391 profile_, browser,
392 gaia_id_, email_, password_, result.refresh_token,
393 start_mode,
394 contents,
395 confirmation_required,
396 signin::GetNextPageURLForPromoURL(current_url_),
397 base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
398 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
403 bool InlineSigninHelper::HandleCrossAccountError(
404 const std::string& refresh_token,
405 signin_metrics::Source source,
406 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
407 OneClickSigninSyncStarter::StartSyncMode start_mode) {
408 std::string last_email =
409 profile_->GetPrefs()->GetString(prefs::kGoogleServicesLastUsername);
411 if (last_email.empty() || gaia::AreEmailsSame(last_email, email_))
412 return false;
414 Browser* browser = chrome::FindLastActiveWithProfile(
415 profile_, chrome::GetActiveDesktop());
416 content::WebContents* web_contents =
417 browser->tab_strip_model()->GetActiveWebContents();
419 ConfirmEmailDialogDelegate::AskForConfirmation(
420 web_contents,
421 last_email,
422 email_,
423 base::Bind(&InlineSigninHelper::ConfirmEmailAction,
424 base::Unretained(this),
425 web_contents,
426 refresh_token,
427 source,
428 confirmation_required,
429 start_mode));
430 return true;
433 void InlineSigninHelper::ConfirmEmailAction(
434 content::WebContents* web_contents,
435 const std::string& refresh_token,
436 signin_metrics::Source source,
437 OneClickSigninSyncStarter::ConfirmationRequired confirmation_required,
438 OneClickSigninSyncStarter::StartSyncMode start_mode,
439 ConfirmEmailDialogDelegate::Action action) {
440 Browser* browser = chrome::FindLastActiveWithProfile(
441 profile_, chrome::GetActiveDesktop());
442 switch (action) {
443 case ConfirmEmailDialogDelegate::CREATE_NEW_USER:
444 if (handler_) {
445 handler_->SyncStarterCallback(
446 OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
448 chrome::ShowSettingsSubPage(browser,
449 std::string(chrome::kCreateProfileSubPage));
450 break;
451 case ConfirmEmailDialogDelegate::START_SYNC:
452 new OneClickSigninSyncStarter(
453 profile_, browser, gaia_id_, email_, password_, refresh_token,
454 start_mode, web_contents, confirmation_required, GURL(),
455 base::Bind(&InlineLoginHandlerImpl::SyncStarterCallback, handler_));
456 break;
457 case ConfirmEmailDialogDelegate::CLOSE:
458 if (handler_) {
459 handler_->SyncStarterCallback(
460 OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
462 break;
463 default:
464 DCHECK(false) << "Invalid action";
466 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
469 void InlineSigninHelper::OnClientOAuthFailure(
470 const GoogleServiceAuthError& error) {
471 if (handler_)
472 handler_->HandleLoginError(error.ToString());
474 AboutSigninInternals* about_signin_internals =
475 AboutSigninInternalsFactory::GetForProfile(profile_);
476 about_signin_internals->OnRefreshTokenReceived("Failure");
478 base::ThreadTaskRunnerHandle::Get()->DeleteSoon(FROM_HERE, this);
481 } // namespace
483 InlineLoginHandlerImpl::InlineLoginHandlerImpl()
484 : confirm_untrusted_signin_(false),
485 weak_factory_(this) {
488 InlineLoginHandlerImpl::~InlineLoginHandlerImpl() {}
490 // This method is not called with webview sign in enabled.
491 void InlineLoginHandlerImpl::DidCommitProvisionalLoadForFrame(
492 content::RenderFrameHost* render_frame_host,
493 const GURL& url,
494 ui::PageTransition transition_type) {
495 if (!web_contents())
496 return;
498 // Returns early if this is not a gaia iframe navigation.
499 const GURL kGaiaExtOrigin(
500 GaiaUrls::GetInstance()->signin_completed_continue_url().GetOrigin());
501 content::RenderFrameHost* gaia_frame = InlineLoginUI::GetAuthFrame(
502 web_contents(), kGaiaExtOrigin, "signin-frame");
503 if (render_frame_host != gaia_frame)
504 return;
506 // Loading any untrusted (e.g., HTTP) URLs in the privileged sign-in process
507 // will require confirmation before the sign in takes effect.
508 if (!url.is_empty()) {
509 GURL origin(url.GetOrigin());
510 if (url.spec() != url::kAboutBlankURL &&
511 origin != kGaiaExtOrigin &&
512 !gaia::IsGaiaSignonRealm(origin)) {
513 confirm_untrusted_signin_ = true;
518 // static
519 bool InlineLoginHandlerImpl::CanOffer(Profile* profile,
520 CanOfferFor can_offer_for,
521 const std::string& gaia_id,
522 const std::string& email,
523 std::string* error_message) {
524 if (error_message)
525 error_message->clear();
527 if (!profile)
528 return false;
530 SigninManager* manager = SigninManagerFactory::GetForProfile(profile);
531 if (manager && !manager->IsSigninAllowed())
532 return false;
534 if (!ChromeSigninClient::ProfileAllowsSigninCookies(profile))
535 return false;
537 if (!email.empty()) {
538 if (!manager)
539 return false;
541 // Make sure this username is not prohibited by policy.
542 if (!manager->IsAllowedUsername(email)) {
543 if (error_message) {
544 error_message->assign(
545 l10n_util::GetStringUTF8(IDS_SYNC_LOGIN_NAME_PROHIBITED));
547 return false;
550 if (can_offer_for == CAN_OFFER_FOR_SECONDARY_ACCOUNT)
551 return true;
553 // If the signin manager already has an authenticated name, then this is a
554 // re-auth scenario. Make sure the email just signed in corresponds to
555 // the one sign in manager expects.
556 std::string current_email = manager->GetAuthenticatedAccountInfo().email;
557 const bool same_email = gaia::AreEmailsSame(current_email, email);
558 if (!current_email.empty() && !same_email) {
559 UMA_HISTOGRAM_ENUMERATION("Signin.Reauth",
560 signin_metrics::HISTOGRAM_ACCOUNT_MISSMATCH,
561 signin_metrics::HISTOGRAM_MAX);
562 if (error_message) {
563 error_message->assign(
564 l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
565 base::UTF8ToUTF16(current_email)));
567 return false;
570 // If some profile, not just the current one, is already connected to this
571 // account, don't show the infobar.
572 if (g_browser_process && !same_email) {
573 ProfileManager* profile_manager = g_browser_process->profile_manager();
574 if (profile_manager) {
575 ProfileInfoCache& cache = profile_manager->GetProfileInfoCache();
576 for (size_t i = 0; i < cache.GetNumberOfProfiles(); ++i) {
577 // For backward compatibility, need to also check the username of the
578 // profile, since the GAIA ID may not have been set yet for the
579 // profile cache info. It will get set once the profile is opened.
580 std::string profile_gaia_id = cache.GetGAIAIdOfProfileAtIndex(i);
581 std::string profile_email =
582 base::UTF16ToUTF8(cache.GetUserNameOfProfileAtIndex(i));
583 if (gaia_id == profile_gaia_id ||
584 gaia::AreEmailsSame(email, profile_email)) {
585 if (error_message) {
586 error_message->assign(
587 l10n_util::GetStringUTF8(IDS_SYNC_USER_NAME_IN_USE_ERROR));
589 return false;
596 return true;
599 void InlineLoginHandlerImpl::SetExtraInitParams(base::DictionaryValue& params) {
600 params.SetString("service", "chromiumsync");
602 content::WebContents* contents = web_ui()->GetWebContents();
603 const GURL& current_url = contents->GetURL();
604 std::string is_constrained;
605 net::GetValueForKeyInQuery(current_url, "constrained", &is_constrained);
607 content::WebContentsObserver::Observe(contents);
608 LogHistogramValue(signin_metrics::HISTOGRAM_SHOWN);
611 void InlineLoginHandlerImpl::CompleteLogin(const base::ListValue* args) {
612 content::WebContents* contents = web_ui()->GetWebContents();
613 const GURL& current_url = contents->GetURL();
615 const base::DictionaryValue* dict = NULL;
616 args->GetDictionary(0, &dict);
618 bool skip_for_now = false;
619 dict->GetBoolean("skipForNow", &skip_for_now);
620 if (skip_for_now) {
621 signin::SetUserSkippedPromo(Profile::FromWebUI(web_ui()));
622 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
623 return;
626 // This value exists only for webview sign in.
627 bool trusted = false;
628 if (dict->GetBoolean("trusted", &trusted))
629 confirm_untrusted_signin_ = !trusted;
631 base::string16 email_string16;
632 dict->GetString("email", &email_string16);
633 DCHECK(!email_string16.empty());
634 std::string email(base::UTF16ToASCII(email_string16));
636 base::string16 password_string16;
637 dict->GetString("password", &password_string16);
638 std::string password(base::UTF16ToASCII(password_string16));
640 base::string16 gaia_id_string16;
641 dict->GetString("gaiaId", &gaia_id_string16);
642 DCHECK(!gaia_id_string16.empty());
643 std::string gaia_id = base::UTF16ToASCII(gaia_id_string16);
645 base::string16 session_index_string16;
646 dict->GetString("sessionIndex", &session_index_string16);
647 std::string session_index = base::UTF16ToASCII(session_index_string16);
648 DCHECK(!session_index.empty());
650 bool choose_what_to_sync = false;
651 dict->GetBoolean("chooseWhatToSync", &choose_what_to_sync);
653 content::StoragePartition* partition =
654 content::BrowserContext::GetStoragePartitionForSite(
655 contents->GetBrowserContext(), signin::GetSigninPartitionURL());
657 // If this was called from the user manager to reauthenticate the profile,
658 // the current profile is the system profile. In this case, use the email to
659 // find the right profile to reauthenticate. Otherwise the profile can be
660 // taken from web_ui().
661 Profile* profile = Profile::FromWebUI(web_ui());
662 if (profile->GetOriginalProfile()->IsSystemProfile()) {
663 // Switch to the profile and finish the login. Don't pass a handler pointer
664 // since it will be destroyed before the callback runs.
665 ProfileManager* manager = g_browser_process->profile_manager();
666 base::FilePath path = profiles::GetPathOfProfileWithEmail(manager, email);
667 if (!path.empty()) {
668 FinishCompleteLoginParams params(nullptr, partition, current_url, path,
669 confirm_untrusted_signin_, email,
670 gaia_id, password, session_index,
671 choose_what_to_sync);
672 ProfileManager::CreateCallback callback = base::Bind(
673 &InlineLoginHandlerImpl::FinishCompleteLogin, params);
674 profiles::SwitchToProfile(path, chrome::GetActiveDesktop(), true,
675 callback, ProfileMetrics::SWITCH_PROFILE_UNLOCK);
677 } else {
678 FinishCompleteLogin(
679 FinishCompleteLoginParams(this, partition, current_url,
680 base::FilePath(), confirm_untrusted_signin_,
681 email, gaia_id, password, session_index,
682 choose_what_to_sync),
683 profile,
684 Profile::CREATE_STATUS_CREATED);
688 InlineLoginHandlerImpl::FinishCompleteLoginParams::FinishCompleteLoginParams(
689 InlineLoginHandlerImpl* handler,
690 content::StoragePartition* partition,
691 const GURL& url,
692 const base::FilePath& profile_path,
693 bool confirm_untrusted_signin,
694 const std::string& email,
695 const std::string& gaia_id,
696 const std::string& password,
697 const std::string& session_index,
698 bool choose_what_to_sync)
699 : handler(handler),
700 partition(partition),
701 url(url),
702 profile_path(profile_path),
703 confirm_untrusted_signin(confirm_untrusted_signin),
704 email(email),
705 gaia_id(gaia_id),
706 password(password),
707 session_index(session_index),
708 choose_what_to_sync(choose_what_to_sync) {}
710 InlineLoginHandlerImpl::
711 FinishCompleteLoginParams::~FinishCompleteLoginParams() {}
713 // static
714 void InlineLoginHandlerImpl::FinishCompleteLogin(
715 const FinishCompleteLoginParams& params,
716 Profile* profile,
717 Profile::CreateStatus status) {
718 // When doing a SAML sign in, this email check may result in a false
719 // positive. This happens when the user types one email address in the
720 // gaia sign in page, but signs in to a different account in the SAML sign in
721 // page.
722 std::string default_email;
723 std::string validate_email;
724 if (net::GetValueForKeyInQuery(params.url, "email", &default_email) &&
725 net::GetValueForKeyInQuery(params.url, "validateEmail",
726 &validate_email) &&
727 validate_email == "1") {
728 if (!gaia::AreEmailsSame(params.email, default_email)) {
729 if (params.handler) {
730 params.handler->HandleLoginError(
731 l10n_util::GetStringFUTF8(IDS_SYNC_WRONG_EMAIL,
732 base::UTF8ToUTF16(default_email)));
734 return;
738 signin_metrics::Source source = signin::GetSourceForPromoURL(params.url);
739 LogHistogramValue(signin_metrics::HISTOGRAM_ACCEPTED);
740 bool switch_to_advanced =
741 params.choose_what_to_sync && (source != signin_metrics::SOURCE_SETTINGS);
742 LogHistogramValue(
743 switch_to_advanced ? signin_metrics::HISTOGRAM_WITH_ADVANCED :
744 signin_metrics::HISTOGRAM_WITH_DEFAULTS);
746 CanOfferFor can_offer_for = CAN_OFFER_FOR_ALL;
747 switch (source) {
748 case signin_metrics::SOURCE_AVATAR_BUBBLE_ADD_ACCOUNT:
749 can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT;
750 break;
751 case signin_metrics::SOURCE_REAUTH: {
752 std::string primary_username =
753 SigninManagerFactory::GetForProfile(profile)
754 ->GetAuthenticatedAccountInfo()
755 .email;
756 if (!gaia::AreEmailsSame(default_email, primary_username))
757 can_offer_for = CAN_OFFER_FOR_SECONDARY_ACCOUNT;
758 break;
760 default:
761 // No need to change |can_offer_for|.
762 break;
765 std::string error_msg;
766 bool can_offer = CanOffer(profile, can_offer_for, params.gaia_id,
767 params.email, &error_msg);
768 if (!can_offer) {
769 if (params.handler)
770 params.handler->HandleLoginError(error_msg);
771 return;
774 AboutSigninInternals* about_signin_internals =
775 AboutSigninInternalsFactory::GetForProfile(profile);
776 about_signin_internals->OnAuthenticationResultReceived("Successful");
778 SigninClient* signin_client =
779 ChromeSigninClientFactory::GetForProfile(profile);
780 std::string signin_scoped_device_id =
781 signin_client->GetSigninScopedDeviceId();
782 base::WeakPtr<InlineLoginHandlerImpl> handler_weak_ptr;
783 if (params.handler)
784 handler_weak_ptr = params.handler->GetWeakPtr();
786 // InlineSigninHelper will delete itself.
787 new InlineSigninHelper(handler_weak_ptr,
788 params.partition->GetURLRequestContext(), profile,
789 params.url,
790 params.email, params.gaia_id, params.password,
791 params.session_index, signin_scoped_device_id,
792 params.choose_what_to_sync,
793 params.confirm_untrusted_signin);
795 // If opened from user manager to reauthenticate, make sure the user manager
796 // is closed and that the profile is marked as unlocked.
797 if (!params.profile_path.empty()) {
798 UserManager::Hide();
799 ProfileManager* profile_manager = g_browser_process->profile_manager();
800 if (profile_manager) {
801 ProfileAttributesEntry* entry;
802 if (profile_manager->GetProfileInfoCache()
803 .GetProfileAttributesWithPath(params.profile_path, &entry)) {
804 entry->SetIsSigninRequired(false);
809 if (params.handler)
810 params.handler->
811 web_ui()->CallJavascriptFunction("inline.login.closeDialog");
814 void InlineLoginHandlerImpl::HandleLoginError(const std::string& error_msg) {
815 SyncStarterCallback(OneClickSigninSyncStarter::SYNC_SETUP_FAILURE);
817 Browser* browser = GetDesktopBrowser();
818 if (browser && !error_msg.empty()) {
819 LoginUIServiceFactory::GetForProfile(Profile::FromWebUI(web_ui()))->
820 DisplayLoginResult(browser, base::UTF8ToUTF16(error_msg));
824 Browser* InlineLoginHandlerImpl::GetDesktopBrowser() {
825 Browser* browser = chrome::FindBrowserWithWebContents(
826 web_ui()->GetWebContents());
827 if (!browser) {
828 browser = chrome::FindLastActiveWithProfile(
829 Profile::FromWebUI(web_ui()), chrome::GetActiveDesktop());
831 return browser;
834 void InlineLoginHandlerImpl::SyncStarterCallback(
835 OneClickSigninSyncStarter::SyncSetupResult result) {
836 content::WebContents* contents = web_ui()->GetWebContents();
838 if (contents->GetController().GetPendingEntry()) {
839 // Do nothing if a navigation is pending, since this call can be triggered
840 // from DidStartLoading. This avoids deleting the pending entry while we are
841 // still navigating to it. See crbug/346632.
842 return;
845 const GURL& current_url = contents->GetLastCommittedURL();
846 signin_metrics::Source source = signin::GetSourceForPromoURL(current_url);
847 bool auto_close = signin::IsAutoCloseEnabledInURL(current_url);
849 if (result == OneClickSigninSyncStarter::SYNC_SETUP_FAILURE) {
850 RedirectToNtpOrAppsPage(contents, source);
851 } else if (auto_close) {
852 base::ThreadTaskRunnerHandle::Get()->PostTask(
853 FROM_HERE,
854 base::Bind(&InlineLoginHandlerImpl::CloseTab,
855 weak_factory_.GetWeakPtr(),
856 signin::ShouldShowAccountManagement(current_url)));
857 } else {
858 RedirectToNtpOrAppsPageIfNecessary(contents, source);
862 void InlineLoginHandlerImpl::CloseTab(bool show_account_management) {
863 content::WebContents* tab = web_ui()->GetWebContents();
864 Browser* browser = chrome::FindBrowserWithWebContents(tab);
865 if (browser) {
866 TabStripModel* tab_strip_model = browser->tab_strip_model();
867 if (tab_strip_model) {
868 int index = tab_strip_model->GetIndexOfWebContents(tab);
869 if (index != TabStripModel::kNoTab) {
870 tab_strip_model->ExecuteContextMenuCommand(
871 index, TabStripModel::CommandCloseTab);
875 if (show_account_management) {
876 browser->window()->ShowAvatarBubbleFromAvatarButton(
877 BrowserWindow::AVATAR_BUBBLE_MODE_ACCOUNT_MANAGEMENT,
878 signin::ManageAccountsParams());