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