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_finder.h"
29 #include "chrome/browser/ui/browser_list.h"
30 #include "chrome/browser/ui/browser_navigator.h"
31 #include "chrome/browser/ui/browser_tabstrip.h"
32 #include "chrome/browser/ui/browser_window.h"
33 #include "chrome/browser/ui/chrome_pages.h"
34 #include "chrome/browser/ui/sync/one_click_signin_sync_observer.h"
35 #include "chrome/browser/ui/tab_dialogs.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/common/url_constants.h"
39 #include "chrome/grit/chromium_strings.h"
40 #include "chrome/grit/generated_resources.h"
41 #include "components/signin/core/browser/signin_manager.h"
42 #include "components/signin/core/browser/signin_metrics.h"
43 #include "components/sync_driver/sync_prefs.h"
44 #include "net/url_request/url_request_context_getter.h"
45 #include "ui/base/l10n/l10n_util.h"
49 // UMA histogram for tracking what users do when presented with the signin
52 // (a) existing enumerated constants should never be deleted or reordered, and
53 // (b) new constants should only be appended at the end of the enumeration.
55 // Keep this in sync with SigninChoice in histograms.xml.
57 SIGNIN_CHOICE_CANCEL
= 0,
58 SIGNIN_CHOICE_CONTINUE
= 1,
59 SIGNIN_CHOICE_NEW_PROFILE
= 2,
60 // SIGNIN_CHOICE_SIZE should always be last - this is a count of the number
61 // of items in this enum.
65 void SetUserChoiceHistogram(SigninChoice choice
) {
66 UMA_HISTOGRAM_ENUMERATION("Enterprise.UserSigninChoice",
73 OneClickSigninSyncStarter::OneClickSigninSyncStarter(
76 const std::string
& email
,
77 const std::string
& password
,
78 const std::string
& refresh_token
,
79 StartSyncMode start_mode
,
80 content::WebContents
* web_contents
,
81 ConfirmationRequired confirmation_required
,
82 const GURL
& continue_url
,
83 Callback sync_setup_completed_callback
)
84 : content::WebContentsObserver(web_contents
),
86 start_mode_(start_mode
),
87 desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE
),
88 confirmation_required_(confirmation_required
),
89 continue_url_(continue_url
),
90 sync_setup_completed_callback_(sync_setup_completed_callback
),
91 weak_pointer_factory_(this) {
93 DCHECK(web_contents
|| continue_url
.is_empty());
94 BrowserList::AddObserver(this);
95 Initialize(profile
, browser
);
97 // Policy is enabled, so pass in a callback to do extra policy-related UI
98 // before signin completes.
99 SigninManagerFactory::GetForProfile(profile_
)->
100 StartSignInWithRefreshToken(
101 refresh_token
, email
, password
,
102 base::Bind(&OneClickSigninSyncStarter::ConfirmSignin
,
103 weak_pointer_factory_
.GetWeakPtr()));
106 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser
* browser
) {
107 if (browser
== browser_
)
111 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
112 BrowserList::RemoveObserver(this);
113 LoginUIServiceFactory::GetForProfile(profile_
)->RemoveObserver(this);
116 void OneClickSigninSyncStarter::Initialize(Profile
* profile
, Browser
* browser
) {
120 LoginUIServiceFactory::GetForProfile(profile_
)->RemoveObserver(this);
125 LoginUIServiceFactory::GetForProfile(profile_
)->AddObserver(this);
127 // Cache the parent desktop for the browser, so we can reuse that same
128 // desktop for any UI we want to display.
130 desktop_type_
= browser
->host_desktop_type();
132 desktop_type_
= chrome::GetActiveDesktop();
135 signin_tracker_
= SigninTrackerFactory::CreateForProfile(profile_
, this);
137 // Let the sync service know that setup is in progress so it doesn't start
138 // syncing until the user has finished any configuration.
139 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
140 if (profile_sync_service
)
141 profile_sync_service
->SetSetupInProgress(true);
143 // Make sure the syncing is not suppressed, otherwise the SigninManager
144 // will not be able to complete sucessfully.
145 sync_driver::SyncPrefs
sync_prefs(profile_
->GetPrefs());
146 sync_prefs
.SetStartSuppressed(false);
149 void OneClickSigninSyncStarter::ConfirmSignin(const std::string
& oauth_token
) {
150 DCHECK(!oauth_token
.empty());
151 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
152 // If this is a new signin (no authenticated username yet) try loading
153 // policy for this user now, before any signed in services are initialized.
154 if (!signin
->IsAuthenticated()) {
155 #if defined(ENABLE_CONFIGURATION_POLICY)
156 policy::UserPolicySigninService
* policy_service
=
157 policy::UserPolicySigninServiceFactory::GetForProfile(profile_
);
158 policy_service
->RegisterForPolicy(
159 signin
->GetUsernameForAuthInProgress(),
161 base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy
,
162 weak_pointer_factory_
.GetWeakPtr()));
168 // The user is already signed in - just tell SigninManager to continue
169 // with its re-auth flow.
170 signin
->CompletePendingSignin();
174 #if defined(ENABLE_CONFIGURATION_POLICY)
175 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
176 base::WeakPtr
<OneClickSigninSyncStarter
> sync_starter
)
177 : sync_starter_(sync_starter
) {
180 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
183 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
184 SetUserChoiceHistogram(SIGNIN_CHOICE_CANCEL
);
185 if (sync_starter_
!= NULL
)
186 sync_starter_
->CancelSigninAndDelete();
189 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
190 SetUserChoiceHistogram(SIGNIN_CHOICE_CONTINUE
);
192 if (sync_starter_
!= NULL
)
193 sync_starter_
->LoadPolicyWithCachedCredentials();
196 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
197 SetUserChoiceHistogram(SIGNIN_CHOICE_NEW_PROFILE
);
199 if (sync_starter_
!= NULL
)
200 sync_starter_
->CreateNewSignedInProfile();
203 void OneClickSigninSyncStarter::OnRegisteredForPolicy(
204 const std::string
& dm_token
, const std::string
& client_id
) {
205 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
206 // If there's no token for the user (policy registration did not succeed) just
207 // finish signing in.
208 if (dm_token
.empty()) {
209 DVLOG(1) << "Policy registration failed";
214 DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token
;
216 // Stash away a copy of our CloudPolicyClient (should not already have one).
217 DCHECK(dm_token_
.empty());
218 DCHECK(client_id_
.empty());
219 dm_token_
= dm_token
;
220 client_id_
= client_id
;
222 // Allow user to create a new profile before continuing with sign-in.
223 browser_
= EnsureBrowser(browser_
, profile_
, desktop_type_
);
224 content::WebContents
* web_contents
=
225 browser_
->tab_strip_model()->GetActiveWebContents();
227 CancelSigninAndDelete();
230 TabDialogs::FromWebContents(web_contents
)->ShowProfileSigninConfirmation(
233 signin
->GetUsernameForAuthInProgress(),
234 new SigninDialogDelegate(weak_pointer_factory_
.GetWeakPtr()));
237 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() {
238 DCHECK(!dm_token_
.empty());
239 DCHECK(!client_id_
.empty());
240 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
241 policy::UserPolicySigninService
* policy_service
=
242 policy::UserPolicySigninServiceFactory::GetForProfile(profile_
);
243 policy_service
->FetchPolicyForSignedInUser(
244 signin
->GetUsernameForAuthInProgress(),
247 profile_
->GetRequestContext(),
248 base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete
,
249 weak_pointer_factory_
.GetWeakPtr()));
252 void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success
) {
253 // For now, we allow signin to complete even if the policy fetch fails. If
254 // we ever want to change this behavior, we could call
255 // SigninManager::SignOut() here instead.
256 DLOG_IF(ERROR
, !success
) << "Error fetching policy for user";
257 DVLOG_IF(1, success
) << "Policy fetch successful - completing signin";
258 SigninManagerFactory::GetForProfile(profile_
)->CompletePendingSignin();
261 void OneClickSigninSyncStarter::CreateNewSignedInProfile() {
262 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
263 DCHECK(!signin
->GetUsernameForAuthInProgress().empty());
264 DCHECK(!dm_token_
.empty());
265 DCHECK(!client_id_
.empty());
266 // Create a new profile and have it call back when done so we can inject our
267 // signin credentials.
268 size_t icon_index
= g_browser_process
->profile_manager()->
269 GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile();
270 ProfileManager::CreateMultiProfileAsync(
271 base::UTF8ToUTF16(signin
->GetUsernameForAuthInProgress()),
272 base::UTF8ToUTF16(profiles::GetDefaultAvatarIconUrl(icon_index
)),
273 base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile
,
274 weak_pointer_factory_
.GetWeakPtr(), desktop_type_
),
278 void OneClickSigninSyncStarter::CompleteInitForNewProfile(
279 chrome::HostDesktopType desktop_type
,
280 Profile
* new_profile
,
281 Profile::CreateStatus status
) {
282 DCHECK_NE(profile_
, new_profile
);
284 // TODO(atwilson): On error, unregister the client to release the DMToken
285 // and surface a better error for the user.
287 case Profile::CREATE_STATUS_LOCAL_FAIL
: {
288 NOTREACHED() << "Error creating new profile";
289 CancelSigninAndDelete();
292 case Profile::CREATE_STATUS_CREATED
: {
295 case Profile::CREATE_STATUS_INITIALIZED
: {
296 // Wait until the profile is initialized before we transfer credentials.
297 SigninManager
* old_signin_manager
=
298 SigninManagerFactory::GetForProfile(profile_
);
299 SigninManager
* new_signin_manager
=
300 SigninManagerFactory::GetForProfile(new_profile
);
301 DCHECK(!old_signin_manager
->GetUsernameForAuthInProgress().empty());
302 DCHECK(!old_signin_manager
->IsAuthenticated());
303 DCHECK(!new_signin_manager
->IsAuthenticated());
304 DCHECK(!dm_token_
.empty());
305 DCHECK(!client_id_
.empty());
307 // Copy credentials from the old profile to the just-created profile,
308 // and switch over to tracking that profile.
309 new_signin_manager
->CopyCredentialsFrom(*old_signin_manager
);
310 FinishProfileSyncServiceSetup();
311 Initialize(new_profile
, NULL
);
312 DCHECK_EQ(profile_
, new_profile
);
314 // We've transferred our credentials to the new profile - notify that
315 // the signin for the original profile was cancelled (must do this after
316 // we have called Initialize() with the new profile, as otherwise this
317 // object will get freed when the signin on the old profile is cancelled.
318 old_signin_manager
->SignOut(signin_metrics::TRANSFER_CREDENTIALS
);
320 // Load policy for the just-created profile - once policy has finished
321 // loading the signin process will complete.
322 LoadPolicyWithCachedCredentials();
324 // Open the profile's first window, after all initialization.
325 profiles::FindOrCreateNewWindowForProfile(
327 chrome::startup::IS_PROCESS_STARTUP
,
328 chrome::startup::IS_FIRST_RUN
,
333 case Profile::CREATE_STATUS_REMOTE_FAIL
:
334 case Profile::CREATE_STATUS_CANCELED
:
335 case Profile::MAX_CREATE_STATUS
: {
336 NOTREACHED() << "Invalid profile creation status";
337 CancelSigninAndDelete();
344 void OneClickSigninSyncStarter::CancelSigninAndDelete() {
345 SigninManagerFactory::GetForProfile(profile_
)->SignOut(
346 signin_metrics::ABORT_SIGNIN
);
347 // The statement above results in a call to SigninFailed() which will free
348 // this object, so do not refer to the OneClickSigninSyncStarter object
352 void OneClickSigninSyncStarter::ConfirmAndSignin() {
353 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
354 if (confirmation_required_
== CONFIRM_UNTRUSTED_SIGNIN
) {
355 browser_
= EnsureBrowser(browser_
, profile_
, desktop_type_
);
356 // Display a confirmation dialog to the user.
357 browser_
->window()->ShowOneClickSigninBubble(
358 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG
,
359 base::UTF8ToUTF16(signin
->GetUsernameForAuthInProgress()),
360 base::string16(), // No error message to display.
361 base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed
,
362 weak_pointer_factory_
.GetWeakPtr()));
363 LoginUIServiceFactory::GetForProfile(profile_
)->UntrustedLoginUIShown();
365 // No confirmation required - just sign in the user.
366 signin
->CompletePendingSignin();
370 void OneClickSigninSyncStarter::UntrustedSigninConfirmed(
371 StartSyncMode response
) {
372 if (response
== UNDO_SYNC
) {
373 CancelSigninAndDelete(); // This statement frees this object.
375 // If the user clicked the "Advanced" link in the confirmation dialog, then
376 // override the current start_mode_ to bring up the advanced sync settings.
378 // If the user signs in from the new avatar bubble, the untrusted dialog
379 // would dismiss the avatar bubble, thus it won't show any confirmation upon
380 // sign in completes. This dialog already has a settings link, thus we just
381 // start sync immediately .
383 if (response
== CONFIGURE_SYNC_FIRST
)
384 start_mode_
= response
;
385 else if (start_mode_
== CONFIRM_SYNC_SETTINGS_FIRST
)
386 start_mode_
= SYNC_WITH_DEFAULT_SETTINGS
;
388 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
389 signin
->CompletePendingSignin();
393 void OneClickSigninSyncStarter::OnSyncConfirmationUIClosed(
394 bool configure_sync_first
) {
395 if (configure_sync_first
) {
396 chrome::ShowSettingsSubPage(browser_
, chrome::kSyncSetupSubPage
);
398 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
399 if (profile_sync_service
)
400 profile_sync_service
->SetSyncSetupCompleted();
401 FinishProfileSyncServiceSetup();
407 void OneClickSigninSyncStarter::SigninFailed(
408 const GoogleServiceAuthError
& error
) {
409 if (!sync_setup_completed_callback_
.is_null())
410 sync_setup_completed_callback_
.Run(SYNC_SETUP_FAILURE
);
412 FinishProfileSyncServiceSetup();
413 if (confirmation_required_
== CONFIRM_AFTER_SIGNIN
) {
414 switch (error
.state()) {
415 case GoogleServiceAuthError::SERVICE_UNAVAILABLE
:
416 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
417 IDS_SYNC_UNRECOVERABLE_ERROR
));
419 case GoogleServiceAuthError::REQUEST_CANCELED
:
420 // No error notification needed if the user manually cancelled signin.
423 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
424 IDS_SYNC_ERROR_SIGNING_IN
));
431 void OneClickSigninSyncStarter::SigninSuccess() {
434 void OneClickSigninSyncStarter::MergeSessionComplete(
435 const GoogleServiceAuthError
& error
) {
436 // Regardless of whether the merge session completed sucessfully or not,
437 // continue with sync starting.
439 if (!sync_setup_completed_callback_
.is_null())
440 sync_setup_completed_callback_
.Run(SYNC_SETUP_SUCCESS
);
442 switch (start_mode_
) {
443 case SYNC_WITH_DEFAULT_SETTINGS
: {
444 // Just kick off the sync machine, no need to configure it first.
445 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
446 if (profile_sync_service
)
447 profile_sync_service
->SetSyncSetupCompleted();
448 FinishProfileSyncServiceSetup();
449 if (confirmation_required_
== CONFIRM_AFTER_SIGNIN
) {
450 base::string16 message
;
451 if (!profile_sync_service
) {
452 // Sync is disabled by policy.
453 message
= l10n_util::GetStringUTF16(
454 IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE
);
456 DisplayFinalConfirmationBubble(message
);
460 case CONFIRM_SYNC_SETTINGS_FIRST
:
461 // Blocks sync until the sync settings confirmation UI is closed.
462 DisplayFinalConfirmationBubble(base::string16());
464 case CONFIGURE_SYNC_FIRST
:
465 ShowSettingsPage(true); // Show sync config UI.
467 case SHOW_SETTINGS_WITHOUT_CONFIGURE
:
468 ShowSettingsPage(false); // Don't show sync config UI.
474 // Navigate to the |continue_url_| if one is set, unless the user first needs
475 // to configure Sync.
476 if (web_contents() && !continue_url_
.is_empty() &&
477 start_mode_
!= CONFIGURE_SYNC_FIRST
) {
484 void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble(
485 const base::string16
& custom_message
) {
486 browser_
= EnsureBrowser(browser_
, profile_
, desktop_type_
);
487 LoginUIServiceFactory::GetForProfile(browser_
->profile())->
488 DisplayLoginResult(browser_
, custom_message
);
492 Browser
* OneClickSigninSyncStarter::EnsureBrowser(
495 chrome::HostDesktopType desktop_type
) {
497 // The user just created a new profile or has closed the browser that
498 // we used previously. Grab the most recently active browser or else
500 browser
= chrome::FindLastActiveWithProfile(profile
, desktop_type
);
502 browser
= new Browser(Browser::CreateParams(profile
,
504 chrome::AddTabAt(browser
, GURL(), -1, true);
506 browser
->window()->Show();
511 void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync
) {
512 // Give the user a chance to configure things. We don't clear the
513 // ProfileSyncService::setup_in_progress flag because we don't want sync
514 // to start up until after the configure UI is displayed (the configure UI
515 // will clear the flag when the user is done setting up sync).
516 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
517 LoginUIService
* login_ui
= LoginUIServiceFactory::GetForProfile(profile_
);
518 if (login_ui
->current_login_ui()) {
519 login_ui
->current_login_ui()->FocusUI();
521 browser_
= EnsureBrowser(browser_
, profile_
, desktop_type_
);
523 // If the sign in tab is showing the native signin page or the blank page
524 // for web-based flow, and is not about to be closed, use it to show the
526 bool use_same_tab
= false;
527 if (web_contents()) {
528 GURL current_url
= web_contents()->GetLastCommittedURL();
529 bool is_chrome_signin_url
=
530 current_url
.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL
;
531 bool is_same_profile
=
532 Profile::FromBrowserContext(web_contents()->GetBrowserContext()) ==
535 is_chrome_signin_url
&&
536 !signin::IsAutoCloseEnabledInURL(current_url
) &&
539 if (profile_sync_service
) {
540 // Need to navigate to the settings page and display the sync UI.
542 ShowSettingsPageInWebContents(web_contents(),
543 chrome::kSyncSetupSubPage
);
545 // If the user is setting up sync for the first time, let them configure
546 // advanced sync settings. However, in the case of re-authentication,
547 // return the user to the settings page without showing any config UI.
548 if (configure_sync
) {
549 chrome::ShowSettingsSubPage(browser_
, chrome::kSyncSetupSubPage
);
551 FinishProfileSyncServiceSetup();
552 chrome::ShowSettings(browser_
);
556 // Sync is disabled - just display the settings page or redirect to the
558 FinishProfileSyncServiceSetup();
560 chrome::ShowSettings(browser_
);
561 else if (!continue_url_
.is_empty())
564 ShowSettingsPageInWebContents(web_contents(), std::string());
569 ProfileSyncService
* OneClickSigninSyncStarter::GetProfileSyncService() {
570 ProfileSyncService
* service
= NULL
;
571 if (profile_
->IsSyncAccessible())
572 service
= ProfileSyncServiceFactory::GetForProfile(profile_
);
576 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
577 ProfileSyncService
* service
=
578 ProfileSyncServiceFactory::GetForProfile(profile_
);
580 service
->SetSetupInProgress(false);
583 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents(
584 content::WebContents
* contents
,
585 const std::string
& sub_page
) {
586 if (!continue_url_
.is_empty()) {
587 // The observer deletes itself once it's done.
588 DCHECK(!sub_page
.empty());
589 new OneClickSigninSyncObserver(contents
, continue_url_
);
592 GURL url
= chrome::GetSettingsUrl(sub_page
);
593 content::OpenURLParams
params(url
,
596 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,
598 contents
->OpenURL(params
);
601 Browser
* browser
= chrome::FindBrowserWithWebContents(contents
);
603 browser
->tab_strip_model()->GetIndexOfWebContents(contents
);
604 browser
->tab_strip_model()->ActivateTabAt(content_index
,
605 false /* user_gesture */);
608 void OneClickSigninSyncStarter::LoadContinueUrl() {
609 web_contents()->GetController().LoadURL(
612 ui::PAGE_TRANSITION_AUTO_TOPLEVEL
,