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/signin/signin_manager.h"
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"
45 using namespace signin_internals_util;
47 using content::BrowserThread;
48 using content::ChildProcessHost;
49 using content::RenderProcessHost;
53 const char kGetInfoDisplayEmailKey[] = "displayEmail";
54 const char kGetInfoEmailKey[] = "email";
56 const char kChromiumSyncService[] = "service=chromiumsync";
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";
66 bool SigninManager::IsWebBasedSigninFlowURL(const GURL& url) {
67 GURL effective(kChromeSigninEffectiveSite);
68 if (url.SchemeIs(effective.scheme().c_str()) &&
69 url.host() == effective.host()) {
73 GURL service_login(GaiaUrls::GetInstance()->service_login_url());
74 if (url.GetOrigin() != service_login.GetOrigin())
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;
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()) {
94 void SigninManager::SetSigninProcess(int process_id) {
95 if (process_id == signin_host_id_)
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);
103 host->AddObserver(this);
104 signin_hosts_observed_.insert(host);
107 void SigninManager::ClearSigninProcess() {
108 signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
111 bool SigninManager::IsSigninProcess(int process_id) const {
112 return process_id == signin_host_id_;
115 bool SigninManager::HasSigninProcess() const {
116 return signin_host_id_ != ChildProcessHost::kInvalidUniqueID;
119 void SigninManager::AddMergeSessionObserver(
120 MergeSessionHelper::Observer* observer) {
121 if (merge_session_helper_)
122 merge_session_helper_->AddObserver(observer);
125 void SigninManager::RemoveMergeSessionObserver(
126 MergeSessionHelper::Observer* observer) {
127 if (merge_session_helper_)
128 merge_session_helper_->RemoveObserver(observer);
131 SigninManager::~SigninManager() {
132 std::set<RenderProcessHost*>::iterator i;
133 for (i = signin_hosts_observed_.begin();
134 i != signin_hosts_observed_.end();
136 (*i)->RemoveObserver(this);
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);
148 std::string SigninManager::SigninTypeToString(
149 SigninManager::SigninType type) {
151 case SIGNIN_TYPE_NONE:
153 case SIGNIN_TYPE_WITH_CREDENTIALS:
154 return "Signin with credentials";
155 case SIGNIN_TYPE_WITH_OAUTH_CODE:
156 return "Signin with oauth code";
160 return std::string();
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());
170 if (!IsAllowedUsername(username)) {
171 // Account is not allowed by admin policy.
172 HandleAuthError(GoogleServiceAuthError(
173 GoogleServiceAuthError::ACCOUNT_DISABLED), true);
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();
184 possibly_invalid_username_.assign(username);
185 password_.assign(password);
187 client_login_.reset(new GaiaAuthFetcher(this,
188 GaiaConstants::kChromeSource,
189 profile_->GetRequestContext()));
190 NotifyDiagnosticsObservers(SIGNIN_TYPE, SigninTypeToString(type));
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()));
202 if (!PrepareForSignin(SIGNIN_TYPE_WITH_CREDENTIALS, username, password))
205 // Store our callback.
206 DCHECK(oauth_token_fetched_callback_.is_null());
207 oauth_token_fetched_callback_ = callback;
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);
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:
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
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);
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()));
238 if (!PrepareForSignin(SIGNIN_TYPE_WITH_OAUTH_CODE, username, password))
241 DCHECK(oauth_token_fetched_callback_.is_null());
242 oauth_token_fetched_callback_ = callback;
244 client_login_->StartAuthCodeForOAuth2TokenExchange(oauth_code);
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));
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-
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.
279 client_login_->StartCookieForOAuthLoginTokenExchange(session_index);
281 HandleAuthError(GoogleServiceAuthError(
282 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS), true);
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_;
293 void SigninManager::ClearTransientSigninData() {
294 DCHECK(IsInitialized());
296 client_login_.reset();
297 last_result_ = ClientLoginResult();
298 possibly_invalid_username_.clear();
300 had_two_factor_error_ = false;
301 type_ = SIGNIN_TYPE_NONE;
302 temp_oauth_login_tokens_ = ClientOAuthResult();
303 oauth_token_fetched_callback_.Reset();
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();
316 // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
318 content::NotificationService::current()->Notify(
319 chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
320 content::Source<Profile>(profile_),
321 content::Details<const GoogleServiceAuthError>(&error));
323 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSigninFailed(error));
326 void SigninManager::SignOut() {
327 DCHECK(IsInitialized());
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);
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
340 ClearTransientSigninData();
345 if (prohibit_signout_) {
346 DVLOG(1) << "Ignoring attempt to sign out while signout is prohibited";
350 ClearTransientSigninData();
352 const std::string& username = GetAuthenticatedUsername();
353 clear_authenticated_username();
354 profile_->GetPrefs()->ClearPref(prefs::kGoogleServicesUsername);
356 // Erase (now) stale information from AboutSigninInternals.
357 NotifyDiagnosticsObservers(USERNAME, "");
359 // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
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));
367 FOR_EACH_OBSERVER(Observer, observer_list_, GoogleSignedOut(username));
369 ProfileOAuth2TokenService* token_service =
370 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
371 token_service->RevokeAllCredentials();
374 void SigninManager::Initialize(Profile* profile, PrefService* local_state) {
375 SigninManagerBase::Initialize(profile, local_state);
377 // local_state can be null during unit tests.
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()));
385 signin_allowed_.Init(prefs::kSigninAllowed, profile_->GetPrefs(),
386 base::Bind(&SigninManager::OnSigninAllowedPrefChanged,
387 base::Unretained(this)));
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.
398 account_id_helper_.reset(new SigninAccountIdHelper(this));
401 void SigninManager::Shutdown() {
402 if (merge_session_helper_)
403 merge_session_helper_->CancelAll();
405 local_state_pref_registrar_.RemoveAll();
406 account_id_helper_.reset();
407 SigninManagerBase::Shutdown();
410 void SigninManager::OnGoogleServicesUsernamePatternChanged() {
411 if (!GetAuthenticatedUsername().empty() &&
412 !IsAllowedUsername(GetAuthenticatedUsername())) {
413 // Signed in user is invalid according to the current policy so sign
419 bool SigninManager::IsSigninAllowed() const {
420 return signin_allowed_.GetValue();
424 bool SigninManager::IsSigninAllowedOnIOThread(ProfileIOData* io_data) {
425 DCHECK(content::BrowserThread::CurrentlyOn(content::BrowserThread::IO));
426 return io_data->signin_allowed()->GetValue();
429 void SigninManager::OnSigninAllowedPrefChanged() {
430 if (!IsSigninAllowed())
435 bool SigninManager::IsUsernameAllowedByPolicy(const std::string& username,
436 const std::string& policy) {
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
444 base::string16 pattern = base::UTF8ToUTF16(policy);
445 if (pattern[0] == L'*')
446 pattern.insert(pattern.begin(), L'.');
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).
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.
467 bool SigninManager::IsAllowedUsername(const std::string& username) const {
468 const PrefService* local_state = local_state_pref_registrar_.prefs();
470 return true; // In a unit test with no local state - all names are allowed.
472 std::string pattern = local_state->GetString(
473 prefs::kGoogleServicesUsernamePattern);
474 return IsUsernameAllowedByPolicy(username, pattern);
477 bool SigninManager::AuthInProgress() const {
478 return !possibly_invalid_username_.empty();
481 const std::string& SigninManager::GetUsernameForAuthInProgress() const {
482 return possibly_invalid_username_;
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. "
489 OnClientLoginFailure(GoogleServiceAuthError(
490 GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS));
493 void SigninManager::DisableOneClickSignIn(Profile* profile) {
494 PrefService* pref_service = profile->GetPrefs();
495 pref_service->SetBoolean(prefs::kReverseAutologinEnabled, false);
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);
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;
514 GoogleServiceAuthError current_error =
515 (invalid_gaia && had_two_factor_error_) ?
516 GoogleServiceAuthError(GoogleServiceAuthError::TWO_FACTOR) : error;
518 if (current_error.state() == GoogleServiceAuthError::TWO_FACTOR)
519 had_two_factor_error_ = true;
521 NotifyDiagnosticsObservers(CLIENT_LOGIN_STATUS, error.ToString());
522 HandleAuthError(current_error, !had_two_factor_error_);
525 void SigninManager::OnClientOAuthSuccess(const ClientOAuthResult& result) {
526 DVLOG(1) << "SigninManager::OnClientOAuthSuccess access_token="
527 << result.access_token;
529 NotifyDiagnosticsObservers(OAUTH_LOGIN_STATUS, "Successful");
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);
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);
551 void SigninManager::OnGetUserInfoSuccess(const UserInfoMap& data) {
552 NotifyDiagnosticsObservers(GET_USER_INFO_STATUS, "Successful");
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);
561 if (display_email_iter == data.end()) {
562 OnGetUserInfoKeyNotFound(kGetInfoDisplayEmailKey);
565 DCHECK(email_iter->first == kGetInfoEmailKey);
566 DCHECK(display_email_iter->first == kGetInfoDisplayEmailKey);
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);
578 possibly_invalid_username_ = email_iter->second;
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);
584 // No oauth token or callback, so just complete our pending signin.
585 CompletePendingSignin();
589 void SigninManager::CompletePendingSignin() {
590 DCHECK(!possibly_invalid_username_.empty());
591 OnSignedIn(possibly_invalid_username_);
593 ProfileOAuth2TokenService* token_service =
594 ProfileOAuth2TokenServiceFactory::GetForProfile(profile_);
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();
602 if (do_merge_session_in_signin_manager) {
603 merge_session_helper_.reset(new MergeSessionHelper(
604 token_service, profile_->GetRequestContext(), NULL));
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();
613 if (do_merge_session_in_signin_manager)
614 merge_session_helper_->LogIn(GetAuthenticatedUsername());
617 void SigninManager::OnExternalSigninCompleted(const std::string& username) {
618 OnSignedIn(username);
621 void SigninManager::OnSignedIn(const std::string& username) {
622 SetAuthenticatedUsername(username);
623 possibly_invalid_username_.clear();
624 profile_->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
625 GetAuthenticatedUsername());
627 // TODO(blundell): Eliminate this notification send once crbug.com/333997 is
629 GoogleServiceSigninSuccessDetails details(GetAuthenticatedUsername(),
631 content::NotificationService::current()->Notify(
632 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
633 content::Source<Profile>(profile_),
634 content::Details<const GoogleServiceSigninSuccessDetails>(&details));
636 FOR_EACH_OBSERVER(Observer, observer_list_,
637 GoogleSigninSucceeded(GetAuthenticatedUsername(),
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_);
646 password_.clear(); // Don't need it anymore.
647 DisableOneClickSignIn(profile_); // Don't ever offer again.
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);
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
662 signin_hosts_observed_.erase(host);
663 if (signin_host_id_ == host->GetID())
664 signin_host_id_ = ChildProcessHost::kInvalidUniqueID;
667 void SigninManager::ProhibitSignout(bool prohibit_signout) {
668 prohibit_signout_ = prohibit_signout;
671 bool SigninManager::IsSignoutProhibited() const {
672 return prohibit_signout_;