Home | History | Annotate | Download | only in signin
      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