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.
5 #include "chrome/browser/ui/sync/one_click_signin_sync_starter.h"
7 #include "base/metrics/histogram.h"
8 #include "base/prefs/pref_service.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/browser/browser_process.h"
12 #if defined(ENABLE_CONFIGURATION_POLICY)
13 #include "chrome/browser/policy/cloud/user_policy_signin_service.h"
14 #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
17 #include "chrome/browser/profiles/profile.h"
18 #include "chrome/browser/profiles/profile_avatar_icon_util.h"
19 #include "chrome/browser/profiles/profile_info_cache.h"
20 #include "chrome/browser/profiles/profile_io_data.h"
21 #include "chrome/browser/profiles/profile_manager.h"
22 #include "chrome/browser/profiles/profile_window.h"
23 #include "chrome/browser/signin/signin_manager_factory.h"
24 #include "chrome/browser/signin/signin_tracker_factory.h"
25 #include "chrome/browser/sync/profile_sync_service.h"
26 #include "chrome/browser/sync/profile_sync_service_factory.h"
27 #include "chrome/browser/ui/browser.h"
28 #include "chrome/browser/ui/browser_dialogs.h"
29 #include "chrome/browser/ui/browser_finder.h"
30 #include "chrome/browser/ui/browser_list.h"
31 #include "chrome/browser/ui/browser_navigator.h"
32 #include "chrome/browser/ui/browser_tabstrip.h"
33 #include "chrome/browser/ui/browser_window.h"
34 #include "chrome/browser/ui/chrome_pages.h"
35 #include "chrome/browser/ui/sync/one_click_signin_sync_observer.h"
36 #include "chrome/browser/ui/tabs/tab_strip_model.h"
37 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
38 #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.h"
39 #include "chrome/common/url_constants.h"
40 #include "components/signin/core/browser/signin_manager.h"
41 #include "components/signin/core/browser/signin_metrics.h"
42 #include "components/signin/core/common/profile_management_switches.h"
43 #include "components/sync_driver/sync_prefs.h"
44 #include "grit/chromium_strings.h"
45 #include "grit/generated_resources.h"
46 #include "net/url_request/url_request_context_getter.h"
47 #include "ui/base/l10n/l10n_util.h"
51 // UMA histogram for tracking what users do when presented with the signin
54 // (a) existing enumerated constants should never be deleted or reordered, and
55 // (b) new constants should only be appended at the end of the enumeration.
57 // Keep this in sync with SigninChoice in histograms.xml.
59 SIGNIN_CHOICE_CANCEL = 0,
60 SIGNIN_CHOICE_CONTINUE = 1,
61 SIGNIN_CHOICE_NEW_PROFILE = 2,
62 // SIGNIN_CHOICE_SIZE should always be last - this is a count of the number
63 // of items in this enum.
67 void SetUserChoiceHistogram(SigninChoice choice) {
68 UMA_HISTOGRAM_ENUMERATION("Enterprise.UserSigninChoice",
75 OneClickSigninSyncStarter::OneClickSigninSyncStarter(
78 const std::string& email,
79 const std::string& password,
80 const std::string& refresh_token,
81 StartSyncMode start_mode,
82 content::WebContents* web_contents,
83 ConfirmationRequired confirmation_required,
84 const GURL& continue_url,
85 Callback sync_setup_completed_callback)
86 : content::WebContentsObserver(web_contents),
88 start_mode_(start_mode),
89 desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE),
90 confirmation_required_(confirmation_required),
91 continue_url_(continue_url),
92 sync_setup_completed_callback_(sync_setup_completed_callback),
93 weak_pointer_factory_(this) {
95 DCHECK(web_contents || continue_url.is_empty());
96 BrowserList::AddObserver(this);
97 Initialize(profile, browser);
99 // Policy is enabled, so pass in a callback to do extra policy-related UI
100 // before signin completes.
101 SigninManagerFactory::GetForProfile(profile_)->
102 StartSignInWithRefreshToken(
103 refresh_token, email, password,
104 base::Bind(&OneClickSigninSyncStarter::ConfirmSignin,
105 weak_pointer_factory_.GetWeakPtr()));
108 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser* browser) {
109 if (browser == browser_)
113 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
114 BrowserList::RemoveObserver(this);
115 LoginUIServiceFactory::GetForProfile(profile_)->RemoveObserver(this);
118 void OneClickSigninSyncStarter::Initialize(Profile* profile, Browser* browser) {
122 LoginUIServiceFactory::GetForProfile(profile_)->RemoveObserver(this);
127 LoginUIServiceFactory::GetForProfile(profile_)->AddObserver(this);
129 // Cache the parent desktop for the browser, so we can reuse that same
130 // desktop for any UI we want to display.
132 desktop_type_ = browser->host_desktop_type();
134 desktop_type_ = chrome::GetActiveDesktop();
137 signin_tracker_ = SigninTrackerFactory::CreateForProfile(profile_, this);
139 // Let the sync service know that setup is in progress so it doesn't start
140 // syncing until the user has finished any configuration.
141 ProfileSyncService* profile_sync_service = GetProfileSyncService();
142 if (profile_sync_service)
143 profile_sync_service->SetSetupInProgress(true);
145 // Make sure the syncing is not suppressed, otherwise the SigninManager
146 // will not be able to complete sucessfully.
147 sync_driver::SyncPrefs sync_prefs(profile_->GetPrefs());
148 sync_prefs.SetStartSuppressed(false);
151 void OneClickSigninSyncStarter::ConfirmSignin(const std::string& oauth_token) {
152 DCHECK(!oauth_token.empty());
153 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
154 // If this is a new signin (no authenticated username yet) try loading
155 // policy for this user now, before any signed in services are initialized.
156 if (signin->GetAuthenticatedUsername().empty()) {
157 #if defined(ENABLE_CONFIGURATION_POLICY)
158 policy::UserPolicySigninService* policy_service =
159 policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
160 policy_service->RegisterForPolicy(
161 signin->GetUsernameForAuthInProgress(),
163 base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy,
164 weak_pointer_factory_.GetWeakPtr()));
170 // The user is already signed in - just tell SigninManager to continue
171 // with its re-auth flow.
172 signin->CompletePendingSignin();
176 #if defined(ENABLE_CONFIGURATION_POLICY)
177 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
178 base::WeakPtr<OneClickSigninSyncStarter> sync_starter)
179 : sync_starter_(sync_starter) {
182 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
185 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
186 SetUserChoiceHistogram(SIGNIN_CHOICE_CANCEL);
187 if (sync_starter_ != NULL)
188 sync_starter_->CancelSigninAndDelete();
191 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
192 SetUserChoiceHistogram(SIGNIN_CHOICE_CONTINUE);
194 if (sync_starter_ != NULL)
195 sync_starter_->LoadPolicyWithCachedCredentials();
198 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
199 SetUserChoiceHistogram(SIGNIN_CHOICE_NEW_PROFILE);
201 if (sync_starter_ != NULL)
202 sync_starter_->CreateNewSignedInProfile();
205 void OneClickSigninSyncStarter::OnRegisteredForPolicy(
206 const std::string& dm_token, const std::string& client_id) {
207 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
208 // If there's no token for the user (policy registration did not succeed) just
209 // finish signing in.
210 if (dm_token.empty()) {
211 DVLOG(1) << "Policy registration failed";
216 DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token;
218 // Stash away a copy of our CloudPolicyClient (should not already have one).
219 DCHECK(dm_token_.empty());
220 DCHECK(client_id_.empty());
221 dm_token_ = dm_token;
222 client_id_ = client_id;
224 // Allow user to create a new profile before continuing with sign-in.
225 browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
226 content::WebContents* web_contents =
227 browser_->tab_strip_model()->GetActiveWebContents();
229 CancelSigninAndDelete();
232 chrome::ShowProfileSigninConfirmationDialog(
236 signin->GetUsernameForAuthInProgress(),
237 new SigninDialogDelegate(weak_pointer_factory_.GetWeakPtr()));
240 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() {
241 DCHECK(!dm_token_.empty());
242 DCHECK(!client_id_.empty());
243 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
244 policy::UserPolicySigninService* policy_service =
245 policy::UserPolicySigninServiceFactory::GetForProfile(profile_);
246 policy_service->FetchPolicyForSignedInUser(
247 signin->GetUsernameForAuthInProgress(),
250 profile_->GetRequestContext(),
251 base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete,
252 weak_pointer_factory_.GetWeakPtr()));
255 void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success) {
256 // For now, we allow signin to complete even if the policy fetch fails. If
257 // we ever want to change this behavior, we could call
258 // SigninManager::SignOut() here instead.
259 DLOG_IF(ERROR, !success) << "Error fetching policy for user";
260 DVLOG_IF(1, success) << "Policy fetch successful - completing signin";
261 SigninManagerFactory::GetForProfile(profile_)->CompletePendingSignin();
264 void OneClickSigninSyncStarter::CreateNewSignedInProfile() {
265 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
266 DCHECK(!signin->GetUsernameForAuthInProgress().empty());
267 DCHECK(!dm_token_.empty());
268 DCHECK(!client_id_.empty());
269 // Create a new profile and have it call back when done so we can inject our
270 // signin credentials.
271 size_t icon_index = g_browser_process->profile_manager()->
272 GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile();
273 ProfileManager::CreateMultiProfileAsync(
274 base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()),
275 base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl(icon_index)),
276 base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile,
277 weak_pointer_factory_.GetWeakPtr(), desktop_type_),
281 void OneClickSigninSyncStarter::CompleteInitForNewProfile(
282 chrome::HostDesktopType desktop_type,
283 Profile* new_profile,
284 Profile::CreateStatus status) {
285 DCHECK_NE(profile_, new_profile);
287 // TODO(atwilson): On error, unregister the client to release the DMToken
288 // and surface a better error for the user.
290 case Profile::CREATE_STATUS_LOCAL_FAIL: {
291 NOTREACHED() << "Error creating new profile";
292 CancelSigninAndDelete();
295 case Profile::CREATE_STATUS_CREATED: {
298 case Profile::CREATE_STATUS_INITIALIZED: {
299 // Wait until the profile is initialized before we transfer credentials.
300 SigninManager* old_signin_manager =
301 SigninManagerFactory::GetForProfile(profile_);
302 SigninManager* new_signin_manager =
303 SigninManagerFactory::GetForProfile(new_profile);
304 DCHECK(!old_signin_manager->GetUsernameForAuthInProgress().empty());
305 DCHECK(old_signin_manager->GetAuthenticatedUsername().empty());
306 DCHECK(new_signin_manager->GetAuthenticatedUsername().empty());
307 DCHECK(!dm_token_.empty());
308 DCHECK(!client_id_.empty());
310 // Copy credentials from the old profile to the just-created profile,
311 // and switch over to tracking that profile.
312 new_signin_manager->CopyCredentialsFrom(*old_signin_manager);
313 FinishProfileSyncServiceSetup();
314 Initialize(new_profile, NULL);
315 DCHECK_EQ(profile_, new_profile);
317 // We've transferred our credentials to the new profile - notify that
318 // the signin for the original profile was cancelled (must do this after
319 // we have called Initialize() with the new profile, as otherwise this
320 // object will get freed when the signin on the old profile is cancelled.
321 old_signin_manager->SignOut(signin_metrics::TRANSFER_CREDENTIALS);
323 // Load policy for the just-created profile - once policy has finished
324 // loading the signin process will complete.
325 LoadPolicyWithCachedCredentials();
327 // Open the profile's first window, after all initialization.
328 profiles::FindOrCreateNewWindowForProfile(
330 chrome::startup::IS_PROCESS_STARTUP,
331 chrome::startup::IS_FIRST_RUN,
336 case Profile::CREATE_STATUS_REMOTE_FAIL:
337 case Profile::CREATE_STATUS_CANCELED:
338 case Profile::MAX_CREATE_STATUS: {
339 NOTREACHED() << "Invalid profile creation status";
340 CancelSigninAndDelete();
347 void OneClickSigninSyncStarter::CancelSigninAndDelete() {
348 SigninManagerFactory::GetForProfile(profile_)->SignOut(
349 signin_metrics::ABORT_SIGNIN);
350 // The statement above results in a call to SigninFailed() which will free
351 // this object, so do not refer to the OneClickSigninSyncStarter object
355 void OneClickSigninSyncStarter::ConfirmAndSignin() {
356 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
357 if (confirmation_required_ == CONFIRM_UNTRUSTED_SIGNIN) {
358 browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
359 // Display a confirmation dialog to the user.
360 browser_->window()->ShowOneClickSigninBubble(
361 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG,
362 base::UTF8ToUTF16(signin->GetUsernameForAuthInProgress()),
363 base::string16(), // No error message to display.
364 base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed,
365 weak_pointer_factory_.GetWeakPtr()));
367 // No confirmation required - just sign in the user.
368 signin->CompletePendingSignin();
372 void OneClickSigninSyncStarter::UntrustedSigninConfirmed(
373 StartSyncMode response) {
374 if (response == UNDO_SYNC) {
375 CancelSigninAndDelete(); // This statement frees this object.
377 // If the user clicked the "Advanced" link in the confirmation dialog, then
378 // override the current start_mode_ to bring up the advanced sync settings.
380 // If the user signs in from the new avatar bubble, the untrusted dialog
381 // would dismiss the avatar bubble, thus it won't show any confirmation upon
382 // sign in completes. This dialog already has a settings link, thus we just
383 // start sync immediately .
385 if (response == CONFIGURE_SYNC_FIRST)
386 start_mode_ = response;
387 else if (start_mode_ == CONFIRM_SYNC_SETTINGS_FIRST)
388 start_mode_ = SYNC_WITH_DEFAULT_SETTINGS;
390 SigninManager* signin = SigninManagerFactory::GetForProfile(profile_);
391 signin->CompletePendingSignin();
395 void OneClickSigninSyncStarter::OnSyncConfirmationUIClosed(
396 bool configure_sync_first) {
397 if (configure_sync_first) {
398 chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
400 ProfileSyncService* profile_sync_service = GetProfileSyncService();
401 if (profile_sync_service)
402 profile_sync_service->SetSyncSetupCompleted();
403 FinishProfileSyncServiceSetup();
409 void OneClickSigninSyncStarter::SigninFailed(
410 const GoogleServiceAuthError& error) {
411 if (!sync_setup_completed_callback_.is_null())
412 sync_setup_completed_callback_.Run(SYNC_SETUP_FAILURE);
414 FinishProfileSyncServiceSetup();
415 if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) {
416 switch (error.state()) {
417 case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
418 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
419 IDS_SYNC_UNRECOVERABLE_ERROR));
421 case GoogleServiceAuthError::REQUEST_CANCELED:
422 // No error notification needed if the user manually cancelled signin.
425 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
426 IDS_SYNC_ERROR_SIGNING_IN));
433 void OneClickSigninSyncStarter::SigninSuccess() {
434 if (switches::IsEnableWebBasedSignin())
435 MergeSessionComplete(GoogleServiceAuthError(GoogleServiceAuthError::NONE));
438 void OneClickSigninSyncStarter::MergeSessionComplete(
439 const GoogleServiceAuthError& error) {
440 // Regardless of whether the merge session completed sucessfully or not,
441 // continue with sync starting.
443 if (!sync_setup_completed_callback_.is_null())
444 sync_setup_completed_callback_.Run(SYNC_SETUP_SUCCESS);
446 switch (start_mode_) {
447 case SYNC_WITH_DEFAULT_SETTINGS: {
448 // Just kick off the sync machine, no need to configure it first.
449 ProfileSyncService* profile_sync_service = GetProfileSyncService();
450 if (profile_sync_service)
451 profile_sync_service->SetSyncSetupCompleted();
452 FinishProfileSyncServiceSetup();
453 if (confirmation_required_ == CONFIRM_AFTER_SIGNIN) {
454 base::string16 message;
455 if (!profile_sync_service) {
456 // Sync is disabled by policy.
457 message = l10n_util::GetStringUTF16(
458 IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE);
460 DisplayFinalConfirmationBubble(message);
464 case CONFIRM_SYNC_SETTINGS_FIRST:
465 // Blocks sync until the sync settings confirmation UI is closed.
466 DisplayFinalConfirmationBubble(base::string16());
468 case CONFIGURE_SYNC_FIRST:
469 ShowSettingsPage(true); // Show sync config UI.
471 case SHOW_SETTINGS_WITHOUT_CONFIGURE:
472 ShowSettingsPage(false); // Don't show sync config UI.
478 // Navigate to the |continue_url_| if one is set, unless the user first needs
479 // to configure Sync.
480 if (web_contents() && !continue_url_.is_empty() &&
481 start_mode_ != CONFIGURE_SYNC_FIRST) {
488 void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble(
489 const base::string16& custom_message) {
490 browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
491 LoginUIServiceFactory::GetForProfile(browser_->profile())->
492 DisplayLoginResult(browser_, custom_message);
496 Browser* OneClickSigninSyncStarter::EnsureBrowser(
499 chrome::HostDesktopType desktop_type) {
501 // The user just created a new profile or has closed the browser that
502 // we used previously. Grab the most recently active browser or else
504 browser = chrome::FindLastActiveWithProfile(profile, desktop_type);
506 browser = new Browser(Browser::CreateParams(profile,
508 chrome::AddTabAt(browser, GURL(), -1, true);
510 browser->window()->Show();
515 void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync) {
516 // Give the user a chance to configure things. We don't clear the
517 // ProfileSyncService::setup_in_progress flag because we don't want sync
518 // to start up until after the configure UI is displayed (the configure UI
519 // will clear the flag when the user is done setting up sync).
520 ProfileSyncService* profile_sync_service = GetProfileSyncService();
521 LoginUIService* login_ui = LoginUIServiceFactory::GetForProfile(profile_);
522 if (login_ui->current_login_ui()) {
523 login_ui->current_login_ui()->FocusUI();
525 browser_ = EnsureBrowser(browser_, profile_, desktop_type_);
527 // If the sign in tab is showing the native signin page or the blank page
528 // for web-based flow, and is not about to be closed, use it to show the
530 bool use_same_tab = false;
531 if (web_contents()) {
532 GURL current_url = web_contents()->GetLastCommittedURL();
533 bool is_chrome_signin_url =
534 current_url.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL;
535 bool is_same_profile =
536 Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ==
539 (is_chrome_signin_url ||
540 signin::IsContinueUrlForWebBasedSigninFlow(current_url)) &&
541 !signin::IsAutoCloseEnabledInURL(current_url) &&
544 if (profile_sync_service) {
545 // Need to navigate to the settings page and display the sync UI.
547 ShowSettingsPageInWebContents(web_contents(),
548 chrome::kSyncSetupSubPage);
550 // If the user is setting up sync for the first time, let them configure
551 // advanced sync settings. However, in the case of re-authentication,
552 // return the user to the settings page without showing any config UI.
553 if (configure_sync) {
554 chrome::ShowSettingsSubPage(browser_, chrome::kSyncSetupSubPage);
556 FinishProfileSyncServiceSetup();
557 chrome::ShowSettings(browser_);
561 // Sync is disabled - just display the settings page or redirect to the
563 FinishProfileSyncServiceSetup();
565 chrome::ShowSettings(browser_);
566 else if (!continue_url_.is_empty())
569 ShowSettingsPageInWebContents(web_contents(), std::string());
574 ProfileSyncService* OneClickSigninSyncStarter::GetProfileSyncService() {
575 ProfileSyncService* service = NULL;
576 if (profile_->IsSyncAccessible())
577 service = ProfileSyncServiceFactory::GetForProfile(profile_);
581 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
582 ProfileSyncService* service =
583 ProfileSyncServiceFactory::GetForProfile(profile_);
585 service->SetSetupInProgress(false);
588 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents(
589 content::WebContents* contents,
590 const std::string& sub_page) {
591 if (!continue_url_.is_empty()) {
592 // The observer deletes itself once it's done.
593 DCHECK(!sub_page.empty());
594 new OneClickSigninSyncObserver(contents, continue_url_);
597 GURL url = chrome::GetSettingsUrl(sub_page);
598 content::OpenURLParams params(url,
601 content::PAGE_TRANSITION_AUTO_TOPLEVEL,
603 contents->OpenURL(params);
606 Browser* browser = chrome::FindBrowserWithWebContents(contents);
608 browser->tab_strip_model()->GetIndexOfWebContents(contents);
609 browser->tab_strip_model()->ActivateTabAt(content_index,
610 false /* user_gesture */);
613 void OneClickSigninSyncStarter::LoadContinueUrl() {
614 web_contents()->GetController().LoadURL(
617 content::PAGE_TRANSITION_AUTO_TOPLEVEL,