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