Home | History | Annotate | Download | only in signin
      1 // Copyright 2014 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/chromeos/login/signin/oauth2_login_manager.h"
      6 
      7 #include <utility>
      8 #include <vector>
      9 
     10 #include "base/command_line.h"
     11 #include "base/metrics/histogram.h"
     12 #include "base/prefs/pref_service.h"
     13 #include "base/strings/string_util.h"
     14 #include "chrome/browser/browser_process.h"
     15 #include "chrome/browser/profiles/profile.h"
     16 #include "chrome/browser/signin/chrome_signin_client_factory.h"
     17 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     18 #include "chrome/browser/signin/signin_manager_factory.h"
     19 #include "chrome/common/chrome_switches.h"
     20 #include "chromeos/chromeos_switches.h"
     21 #include "components/signin/core/browser/profile_oauth2_token_service.h"
     22 #include "components/signin/core/browser/signin_client.h"
     23 #include "components/signin/core/browser/signin_manager.h"
     24 #include "components/user_manager/user_manager.h"
     25 #include "google_apis/gaia/gaia_auth_util.h"
     26 #include "google_apis/gaia/gaia_constants.h"
     27 #include "google_apis/gaia/gaia_urls.h"
     28 #include "net/url_request/url_request_context_getter.h"
     29 
     30 namespace chromeos {
     31 
     32 namespace {
     33 
     34 static const char kServiceScopeGetUserInfo[] =
     35     "https://www.googleapis.com/auth/userinfo.email";
     36 static const int kMaxRetries = 5;
     37 
     38 }  // namespace
     39 
     40 OAuth2LoginManager::OAuth2LoginManager(Profile* user_profile)
     41     : user_profile_(user_profile),
     42       restore_strategy_(RESTORE_FROM_COOKIE_JAR),
     43       state_(SESSION_RESTORE_NOT_STARTED) {
     44   GetTokenService()->AddObserver(this);
     45 
     46   // For telemetry, we mark session restore completed to avoid warnings from
     47   // MergeSessionThrottle.
     48   if (CommandLine::ForCurrentProcess()->
     49           HasSwitch(chromeos::switches::kDisableGaiaServices)) {
     50     SetSessionRestoreState(SESSION_RESTORE_DONE);
     51   }
     52 }
     53 
     54 OAuth2LoginManager::~OAuth2LoginManager() {
     55 }
     56 
     57 void OAuth2LoginManager::AddObserver(OAuth2LoginManager::Observer* observer) {
     58   observer_list_.AddObserver(observer);
     59 }
     60 
     61 void OAuth2LoginManager::RemoveObserver(
     62     OAuth2LoginManager::Observer* observer) {
     63   observer_list_.RemoveObserver(observer);
     64 }
     65 
     66 void OAuth2LoginManager::RestoreSession(
     67     net::URLRequestContextGetter* auth_request_context,
     68     SessionRestoreStrategy restore_strategy,
     69     const std::string& oauth2_refresh_token,
     70     const std::string& auth_code) {
     71   DCHECK(user_profile_);
     72   auth_request_context_ = auth_request_context;
     73   restore_strategy_ = restore_strategy;
     74   refresh_token_ = oauth2_refresh_token;
     75   oauthlogin_access_token_ = std::string();
     76   auth_code_ = auth_code;
     77   session_restore_start_ = base::Time::Now();
     78   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_PREPARING);
     79   ContinueSessionRestore();
     80 }
     81 
     82 void OAuth2LoginManager::ContinueSessionRestore() {
     83   if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR ||
     84       restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
     85     FetchOAuth2Tokens();
     86     return;
     87   }
     88 
     89   // Save passed OAuth2 refresh token.
     90   if (restore_strategy_ == RESTORE_FROM_PASSED_OAUTH2_REFRESH_TOKEN) {
     91     DCHECK(!refresh_token_.empty());
     92     restore_strategy_ = RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN;
     93     StoreOAuth2Token();
     94     return;
     95   }
     96 
     97   DCHECK(restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN);
     98   RestoreSessionFromSavedTokens();
     99 }
    100 
    101 void OAuth2LoginManager::RestoreSessionFromSavedTokens() {
    102   ProfileOAuth2TokenService* token_service = GetTokenService();
    103   const std::string& primary_account_id = GetPrimaryAccountId();
    104   if (token_service->RefreshTokenIsAvailable(primary_account_id)) {
    105     VLOG(1) << "OAuth2 refresh token is already loaded.";
    106     VerifySessionCookies();
    107   } else {
    108     VLOG(1) << "Loading OAuth2 refresh token from database.";
    109 
    110     // Flag user with unknown token status in case there are no saved tokens
    111     // and OnRefreshTokenAvailable is not called. Flagging it here would
    112     // cause user to go through Gaia in next login to obtain a new refresh
    113     // token.
    114     user_manager::UserManager::Get()->SaveUserOAuthStatus(
    115         primary_account_id, user_manager::User::OAUTH_TOKEN_STATUS_UNKNOWN);
    116 
    117     token_service->LoadCredentials(primary_account_id);
    118   }
    119 }
    120 
    121 void OAuth2LoginManager::Stop() {
    122   oauth2_token_fetcher_.reset();
    123   login_verifier_.reset();
    124 }
    125 
    126 bool OAuth2LoginManager::ShouldBlockTabLoading() {
    127   return state_ == SESSION_RESTORE_PREPARING ||
    128          state_ == SESSION_RESTORE_IN_PROGRESS;
    129 }
    130 
    131 void OAuth2LoginManager::OnRefreshTokenAvailable(
    132     const std::string& account_id) {
    133   VLOG(1) << "OnRefreshTokenAvailable";
    134 
    135   if (state_ == SESSION_RESTORE_NOT_STARTED)
    136     return;
    137 
    138   // TODO(fgorski): Once ProfileOAuth2TokenService supports multi-login, make
    139   // sure to restore session cookies in the context of the correct account_id.
    140 
    141   // Do not validate tokens for supervised users, as they don't actually have
    142   // oauth2 token.
    143   if (user_manager::UserManager::Get()->IsLoggedInAsSupervisedUser()) {
    144     VLOG(1) << "Logged in as supervised user, skip token validation.";
    145     return;
    146   }
    147   // Only restore session cookies for the primary account in the profile.
    148   if (GetPrimaryAccountId() == account_id) {
    149     // Token is loaded. Undo the flagging before token loading.
    150     user_manager::UserManager::Get()->SaveUserOAuthStatus(
    151         account_id, user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
    152     VerifySessionCookies();
    153   }
    154 }
    155 
    156 ProfileOAuth2TokenService* OAuth2LoginManager::GetTokenService() {
    157   return ProfileOAuth2TokenServiceFactory::GetForProfile(user_profile_);
    158 }
    159 
    160 const std::string& OAuth2LoginManager::GetPrimaryAccountId() {
    161   SigninManagerBase* signin_manager =
    162       SigninManagerFactory::GetForProfile(user_profile_);
    163   return signin_manager->GetAuthenticatedAccountId();
    164 }
    165 
    166 void OAuth2LoginManager::StoreOAuth2Token() {
    167   const std::string& primary_account_id = GetPrimaryAccountId();
    168   if (primary_account_id.empty()) {
    169     GetAccountIdOfRefreshToken(refresh_token_);
    170     return;
    171   }
    172 
    173   OnGetUserEmailResponse(primary_account_id);
    174 }
    175 
    176 void OAuth2LoginManager::GetAccountIdOfRefreshToken(
    177     const std::string& refresh_token) {
    178   gaia::OAuthClientInfo client_info;
    179   GaiaUrls* gaia_urls = GaiaUrls::GetInstance();
    180   client_info.client_id = gaia_urls->oauth2_chrome_client_id();
    181   client_info.client_secret = gaia_urls->oauth2_chrome_client_secret();
    182 
    183   account_id_fetcher_.reset(new gaia::GaiaOAuthClient(
    184       auth_request_context_.get()));
    185   account_id_fetcher_->RefreshToken(client_info, refresh_token,
    186       std::vector<std::string>(1, kServiceScopeGetUserInfo), kMaxRetries,
    187       this);
    188 }
    189 
    190 void OAuth2LoginManager::OnRefreshTokenResponse(
    191     const std::string& access_token,
    192     int expires_in_seconds) {
    193   account_id_fetcher_->GetUserEmail(access_token, kMaxRetries, this);
    194 }
    195 
    196 void OAuth2LoginManager::OnGetUserEmailResponse(
    197     const std::string& user_email)  {
    198   DCHECK(!refresh_token_.empty());
    199   account_id_fetcher_.reset();
    200   std::string canonicalized = gaia::CanonicalizeEmail(user_email);
    201   GetTokenService()->UpdateCredentials(canonicalized, refresh_token_);
    202 
    203   FOR_EACH_OBSERVER(Observer, observer_list_,
    204                     OnNewRefreshTokenAvaiable(user_profile_));
    205 }
    206 
    207 void OAuth2LoginManager::OnOAuthError() {
    208   account_id_fetcher_.reset();
    209   LOG(ERROR) << "Account id fetch failed!";
    210   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
    211 }
    212 
    213 void OAuth2LoginManager::OnNetworkError(int response_code) {
    214   account_id_fetcher_.reset();
    215   LOG(ERROR) << "Account id fetch failed! response_code=" << response_code;
    216   SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
    217 }
    218 
    219 void OAuth2LoginManager::FetchOAuth2Tokens() {
    220   DCHECK(auth_request_context_.get());
    221   // If we have authenticated cookie jar, get OAuth1 token first, then fetch
    222   // SID/LSID cookies through OAuthLogin call.
    223   if (restore_strategy_ == RESTORE_FROM_COOKIE_JAR) {
    224     SigninClient* signin_client =
    225         ChromeSigninClientFactory::GetForProfile(user_profile_);
    226     std::string signin_scoped_device_id =
    227         signin_client->GetSigninScopedDeviceId();
    228 
    229     oauth2_token_fetcher_.reset(
    230         new OAuth2TokenFetcher(this, auth_request_context_.get()));
    231     oauth2_token_fetcher_->StartExchangeFromCookies(std::string(),
    232                                                     signin_scoped_device_id);
    233   } else if (restore_strategy_ == RESTORE_FROM_AUTH_CODE) {
    234     DCHECK(!auth_code_.empty());
    235     oauth2_token_fetcher_.reset(
    236         new OAuth2TokenFetcher(this,
    237                                g_browser_process->system_request_context()));
    238     oauth2_token_fetcher_->StartExchangeFromAuthCode(auth_code_);
    239   } else {
    240     NOTREACHED();
    241     SetSessionRestoreState(OAuth2LoginManager::SESSION_RESTORE_FAILED);
    242   }
    243 }
    244 
    245 void OAuth2LoginManager::OnOAuth2TokensAvailable(
    246     const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) {
    247   VLOG(1) << "OAuth2 tokens fetched";
    248   DCHECK(refresh_token_.empty());
    249   refresh_token_.assign(oauth2_tokens.refresh_token);
    250   oauthlogin_access_token_ = oauth2_tokens.access_token;
    251   StoreOAuth2Token();
    252 }
    253 
    254 void OAuth2LoginManager::OnOAuth2TokensFetchFailed() {
    255   LOG(ERROR) << "OAuth2 tokens fetch failed!";
    256   RecordSessionRestoreOutcome(SESSION_RESTORE_TOKEN_FETCH_FAILED,
    257                               SESSION_RESTORE_FAILED);
    258 }
    259 
    260 void OAuth2LoginManager::VerifySessionCookies() {
    261   DCHECK(!login_verifier_.get());
    262   login_verifier_.reset(
    263       new OAuth2LoginVerifier(this,
    264                               g_browser_process->system_request_context(),
    265                               user_profile_->GetRequestContext(),
    266                               oauthlogin_access_token_));
    267 
    268   if (restore_strategy_ == RESTORE_FROM_SAVED_OAUTH2_REFRESH_TOKEN) {
    269     login_verifier_->VerifyUserCookies(user_profile_);
    270     return;
    271   }
    272 
    273   RestoreSessionCookies();
    274 }
    275 
    276 void OAuth2LoginManager::RestoreSessionCookies() {
    277   SetSessionRestoreState(SESSION_RESTORE_IN_PROGRESS);
    278   login_verifier_->VerifyProfileTokens(user_profile_);
    279 }
    280 
    281 void OAuth2LoginManager::Shutdown() {
    282   GetTokenService()->RemoveObserver(this);
    283   login_verifier_.reset();
    284   oauth2_token_fetcher_.reset();
    285 }
    286 
    287 void OAuth2LoginManager::OnSessionMergeSuccess() {
    288   VLOG(1) << "OAuth2 refresh and/or GAIA token verification succeeded.";
    289   RecordSessionRestoreOutcome(SESSION_RESTORE_SUCCESS,
    290                               SESSION_RESTORE_DONE);
    291 }
    292 
    293 void OAuth2LoginManager::OnSessionMergeFailure(bool connection_error) {
    294   LOG(ERROR) << "OAuth2 refresh and GAIA token verification failed!"
    295              << " connection_error: " << connection_error;
    296   RecordSessionRestoreOutcome(SESSION_RESTORE_MERGE_SESSION_FAILED,
    297                               connection_error ?
    298                                   SESSION_RESTORE_CONNECTION_FAILED :
    299                                   SESSION_RESTORE_FAILED);
    300 }
    301 
    302 void OAuth2LoginManager::OnListAccountsSuccess(const std::string& data) {
    303   MergeVerificationOutcome outcome = POST_MERGE_SUCCESS;
    304   // Let's analyze which accounts we see logged in here:
    305   std::vector<std::pair<std::string, bool> > accounts;
    306   gaia::ParseListAccountsData(data, &accounts);
    307   std::string user_email = gaia::CanonicalizeEmail(GetPrimaryAccountId());
    308   if (!accounts.empty()) {
    309     bool found = false;
    310     bool first = true;
    311     for (std::vector<std::pair<std::string, bool> >::const_iterator iter =
    312              accounts.begin();
    313          iter != accounts.end(); ++iter) {
    314       if (gaia::CanonicalizeEmail(iter->first) == user_email) {
    315         found = iter->second;
    316         break;
    317       }
    318 
    319       first = false;
    320     }
    321 
    322     if (!found)
    323       outcome = POST_MERGE_MISSING_PRIMARY_ACCOUNT;
    324     else if (!first)
    325       outcome = POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT;
    326 
    327   } else {
    328     outcome = POST_MERGE_NO_ACCOUNTS;
    329   }
    330 
    331   bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
    332   RecordCookiesCheckOutcome(is_pre_merge, outcome);
    333   // If the primary account is missing during the initial cookie freshness
    334   // check, try to restore GAIA session cookies form the OAuth2 tokens.
    335   if (is_pre_merge) {
    336     if (outcome != POST_MERGE_SUCCESS &&
    337         outcome != POST_MERGE_PRIMARY_NOT_FIRST_ACCOUNT) {
    338       RestoreSessionCookies();
    339     } else {
    340       // We are done with this account, it's GAIA cookies are legit.
    341       RecordSessionRestoreOutcome(SESSION_RESTORE_NOT_NEEDED,
    342                                   SESSION_RESTORE_DONE);
    343     }
    344   }
    345 }
    346 
    347 void OAuth2LoginManager::OnListAccountsFailure(bool connection_error) {
    348   bool is_pre_merge = (state_ == SESSION_RESTORE_PREPARING);
    349   RecordCookiesCheckOutcome(
    350       is_pre_merge,
    351       connection_error ? POST_MERGE_CONNECTION_FAILED :
    352                          POST_MERGE_VERIFICATION_FAILED);
    353   if (is_pre_merge) {
    354     if (!connection_error) {
    355       // If we failed to get account list, our cookies might be stale so we
    356       // need to attempt to restore them.
    357       RestoreSessionCookies();
    358     } else {
    359       RecordSessionRestoreOutcome(SESSION_RESTORE_LISTACCOUNTS_FAILED,
    360                                   SESSION_RESTORE_CONNECTION_FAILED);
    361     }
    362   }
    363 }
    364 
    365 void OAuth2LoginManager::RecordSessionRestoreOutcome(
    366     SessionRestoreOutcome outcome,
    367     OAuth2LoginManager::SessionRestoreState state) {
    368   UMA_HISTOGRAM_ENUMERATION("OAuth2Login.SessionRestore",
    369                             outcome,
    370                             SESSION_RESTORE_COUNT);
    371   SetSessionRestoreState(state);
    372 }
    373 
    374 // static
    375 void OAuth2LoginManager::RecordCookiesCheckOutcome(
    376     bool is_pre_merge,
    377     MergeVerificationOutcome outcome) {
    378   if (is_pre_merge) {
    379     UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PreMergeVerification",
    380                               outcome,
    381                               POST_MERGE_COUNT);
    382   } else {
    383     UMA_HISTOGRAM_ENUMERATION("OAuth2Login.PostMergeVerification",
    384                               outcome,
    385                               POST_MERGE_COUNT);
    386   }
    387 }
    388 
    389 void OAuth2LoginManager::SetSessionRestoreState(
    390     OAuth2LoginManager::SessionRestoreState state) {
    391   if (state_ == state)
    392     return;
    393 
    394   state_ = state;
    395   if (state == OAuth2LoginManager::SESSION_RESTORE_FAILED) {
    396     UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToFailure",
    397                         base::Time::Now() - session_restore_start_);
    398   } else if (state == OAuth2LoginManager::SESSION_RESTORE_DONE) {
    399     UMA_HISTOGRAM_TIMES("OAuth2Login.SessionRestoreTimeToSuccess",
    400                         base::Time::Now() - session_restore_start_);
    401   }
    402 
    403   FOR_EACH_OBSERVER(Observer, observer_list_,
    404                     OnSessionRestoreStateChanged(user_profile_, state_));
    405 }
    406 
    407 void OAuth2LoginManager::SetSessionRestoreStartForTesting(
    408     const base::Time& time) {
    409   session_restore_start_ = time;
    410 }
    411 
    412 }  // namespace chromeos
    413