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