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/prefs/pref_service.h"
8 #include "base/strings/utf_string_conversions.h"
9 #include "chrome/browser/browser_process.h"
11 #if defined(ENABLE_CONFIGURATION_POLICY)
12 #include "chrome/browser/policy/cloud/user_policy_signin_service.h"
13 #include "chrome/browser/policy/cloud/user_policy_signin_service_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/profiles/profile_info_cache.h"
18 #include "chrome/browser/profiles/profile_io_data.h"
19 #include "chrome/browser/profiles/profile_manager.h"
20 #include "chrome/browser/profiles/profile_window.h"
21 #include "chrome/browser/signin/signin_manager.h"
22 #include "chrome/browser/signin/signin_manager_factory.h"
23 #include "chrome/browser/sync/profile_sync_service.h"
24 #include "chrome/browser/sync/profile_sync_service_factory.h"
25 #include "chrome/browser/sync/sync_prefs.h"
26 #include "chrome/browser/ui/browser.h"
27 #include "chrome/browser/ui/browser_dialogs.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/tabs/tab_strip_model.h"
35 #include "chrome/browser/ui/webui/signin/login_ui_service.h"
36 #include "chrome/browser/ui/webui/signin/login_ui_service_factory.h"
37 #include "chrome/browser/ui/webui/signin/profile_signin_confirmation_dialog.h"
38 #include "chrome/common/profile_management_switches.h"
39 #include "chrome/common/url_constants.h"
40 #include "grit/chromium_strings.h"
41 #include "grit/generated_resources.h"
42 #include "ui/base/l10n/l10n_util.h"
43 #include "ui/base/resource/resource_bundle.h"
45 OneClickSigninSyncStarter::OneClickSigninSyncStarter(
48 const std::string
& session_index
,
49 const std::string
& email
,
50 const std::string
& password
,
51 const std::string
& oauth_code
,
52 StartSyncMode start_mode
,
53 content::WebContents
* web_contents
,
54 ConfirmationRequired confirmation_required
,
55 Callback sync_setup_completed_callback
)
56 : content::WebContentsObserver(web_contents
),
57 start_mode_(start_mode
),
58 desktop_type_(chrome::HOST_DESKTOP_TYPE_NATIVE
),
59 confirmation_required_(confirmation_required
),
60 sync_setup_completed_callback_(sync_setup_completed_callback
),
61 weak_pointer_factory_(this) {
63 BrowserList::AddObserver(this);
65 Initialize(profile
, browser
);
67 // If oauth_code is supplied, then start the sign in process using the
68 // oauth_code; otherwise start the signin process using the cookies in the
70 SigninManager
* manager
= SigninManagerFactory::GetForProfile(profile_
);
71 SigninManager::OAuthTokenFetchedCallback callback
;
72 // Policy is enabled, so pass in a callback to do extra policy-related UI
73 // before signin completes.
74 callback
= base::Bind(&OneClickSigninSyncStarter::ConfirmSignin
,
75 weak_pointer_factory_
.GetWeakPtr());
76 if (oauth_code
.empty()) {
77 manager
->StartSignInWithCredentials(
78 session_index
, email
, password
, callback
);
80 manager
->StartSignInWithOAuthCode(email
, password
, oauth_code
, callback
);
84 void OneClickSigninSyncStarter::OnBrowserRemoved(Browser
* browser
) {
85 if (browser
== browser_
)
89 OneClickSigninSyncStarter::~OneClickSigninSyncStarter() {
90 BrowserList::RemoveObserver(this);
93 void OneClickSigninSyncStarter::Initialize(Profile
* profile
, Browser
* browser
) {
98 // Cache the parent desktop for the browser, so we can reuse that same
99 // desktop for any UI we want to display.
101 desktop_type_
= browser
->host_desktop_type();
103 signin_tracker_
.reset(new SigninTracker(profile_
, this));
105 // Let the sync service know that setup is in progress so it doesn't start
106 // syncing until the user has finished any configuration.
107 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
108 if (profile_sync_service
)
109 profile_sync_service
->SetSetupInProgress(true);
111 // Make sure the syncing is not suppressed, otherwise the SigninManager
112 // will not be able to complete sucessfully.
113 browser_sync::SyncPrefs
sync_prefs(profile_
->GetPrefs());
114 sync_prefs
.SetStartSuppressed(false);
117 void OneClickSigninSyncStarter::ConfirmSignin(const std::string
& oauth_token
) {
118 DCHECK(!oauth_token
.empty());
119 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
120 // If this is a new signin (no authenticated username yet) try loading
121 // policy for this user now, before any signed in services are initialized.
122 if (signin
->GetAuthenticatedUsername().empty()) {
123 #if defined(ENABLE_CONFIGURATION_POLICY)
124 policy::UserPolicySigninService
* policy_service
=
125 policy::UserPolicySigninServiceFactory::GetForProfile(profile_
);
126 policy_service
->RegisterForPolicy(
127 signin
->GetUsernameForAuthInProgress(),
129 base::Bind(&OneClickSigninSyncStarter::OnRegisteredForPolicy
,
130 weak_pointer_factory_
.GetWeakPtr()));
136 // The user is already signed in - just tell SigninManager to continue
137 // with its re-auth flow.
138 signin
->CompletePendingSignin();
142 #if defined(ENABLE_CONFIGURATION_POLICY)
143 OneClickSigninSyncStarter::SigninDialogDelegate::SigninDialogDelegate(
144 base::WeakPtr
<OneClickSigninSyncStarter
> sync_starter
)
145 : sync_starter_(sync_starter
) {
148 OneClickSigninSyncStarter::SigninDialogDelegate::~SigninDialogDelegate() {
151 void OneClickSigninSyncStarter::SigninDialogDelegate::OnCancelSignin() {
152 if (sync_starter_
!= NULL
)
153 sync_starter_
->CancelSigninAndDelete();
156 void OneClickSigninSyncStarter::SigninDialogDelegate::OnContinueSignin() {
157 if (sync_starter_
!= NULL
)
158 sync_starter_
->LoadPolicyWithCachedCredentials();
161 void OneClickSigninSyncStarter::SigninDialogDelegate::OnSigninWithNewProfile() {
162 if (sync_starter_
!= NULL
)
163 sync_starter_
->CreateNewSignedInProfile();
166 void OneClickSigninSyncStarter::OnRegisteredForPolicy(
167 const std::string
& dm_token
, const std::string
& client_id
) {
168 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
169 // If there's no token for the user (policy registration did not succeed) just
170 // finish signing in.
171 if (dm_token
.empty()) {
172 DVLOG(1) << "Policy registration failed";
177 DVLOG(1) << "Policy registration succeeded: dm_token=" << dm_token
;
179 // Stash away a copy of our CloudPolicyClient (should not already have one).
180 DCHECK(dm_token_
.empty());
181 DCHECK(client_id_
.empty());
182 dm_token_
= dm_token
;
183 client_id_
= client_id
;
185 // Allow user to create a new profile before continuing with sign-in.
187 content::WebContents
* web_contents
=
188 browser_
->tab_strip_model()->GetActiveWebContents();
190 CancelSigninAndDelete();
193 chrome::ShowProfileSigninConfirmationDialog(
197 signin
->GetUsernameForAuthInProgress(),
198 new SigninDialogDelegate(weak_pointer_factory_
.GetWeakPtr()));
201 void OneClickSigninSyncStarter::LoadPolicyWithCachedCredentials() {
202 DCHECK(!dm_token_
.empty());
203 DCHECK(!client_id_
.empty());
204 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
205 policy::UserPolicySigninService
* policy_service
=
206 policy::UserPolicySigninServiceFactory::GetForProfile(profile_
);
207 policy_service
->FetchPolicyForSignedInUser(
208 signin
->GetUsernameForAuthInProgress(),
211 profile_
->GetRequestContext(),
212 base::Bind(&OneClickSigninSyncStarter::OnPolicyFetchComplete
,
213 weak_pointer_factory_
.GetWeakPtr()));
216 void OneClickSigninSyncStarter::OnPolicyFetchComplete(bool success
) {
217 // For now, we allow signin to complete even if the policy fetch fails. If
218 // we ever want to change this behavior, we could call
219 // SigninManager::SignOut() here instead.
220 DLOG_IF(ERROR
, !success
) << "Error fetching policy for user";
221 DVLOG_IF(1, success
) << "Policy fetch successful - completing signin";
222 SigninManagerFactory::GetForProfile(profile_
)->CompletePendingSignin();
225 void OneClickSigninSyncStarter::CreateNewSignedInProfile() {
226 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
227 DCHECK(!signin
->GetUsernameForAuthInProgress().empty());
228 DCHECK(!dm_token_
.empty());
229 DCHECK(!client_id_
.empty());
230 // Create a new profile and have it call back when done so we can inject our
231 // signin credentials.
232 size_t icon_index
= g_browser_process
->profile_manager()->
233 GetProfileInfoCache().ChooseAvatarIconIndexForNewProfile();
234 ProfileManager::CreateMultiProfileAsync(
235 base::UTF8ToUTF16(signin
->GetUsernameForAuthInProgress()),
236 base::UTF8ToUTF16(ProfileInfoCache::GetDefaultAvatarIconUrl(icon_index
)),
237 base::Bind(&OneClickSigninSyncStarter::CompleteInitForNewProfile
,
238 weak_pointer_factory_
.GetWeakPtr(), desktop_type_
),
242 void OneClickSigninSyncStarter::CompleteInitForNewProfile(
243 chrome::HostDesktopType desktop_type
,
244 Profile
* new_profile
,
245 Profile::CreateStatus status
) {
246 DCHECK_NE(profile_
, new_profile
);
248 // TODO(atwilson): On error, unregister the client to release the DMToken
249 // and surface a better error for the user.
251 case Profile::CREATE_STATUS_LOCAL_FAIL
: {
252 NOTREACHED() << "Error creating new profile";
253 CancelSigninAndDelete();
256 case Profile::CREATE_STATUS_CREATED
: {
259 case Profile::CREATE_STATUS_INITIALIZED
: {
260 // Wait until the profile is initialized before we transfer credentials.
261 SigninManager
* old_signin_manager
=
262 SigninManagerFactory::GetForProfile(profile_
);
263 SigninManager
* new_signin_manager
=
264 SigninManagerFactory::GetForProfile(new_profile
);
265 DCHECK(!old_signin_manager
->GetUsernameForAuthInProgress().empty());
266 DCHECK(old_signin_manager
->GetAuthenticatedUsername().empty());
267 DCHECK(new_signin_manager
->GetAuthenticatedUsername().empty());
268 DCHECK(!dm_token_
.empty());
269 DCHECK(!client_id_
.empty());
271 // Copy credentials from the old profile to the just-created profile,
272 // and switch over to tracking that profile.
273 new_signin_manager
->CopyCredentialsFrom(*old_signin_manager
);
274 FinishProfileSyncServiceSetup();
275 Initialize(new_profile
, NULL
);
276 DCHECK_EQ(profile_
, new_profile
);
278 // We've transferred our credentials to the new profile - notify that
279 // the signin for the original profile was cancelled (must do this after
280 // we have called Initialize() with the new profile, as otherwise this
281 // object will get freed when the signin on the old profile is cancelled.
282 old_signin_manager
->SignOut();
284 // Load policy for the just-created profile - once policy has finished
285 // loading the signin process will complete.
286 LoadPolicyWithCachedCredentials();
288 // Open the profile's first window, after all initialization.
289 profiles::FindOrCreateNewWindowForProfile(
291 chrome::startup::IS_PROCESS_STARTUP
,
292 chrome::startup::IS_FIRST_RUN
,
297 case Profile::CREATE_STATUS_REMOTE_FAIL
:
298 case Profile::CREATE_STATUS_CANCELED
:
299 case Profile::MAX_CREATE_STATUS
: {
300 NOTREACHED() << "Invalid profile creation status";
301 CancelSigninAndDelete();
308 void OneClickSigninSyncStarter::CancelSigninAndDelete() {
309 SigninManagerFactory::GetForProfile(profile_
)->SignOut();
310 // The statement above results in a call to SigninFailed() which will free
311 // this object, so do not refer to the OneClickSigninSyncStarter object
315 void OneClickSigninSyncStarter::ConfirmAndSignin() {
316 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
317 if (confirmation_required_
== CONFIRM_UNTRUSTED_SIGNIN
) {
319 // Display a confirmation dialog to the user.
320 browser_
->window()->ShowOneClickSigninBubble(
321 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_SAML_MODAL_DIALOG
,
322 base::UTF8ToUTF16(signin
->GetUsernameForAuthInProgress()),
323 base::string16(), // No error message to display.
324 base::Bind(&OneClickSigninSyncStarter::UntrustedSigninConfirmed
,
325 weak_pointer_factory_
.GetWeakPtr()));
327 // No confirmation required - just sign in the user.
328 signin
->CompletePendingSignin();
332 void OneClickSigninSyncStarter::UntrustedSigninConfirmed(
333 StartSyncMode response
) {
334 if (response
== UNDO_SYNC
) {
335 CancelSigninAndDelete(); // This statement frees this object.
337 // If the user clicked the "Advanced" link in the confirmation dialog, then
338 // override the current start_mode_ to bring up the advanced sync settings.
339 if (response
== CONFIGURE_SYNC_FIRST
)
340 start_mode_
= response
;
341 SigninManager
* signin
= SigninManagerFactory::GetForProfile(profile_
);
342 signin
->CompletePendingSignin();
346 void OneClickSigninSyncStarter::SigninFailed(
347 const GoogleServiceAuthError
& error
) {
348 if (!sync_setup_completed_callback_
.is_null())
349 sync_setup_completed_callback_
.Run(SYNC_SETUP_FAILURE
);
351 FinishProfileSyncServiceSetup();
352 if (confirmation_required_
== CONFIRM_AFTER_SIGNIN
) {
353 switch (error
.state()) {
354 case GoogleServiceAuthError::SERVICE_UNAVAILABLE
:
355 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
356 IDS_SYNC_UNRECOVERABLE_ERROR
));
358 case GoogleServiceAuthError::REQUEST_CANCELED
:
359 // No error notification needed if the user manually cancelled signin.
362 DisplayFinalConfirmationBubble(l10n_util::GetStringUTF16(
363 IDS_SYNC_ERROR_SIGNING_IN
));
370 void OneClickSigninSyncStarter::SigninSuccess() {
371 if (switches::IsEnableWebBasedSignin())
372 MergeSessionComplete(GoogleServiceAuthError(GoogleServiceAuthError::NONE
));
375 void OneClickSigninSyncStarter::MergeSessionComplete(
376 const GoogleServiceAuthError
& error
) {
377 // Regardless of whether the merge session completed sucessfully or not,
378 // continue with sync starting.
380 if (!sync_setup_completed_callback_
.is_null())
381 sync_setup_completed_callback_
.Run(SYNC_SETUP_SUCCESS
);
383 switch (start_mode_
) {
384 case SYNC_WITH_DEFAULT_SETTINGS
: {
385 // Just kick off the sync machine, no need to configure it first.
386 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
387 if (profile_sync_service
)
388 profile_sync_service
->SetSyncSetupCompleted();
389 FinishProfileSyncServiceSetup();
390 if (confirmation_required_
== CONFIRM_AFTER_SIGNIN
) {
391 base::string16 message
;
392 if (!profile_sync_service
) {
393 // Sync is disabled by policy.
394 message
= l10n_util::GetStringUTF16(
395 IDS_ONE_CLICK_SIGNIN_BUBBLE_SYNC_DISABLED_MESSAGE
);
397 DisplayFinalConfirmationBubble(message
);
401 case CONFIGURE_SYNC_FIRST
:
402 ShowSettingsPage(true); // Show sync config UI.
404 case SHOW_SETTINGS_WITHOUT_CONFIGURE
:
405 ShowSettingsPage(false); // Don't show sync config UI.
413 void OneClickSigninSyncStarter::DisplayFinalConfirmationBubble(
414 const base::string16
& custom_message
) {
416 browser_
->window()->ShowOneClickSigninBubble(
417 BrowserWindow::ONE_CLICK_SIGNIN_BUBBLE_TYPE_BUBBLE
,
418 base::string16(), // No email required - this is not a SAML confirmation.
420 // Callback is ignored.
421 BrowserWindow::StartSyncCallback());
424 void OneClickSigninSyncStarter::EnsureBrowser() {
426 // The user just created a new profile or has closed the browser that
427 // we used previously. Grab the most recently active browser or else
429 browser_
= chrome::FindLastActiveWithProfile(profile_
, desktop_type_
);
431 browser_
= new Browser(Browser::CreateParams(profile_
,
433 chrome::AddTabAt(browser_
, GURL(), -1, true);
435 browser_
->window()->Show();
439 void OneClickSigninSyncStarter::ShowSettingsPage(bool configure_sync
) {
440 // Give the user a chance to configure things. We don't clear the
441 // ProfileSyncService::setup_in_progress flag because we don't want sync
442 // to start up until after the configure UI is displayed (the configure UI
443 // will clear the flag when the user is done setting up sync).
444 ProfileSyncService
* profile_sync_service
= GetProfileSyncService();
445 LoginUIService
* login_ui
= LoginUIServiceFactory::GetForProfile(profile_
);
446 if (login_ui
->current_login_ui()) {
447 login_ui
->current_login_ui()->FocusUI();
451 // If the sign in tab is showing the native signin page or the blank page
452 // for web-based flow, and is not about to be closed, use it to show the
454 bool use_same_tab
= false;
455 if (web_contents()) {
456 GURL current_url
= web_contents()->GetLastCommittedURL();
457 bool is_chrome_signin_url
=
458 current_url
.GetOrigin().spec() == chrome::kChromeUIChromeSigninURL
;
460 (is_chrome_signin_url
||
461 signin::IsContinueUrlForWebBasedSigninFlow(current_url
)) &&
462 !signin::IsAutoCloseEnabledInURL(current_url
);
464 if (profile_sync_service
) {
465 // Need to navigate to the settings page and display the sync UI.
467 ShowSettingsPageInWebContents(web_contents(),
468 chrome::kSyncSetupSubPage
);
470 // If the user is setting up sync for the first time, let them configure
471 // advanced sync settings. However, in the case of re-authentication,
472 // return the user to the settings page without showing any config UI.
473 if (configure_sync
) {
474 chrome::ShowSettingsSubPage(browser_
, chrome::kSyncSetupSubPage
);
476 FinishProfileSyncServiceSetup();
477 chrome::ShowSettings(browser_
);
481 // Sync is disabled - just display the settings page.
482 FinishProfileSyncServiceSetup();
484 ShowSettingsPageInWebContents(web_contents(), std::string());
486 chrome::ShowSettings(browser_
);
491 ProfileSyncService
* OneClickSigninSyncStarter::GetProfileSyncService() {
492 ProfileSyncService
* service
= NULL
;
493 if (profile_
->IsSyncAccessible())
494 service
= ProfileSyncServiceFactory::GetForProfile(profile_
);
498 void OneClickSigninSyncStarter::FinishProfileSyncServiceSetup() {
499 ProfileSyncService
* service
=
500 ProfileSyncServiceFactory::GetForProfile(profile_
);
502 service
->SetSetupInProgress(false);
505 void OneClickSigninSyncStarter::ShowSettingsPageInWebContents(
506 content::WebContents
* contents
,
507 const std::string
& sub_page
) {
508 std::string url
= std::string(chrome::kChromeUISettingsURL
) + sub_page
;
509 content::OpenURLParams
params(GURL(url
),
512 content::PAGE_TRANSITION_AUTO_TOPLEVEL
,
514 contents
->OpenURL(params
);
517 Browser
* browser
= chrome::FindBrowserWithWebContents(contents
);
519 browser
->tab_strip_model()->GetIndexOfWebContents(contents
);
520 browser
->tab_strip_model()->ActivateTabAt(content_index
,
521 false /* user_gesture */);