Upstream version 5.34.92.0
[platform/framework/web/crosswalk.git] / src / chrome / browser / signin / signin_manager.cc
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.
4
5 #include "chrome/browser/signin/signin_manager.h"
6
7 #include <string>
8 #include <vector>
9
10 #include "base/memory/ref_counted.h"
11 #include "base/prefs/pref_service.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "base/time/time.h"
16 #include "chrome/browser/chrome_notification_types.h"
17 #include "chrome/browser/profiles/profile_io_data.h"
18 #include "chrome/browser/signin/about_signin_internals.h"
19 #include "chrome/browser/signin/about_signin_internals_factory.h"
20 #include "chrome/browser/signin/local_auth.h"
21 #include "chrome/browser/signin/profile_oauth2_token_service.h"
22 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
23 #include "chrome/browser/signin/signin_account_id_helper.h"
24 #include "chrome/browser/signin/signin_global_error.h"
25 #include "chrome/browser/signin/signin_internals_util.h"
26 #include "chrome/browser/signin/signin_manager_factory.h"
27 #include "chrome/browser/ui/global_error/global_error_service.h"
28 #include "chrome/browser/ui/global_error/global_error_service_factory.h"
29 #include "chrome/common/pref_names.h"
30 #include "chrome/common/profile_management_switches.h"
31 #include "components/signin/core/signin_manager_cookie_helper.h"
32 #include "components/signin/core/signin_manager_delegate.h"
33 #include "content/public/browser/browser_thread.h"
34 #include "content/public/browser/notification_service.h"
35 #include "content/public/browser/render_process_host.h"
36 #include "content/public/common/child_process_host.h"
37 #include "google_apis/gaia/gaia_auth_fetcher.h"
38 #include "google_apis/gaia/gaia_auth_util.h"
39 #include "google_apis/gaia/gaia_constants.h"
40 #include "google_apis/gaia/gaia_urls.h"
41 #include "net/base/escape.h"
42 #include "net/url_request/url_request_context.h"
43 #include "third_party/icu/source/i18n/unicode/regex.h"
44
45 using namespace signin_internals_util;
46
47 using content::BrowserThread;
48 using content::ChildProcessHost;
49 using content::RenderProcessHost;
50
51 namespace {
52
53 const char kGetInfoDisplayEmailKey[] = "displayEmail";
54 const char kGetInfoEmailKey[] = "email";
55
56 const char kChromiumSyncService[] = "service=chromiumsync";
57
58 }  // namespace
59
60 // Under the covers, we use a dummy chrome-extension ID to serve the purposes
61 // outlined in the .h file comment for this string.
62 const char* SigninManager::kChromeSigninEffectiveSite =
63     "chrome-extension://acfccoigjajmmgbhpfbjnpckhjjegnih";
64
65 // static
66 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
67   GURL effective(kChromeSigninEffectiveSite);
68   if (url.SchemeIs(effective.scheme().c_str()) &&
69       url.host() == effective.host()) {
70     return true;
71   }
72
73   GURL service_login(GaiaUrls::GetInstance()->service_login_url());
74   if (url.GetOrigin() != service_login.GetOrigin())
75     return false;
76
77   // Any login UI URLs with signin=chromiumsync should be considered a web
78   // URL (relies on GAIA keeping the "service=chromiumsync" query string
79   // fragment present even when embedding inside a "continue" parameter).
80   return net::UnescapeURLComponent(
81       url.query(), net::UnescapeRule::URL_SPECIAL_CHARS)
82           .find(kChromiumSyncService) != std::string::npos;
83 }
84
85 SigninManager::SigninManager(scoped_ptr<SigninManagerDelegate> delegate)
86     : prohibit_signout_(false),
87       had_two_factor_error_(false),
88       type_(SIGNIN_TYPE_NONE),
89       weak_pointer_factory_(this),
90       signin_host_id_(ChildProcessHost::kInvalidUniqueID),
91       delegate_(delegate.Pass()) {
92 }
93
94 void SigninManager::SetSigninProcess(int process_id) {
95   if (process_id == signin_host_id_)
96     return;
97   DLOG_IF(WARNING,
98           signin_host_id_ != ChildProcessHost::kInvalidUniqueID)
99       << "Replacing in-use signin process.";
100   signin_host_id_ = process_id;
101   RenderProcessHost* host = RenderProcessHost::FromID(process_id);
102   DCHECK(host);
103   host->AddObserver(this);
104   signin_hosts_observed_.insert(host);
105 }
106
107 void SigninManager::ClearSigninProcess() {
108   signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
109 }
110
111 bool SigninManager::IsSigninProcess(int process_id) const {
112   return process_id == signin_host_id_;
113 }
114
115 bool SigninManager::HasSigninProcess() const {
116   return signin_host_id_ != ChildProcessHost::kInvalidUniqueID;
117 }
118
119 void SigninManager::AddMergeSessionObserver(
120     MergeSessionHelper::Observer* observer) {
121   if (merge_session_helper_)
122     merge_session_helper_->AddObserver(observer);
123 }
124
125 void SigninManager::RemoveMergeSessionObserver(
126     MergeSessionHelper::Observer* observer) {
127   if (merge_session_helper_)
128     merge_session_helper_->RemoveObserver(observer);
129 }
130
131 SigninManager::~SigninManager() {
132   std::set<RenderProcessHost*>::iterator i;
133   for (i = signin_hosts_observed_.begin();
134        i != signin_hosts_observed_.end();
135        ++i) {
136     (*i)->RemoveObserver(this);
137   }
138 }
139
140 void SigninManager::InitTokenService() {
141   ProfileOAuth2TokenService* token_service =
142       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
143   const std::string& account_id = GetAuthenticatedUsername();
144   if (token_service && !account_id.empty())
145     token_service->LoadCredentials(account_id);
146 }
147
148 std::string SigninManager::SigninTypeToString(
149     SigninManager::SigninType type) {
150   switch (type) {
151     case SIGNIN_TYPE_NONE:
152       return "No Signin";
153     case SIGNIN_TYPE_WITH_CREDENTIALS:
154       return "Signin with credentials";
155     case SIGNIN_TYPE_WITH_OAUTH_CODE:
156       return "Signin with oauth code";
157   }
158
159   NOTREACHED();
160   return std::string();
161 }
162
163 bool SigninManager::PrepareForSignin(SigninType type,
164                                      const std::string& username,
165                                      const std::string& password) {
166   DCHECK(possibly_invalid_username_.empty() ||
167          possibly_invalid_username_ == username);
168   DCHECK(!username.empty());
169
170   if (!IsAllowedUsername(username)) {
171     // Account is not allowed by admin policy.
172     HandleAuthError(GoogleServiceAuthError(
173         GoogleServiceAuthError::ACCOUNT_DISABLED), true);
174     return false;
175   }
176
177   // This attempt is either 1) the user trying to establish initial sync, or
178   // 2) trying to refresh credentials for an existing username.  If it is 2, we
179   // need to try again, but take care to leave state around tracking that the
180   // user has successfully signed in once before with this username, so that on
181   // restart we don't think sync setup has never completed.
182   ClearTransientSigninData();
183   type_ = type;
184   possibly_invalid_username_.assign(username);
185   password_.assign(password);
186
187   client_login_.reset(new GaiaAuthFetcher(this,
188                                           GaiaConstants::kChromeSource,
189                                           profile_->GetRequestContext()));
190   NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
191   return true;
192 }
193
194 void SigninManager::StartSignInWithCredentials(
195     const std::string& session_index,
196     const std::string& username,
197     const std::string& password,
198     const OAuthTokenFetchedCallback& callback) {
199   DCHECK(GetAuthenticatedUsername().empty() ||
200          gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
201
202   if (!PrepareForSignin(SIGNIN_TYPE_WITH_CREDENTIALS, username, password))
203     return;
204
205   // Store our callback.
206   DCHECK(oauth_token_fetched_callback_.is_null());
207   oauth_token_fetched_callback_ = callback;
208
209   if (password.empty()) {
210     // Chrome must verify the GAIA cookies first if auto sign-in is triggered
211     // with no password provided. This is to protect Chrome against forged
212     // GAIA cookies from a super-domain.
213     VerifyGaiaCookiesBeforeSignIn(session_index);
214   } else {
215     // This function starts with the current state of the web session's cookie
216     // jar and mints a new ClientLogin-style SID/LSID pair.  This involves going
217     // through the follow process or requests to GAIA and LSO:
218     //
219     // - call /o/oauth2/programmatic_auth with the returned token to get oauth2
220     //   access and refresh tokens
221     // - call /accounts/OAuthLogin with the oauth2 access token and get SID/LSID
222     //   pair for use by the token service
223     //
224     // The resulting SID/LSID can then be used just as if
225     // client_login_->StartClientLogin() had completed successfully.
226     client_login_->StartCookieForOAuthLoginTokenExchange(session_index);
227   }
228 }
229
230 void SigninManager::StartSignInWithOAuthCode(
231     const std::string& username,
232     const std::string& password,
233     const std::string& oauth_code,
234     const OAuthTokenFetchedCallback& callback) {
235   DCHECK(GetAuthenticatedUsername().empty() ||
236          gaia::AreEmailsSame(username, GetAuthenticatedUsername()));
237
238   if (!PrepareForSignin(SIGNIN_TYPE_WITH_OAUTH_CODE, username, password))
239     return;
240
241   DCHECK(oauth_token_fetched_callback_.is_null());
242   oauth_token_fetched_callback_ = callback;
243
244   client_login_->StartAuthCodeForOAuth2TokenExchange(oauth_code);
245 }
246
247 void SigninManager::VerifyGaiaCookiesBeforeSignIn(
248     const std::string& session_index) {
249   scoped_refptr<SigninManagerCookieHelper> cookie_helper(
250       new SigninManagerCookieHelper(
251           profile_->GetRequestContext(),
252           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::UI),
253           BrowserThread::GetMessageLoopProxyForThread(BrowserThread::IO)));
254   cookie_helper->StartFetchingGaiaCookiesOnUIThread(
255       base::Bind(&SigninManager::OnGaiaCookiesFetched,
256                  weak_pointer_factory_.GetWeakPtr(), session_index));
257 }
258
259 void SigninManager::OnGaiaCookiesFetched(
260     const std::string session_index, const net::CookieList& cookie_list) {
261   net::CookieList::const_iterator it;
262   bool success = false;
263   for (it = cookie_list.begin(); it != cookie_list.end(); ++it) {
264     // Make sure the LSID cookie is set on the GAIA host, instead of a super-
265     // domain.
266     if (it->Name() == "LSID") {
267       if (it->IsHostCookie() && it->IsHttpOnly() && it->IsSecure()) {
268         // Found a valid LSID cookie. Continue loop to make sure we don't have
269         // invalid LSID cookies on any super-domain.
270         success = true;
271       } else {
272         success = false;
273         break;
274       }
275     }
276   }
277
278   if (success) {
279     client_login_->StartCookieForOAuthLoginTokenExchange(session_index);
280   } else {
281     HandleAuthError(GoogleServiceAuthError(
282         GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS), true);
283   }
284 }
285
286 void SigninManager::CopyCredentialsFrom(const SigninManager& source) {
287   DCHECK_NE(this, &source);
288   possibly_invalid_username_ = source.possibly_invalid_username_;
289   last_result_ = source.last_result_;
290   temp_oauth_login_tokens_ = source.temp_oauth_login_tokens_;
291 }
292
293 void SigninManager::ClearTransientSigninData() {
294   DCHECK(IsInitialized());
295
296   client_login_.reset();
297   last_result_ = ClientLoginResult();
298   possibly_invalid_username_.clear();
299   password_.clear();
300   had_two_factor_error_ = false;
301   type_ = SIGNIN_TYPE_NONE;
302   temp_oauth_login_tokens_ = ClientOAuthResult();
303   oauth_token_fetched_callback_.Reset();
304 }
305
306 void SigninManager::HandleAuthError(const GoogleServiceAuthError& error,
307                                     bool clear_transient_data) {
308   // In some cases, the user should not be signed out.  For example, the failure
309   // may be due to a captcha or OTP challenge.  In these cases, the transient
310   // data must be kept to properly handle the follow up. This routine clears
311   // the data before sending out the notification so the SigninManager is no
312   // longer in the AuthInProgress state when the notification goes out.
313   if (clear_transient_data)
314     ClearTransientSigninData();
315
316   // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
317   // fixed.
318   content::NotificationService::current()->Notify(
319       chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
320       content::Source<Profile>(profile_),
321       content::Details<const GoogleServiceAuthError>(&error));
322
323   FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
324 }
325
326 void SigninManager::SignOut() {
327   DCHECK(IsInitialized());
328
329   if (GetAuthenticatedUsername().empty()) {
330     if (AuthInProgress()) {
331       // If the user is in the process of signing in, then treat a call to
332       // SignOut as a cancellation request.
333       GoogleServiceAuthError error(GoogleServiceAuthError::REQUEST_CANCELED);
334       HandleAuthError(error, true);
335     } else {
336       // Clean up our transient data and exit if we aren't signed in.
337       // This avoids a perf regression from clearing out the TokenDB if
338       // SignOut() is invoked on startup to clean up any incomplete previous
339       // signin attempts.
340       ClearTransientSigninData();
341     }
342     return;
343   }
344
345   if (prohibit_signout_) {
346     DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
347     return;
348   }
349
350   ClearTransientSigninData();
351
352   const std::string& username = GetAuthenticatedUsername();
353   clear_authenticated_username();
354   profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
355
356   // Erase (now) stale information from AboutSigninInternals.
357   NotifyDiagnosticsObservers(USERNAME, "");
358
359   // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
360   // fixed.
361   GoogleServiceSignoutDetails details(username);
362   content::NotificationService::current()->Notify(
363       chrome::NOTIFICATION_GOOGLE_SIGNED_OUT,
364       content::Source<Profile>(profile_),
365       content::Details<const GoogleServiceSignoutDetails>(&details));
366
367   FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
368
369   ProfileOAuth2TokenService* token_service =
370       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
371   token_service->RevokeAllCredentials();
372 }
373
374 void SigninManager::Initialize(Profile* profile, PrefService* local_state) {
375   SigninManagerBase::Initialize(profile, local_state);
376
377   // local_state can be null during unit tests.
378   if (local_state) {
379     local_state_pref_registrar_.Init(local_state);
380     local_state_pref_registrar_.Add(
381         prefs::kGoogleServicesUsernamePattern,
382         base::Bind(&SigninManager::OnGoogleServicesUsernamePatternChanged,
383                    weak_pointer_factory_.GetWeakPtr()));
384   }
385   signin_allowed_.Init(prefs::kSigninAllowed, profile_->GetPrefs(),
386       base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
387                  base::Unretained(this)));
388
389   std::string user = profile_->GetPrefs()->GetString(
390       prefs::kGoogleServicesUsername);
391   if ((!user.empty() && !IsAllowedUsername(user)) || !IsSigninAllowed()) {
392     // User is signed in, but the username is invalid - the administrator must
393     // have changed the policy since the last signin, so sign out the user.
394     SignOut();
395   }
396
397   InitTokenService();
398   account_id_helper_.reset(new SigninAccountIdHelper(this));
399 }
400
401 void SigninManager::Shutdown() {
402   if (merge_session_helper_)
403     merge_session_helper_->CancelAll();
404
405   local_state_pref_registrar_.RemoveAll();
406   account_id_helper_.reset();
407   SigninManagerBase::Shutdown();
408 }
409
410 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
411   if (!GetAuthenticatedUsername().empty() &&
412       !IsAllowedUsername(GetAuthenticatedUsername())) {
413     // Signed in user is invalid according to the current policy so sign
414     // the user out.
415     SignOut();
416   }
417 }
418
419 bool SigninManager::IsSigninAllowed() const {
420   return signin_allowed_.GetValue();
421 }
422
423 // static
424 bool SigninManager::IsSigninAllowedOnIOThread(ProfileIOData* io_data) {
425   DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
426   return io_data->signin_allowed()->GetValue();
427 }
428
429 void SigninManager::OnSigninAllowedPrefChanged() {
430   if (!IsSigninAllowed())
431     SignOut();
432 }
433
434 // static
435 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
436                                               const std::string& policy) {
437   if (policy.empty())
438     return true;
439
440   // Patterns like "*@foo.com" are not accepted by our regex engine (since they
441   // are not valid regular expressions - they should instead be ".*@foo.com").
442   // For convenience, detect these patterns and insert a "." character at the
443   // front.
444   base::string16 pattern = base::UTF8ToUTF16(policy);
445   if (pattern[0] == L'*')
446     pattern.insert(pattern.begin(), L'.');
447
448   // See if the username matches the policy-provided pattern.
449   UErrorCode status = U_ZERO_ERROR;
450   const icu::UnicodeString icu_pattern(pattern.data(), pattern.length());
451   icu::RegexMatcher matcher(icu_pattern, UREGEX_CASE_INSENSITIVE, status);
452   if (!U_SUCCESS(status)) {
453     LOG(ERROR) << "Invalid login regex: " << pattern << ", status: " << status;
454     // If an invalid pattern is provided, then prohibit *all* logins (better to
455     // break signin than to quietly allow users to sign in).
456     return false;
457   }
458   base::string16 username16 = base::UTF8ToUTF16(username);
459   icu::UnicodeString icu_input(username16.data(), username16.length());
460   matcher.reset(icu_input);
461   status = U_ZERO_ERROR;
462   UBool match = matcher.matches(status);
463   DCHECK(U_SUCCESS(status));
464   return !!match;  // !! == convert from UBool to bool.
465 }
466
467 bool SigninManager::IsAllowedUsername(const std::string& username) const {
468   const PrefService* local_state = local_state_pref_registrar_.prefs();
469   if (!local_state)
470     return true; // In a unit test with no local state - all names are allowed.
471
472   std::string pattern = local_state->GetString(
473       prefs::kGoogleServicesUsernamePattern);
474   return IsUsernameAllowedByPolicy(username, pattern);
475 }
476
477 bool SigninManager::AuthInProgress() const {
478   return !possibly_invalid_username_.empty();
479 }
480
481 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
482   return possibly_invalid_username_;
483 }
484
485 void SigninManager::OnGetUserInfoKeyNotFound(const std::string& key) {
486   DCHECK(key == kGetInfoDisplayEmailKey || key == kGetInfoEmailKey);
487   LOG(ERROR) << "Account is not associated with a valid email address. "
488              << "Login failed.";
489   OnClientLoginFailure(GoogleServiceAuthError(
490       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
491 }
492
493 void SigninManager::DisableOneClickSignIn(Profile* profile) {
494   PrefService* pref_service = profile->GetPrefs();
495   pref_service->SetBoolean(prefs::kReverseAutologinEnabled, false);
496 }
497
498 void SigninManager::OnClientLoginSuccess(const ClientLoginResult& result) {
499   last_result_ = result;
500   // Update signin_internals_
501   NotifyDiagnosticsObservers(CLIENT_LOGIN_STATUS, "Successful");
502   // Make a request for the canonical email address and services.
503   client_login_->StartGetUserInfo(result.lsid);
504 }
505
506 void SigninManager::OnClientLoginFailure(const GoogleServiceAuthError& error) {
507   // If we got a bad ASP, prompt for an ASP again by forcing another TWO_FACTOR
508   // error.  This function does not call HandleAuthError() because dealing
509   // with TWO_FACTOR errors needs special handling: we don't want to clear the
510   // transient signin data in such error cases.
511   bool invalid_gaia = error.state() ==
512       GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS;
513
514   GoogleServiceAuthError current_error =
515       (invalid_gaia && had_two_factor_error_) ?
516       GoogleServiceAuthError(GoogleServiceAuthError::TWO_FACTOR) : error;
517
518   if (current_error.state() == GoogleServiceAuthError::TWO_FACTOR)
519     had_two_factor_error_ = true;
520
521   NotifyDiagnosticsObservers(CLIENT_LOGIN_STATUS, error.ToString());
522   HandleAuthError(current_error, !had_two_factor_error_);
523 }
524
525 void SigninManager::OnClientOAuthSuccess(const ClientOAuthResult& result) {
526   DVLOG(1) << "SigninManager::OnClientOAuthSuccess access_token="
527            << result.access_token;
528
529   NotifyDiagnosticsObservers(OAUTH_LOGIN_STATUS, "Successful");
530
531   switch (type_) {
532     case SIGNIN_TYPE_WITH_CREDENTIALS:
533     case SIGNIN_TYPE_WITH_OAUTH_CODE:
534       temp_oauth_login_tokens_ = result;
535       client_login_->StartOAuthLogin(result.access_token,
536                                      GaiaConstants::kGaiaService);
537       break;
538     default:
539       NOTREACHED();
540       break;
541   }
542 }
543
544 void SigninManager::OnClientOAuthFailure(const GoogleServiceAuthError& error) {
545   bool clear_transient_data = true;
546   NotifyDiagnosticsObservers(OAUTH_LOGIN_STATUS, error.ToString());
547   LOG(WARNING) << "SigninManager::OnClientOAuthFailure";
548   HandleAuthError(error, clear_transient_data);
549 }
550
551 void SigninManager::OnGetUserInfoSuccess(const UserInfoMap& data) {
552   NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
553
554   UserInfoMap::const_iterator email_iter = data.find(kGetInfoEmailKey);
555   UserInfoMap::const_iterator display_email_iter =
556       data.find(kGetInfoDisplayEmailKey);
557   if (email_iter == data.end()) {
558     OnGetUserInfoKeyNotFound(kGetInfoEmailKey);
559     return;
560   }
561   if (display_email_iter == data.end()) {
562     OnGetUserInfoKeyNotFound(kGetInfoDisplayEmailKey);
563     return;
564   }
565   DCHECK(email_iter->first == kGetInfoEmailKey);
566   DCHECK(display_email_iter->first == kGetInfoDisplayEmailKey);
567
568   // When signing in with credentials, the possibly invalid name is the Gaia
569   // display name. If the name returned by GetUserInfo does not match what is
570   // expected, return an error.
571   if (type_ == SIGNIN_TYPE_WITH_CREDENTIALS &&
572       !gaia::AreEmailsSame(display_email_iter->second,
573                            possibly_invalid_username_)) {
574     OnGetUserInfoKeyNotFound(kGetInfoDisplayEmailKey);
575     return;
576   }
577
578   possibly_invalid_username_ = email_iter->second;
579
580   if (!oauth_token_fetched_callback_.is_null() &&
581       !temp_oauth_login_tokens_.refresh_token.empty()) {
582     oauth_token_fetched_callback_.Run(temp_oauth_login_tokens_.refresh_token);
583   } else {
584     // No oauth token or callback, so just complete our pending signin.
585     CompletePendingSignin();
586   }
587 }
588
589 void SigninManager::CompletePendingSignin() {
590   DCHECK(!possibly_invalid_username_.empty());
591   OnSignedIn(possibly_invalid_username_);
592
593   ProfileOAuth2TokenService* token_service =
594       ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
595
596   // If inline sign in is enabled, but new profile management is not, perform a
597   // merge session now to push the user's credentials into the cookie jar.
598   bool do_merge_session_in_signin_manager =
599       !switches::IsEnableWebBasedSignin() &&
600       !switches::IsNewProfileManagement();
601
602   if (do_merge_session_in_signin_manager) {
603     merge_session_helper_.reset(new MergeSessionHelper(
604         token_service, profile_->GetRequestContext(), NULL));
605   }
606
607   DCHECK(!temp_oauth_login_tokens_.refresh_token.empty());
608   DCHECK(!GetAuthenticatedUsername().empty());
609   token_service->UpdateCredentials(GetAuthenticatedUsername(),
610                                    temp_oauth_login_tokens_.refresh_token);
611   temp_oauth_login_tokens_ = ClientOAuthResult();
612
613   if (do_merge_session_in_signin_manager)
614     merge_session_helper_->LogIn(GetAuthenticatedUsername());
615 }
616
617 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
618   OnSignedIn(username);
619 }
620
621 void SigninManager::OnSignedIn(const std::string& username) {
622   SetAuthenticatedUsername(username);
623   possibly_invalid_username_.clear();
624   profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
625                                   GetAuthenticatedUsername());
626
627   // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
628   // fixed.
629   GoogleServiceSigninSuccessDetails details(GetAuthenticatedUsername(),
630                                             password_);
631   content::NotificationService::current()->Notify(
632       chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
633       content::Source<Profile>(profile_),
634       content::Details<const GoogleServiceSigninSuccessDetails>(&details));
635
636   FOR_EACH_OBSERVER(Observer, observer_list_,
637                     GoogleSigninSucceeded(GetAuthenticatedUsername(),
638                                           password_));
639
640 #if !defined(OS_ANDROID)
641   // Don't store password hash except for users of new profile features.
642   if (switches::IsNewProfileManagement())
643     chrome::SetLocalAuthCredentials(profile_, password_);
644 #endif
645
646   password_.clear();  // Don't need it anymore.
647   DisableOneClickSignIn(profile_);  // Don't ever offer again.
648 }
649
650 void SigninManager::OnGetUserInfoFailure(const GoogleServiceAuthError& error) {
651   LOG(ERROR) << "Unable to retreive the canonical email address. Login failed.";
652   NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, error.ToString());
653   // REVIEW: why does this call OnClientLoginFailure?
654   OnClientLoginFailure(error);
655 }
656
657 void SigninManager::RenderProcessHostDestroyed(RenderProcessHost* host) {
658   // It's possible we're listening to a "stale" renderer because it was replaced
659   // with a new process by process-per-site. In either case, stop observing it,
660   // but only reset signin_host_id_ tracking if this was from the current signin
661   // process.
662   signin_hosts_observed_.erase(host);
663   if (signin_host_id_ == host->GetID())
664     signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
665 }
666
667 void SigninManager::ProhibitSignout(bool prohibit_signout) {
668   prohibit_signout_ = prohibit_signout;
669 }
670
671 bool SigninManager::IsSignoutProhibited() const {
672   return prohibit_signout_;
673 }