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_verifier.h"
      6 
      7 #include <vector>
      8 
      9 #include "base/logging.h"
     10 #include "base/metrics/histogram.h"
     11 #include "base/strings/string_util.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/time/time.h"
     14 #include "chrome/browser/chromeos/net/network_portal_detector.h"
     15 #include "chrome/browser/signin/profile_oauth2_token_service.h"
     16 #include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
     17 #include "chromeos/network/network_handler.h"
     18 #include "chromeos/network/network_state.h"
     19 #include "chromeos/network/network_state_handler.h"
     20 #include "content/public/browser/browser_thread.h"
     21 #include "google_apis/gaia/gaia_constants.h"
     22 #include "google_apis/gaia/gaia_urls.h"
     23 #include "third_party/cros_system_api/dbus/service_constants.h"
     24 
     25 using content::BrowserThread;
     26 
     27 namespace {
     28 
     29 // OAuth token request max retry count.
     30 const int kMaxRequestAttemptCount = 5;
     31 
     32 // OAuth token request retry delay in milliseconds.
     33 const int kRequestRestartDelay = 3000;
     34 
     35 // Post merge session verification delay.
     36 #ifndef NDEBUG
     37 const int kPostResoreVerificationDelay = 1000;
     38 #else
     39 const int kPostResoreVerificationDelay = 1000*60*3;
     40 #endif
     41 
     42 bool IsConnectionOrServiceError(const GoogleServiceAuthError& error) {
     43   return error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
     44          error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
     45          error.state() == GoogleServiceAuthError::REQUEST_CANCELED;
     46 }
     47 
     48 }  // namespace
     49 
     50 namespace chromeos {
     51 
     52 OAuth2LoginVerifier::OAuth2LoginVerifier(
     53     OAuth2LoginVerifier::Delegate* delegate,
     54     net::URLRequestContextGetter* system_request_context,
     55     net::URLRequestContextGetter* user_request_context)
     56     : delegate_(delegate),
     57       system_request_context_(system_request_context),
     58       user_request_context_(user_request_context),
     59       retry_count_(0) {
     60   DCHECK(delegate);
     61 }
     62 
     63 OAuth2LoginVerifier::~OAuth2LoginVerifier() {
     64 }
     65 
     66 void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
     67   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
     68 
     69   // Delay the verification if the network is not connected or on a captive
     70   // portal.
     71   const NetworkState* default_network =
     72       NetworkHandler::Get()->network_state_handler()->DefaultNetwork();
     73   NetworkPortalDetector* detector = NetworkPortalDetector::Get();
     74   if (!default_network ||
     75       default_network->connection_state() == shill::kStatePortal ||
     76       (detector && detector->GetCaptivePortalState(default_network).status !=
     77            NetworkPortalDetector::CAPTIVE_PORTAL_STATUS_ONLINE)) {
     78     // If network is offline, defer the token fetching until online.
     79     VLOG(1) << "Network is offline.  Deferring OAuth2 access token fetch.";
     80     BrowserThread::PostDelayedTask(
     81         BrowserThread::UI,
     82         FROM_HERE,
     83         base::Bind(
     84             &OAuth2LoginVerifier::VerifyProfileTokens, AsWeakPtr(), profile),
     85         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
     86     return;
     87   }
     88 
     89   access_token_.clear();
     90   gaia_token_.clear();
     91   StartFetchingOAuthLoginAccessToken(profile);
     92 }
     93 
     94 void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile* profile) {
     95   OAuth2TokenService::ScopeSet scopes;
     96   scopes.insert(GaiaUrls::GetInstance()->oauth1_login_scope());
     97   ProfileOAuth2TokenService* token_service =
     98       ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
     99   login_token_request_ = token_service->StartRequestWithContext(
    100       token_service->GetPrimaryAccountId(),
    101       system_request_context_.get(),
    102       scopes,
    103       this);
    104 }
    105 
    106 void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
    107   // No service will fetch us uber auth token.
    108   gaia_fetcher_.reset(
    109       new GaiaAuthFetcher(this,
    110                           std::string(GaiaConstants::kChromeOSSource),
    111                           user_request_context_.get()));
    112   gaia_fetcher_->StartTokenFetchForUberAuthExchange(access_token_);
    113 }
    114 
    115 
    116 void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
    117     const std::string& uber_token) {
    118   VLOG(1) << "OAuthLogin(uber_token) successful!";
    119   retry_count_ = 0;
    120   gaia_token_ = uber_token;
    121   StartMergeSession();
    122 }
    123 
    124 void OAuth2LoginVerifier::OnUberAuthTokenFailure(
    125     const GoogleServiceAuthError& error) {
    126   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    127   LOG(WARNING) << "OAuthLogin(uber_token) failed,"
    128                << " error: " << error.state();
    129   RetryOnError("OAuthLoginUberToken", error,
    130                base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken,
    131                           AsWeakPtr()),
    132                base::Bind(&Delegate::OnSessionMergeFailure,
    133                           base::Unretained(delegate_)));
    134 }
    135 
    136 void OAuth2LoginVerifier::StartMergeSession() {
    137   DCHECK(!gaia_token_.empty());
    138   gaia_fetcher_.reset(
    139       new GaiaAuthFetcher(this,
    140                           std::string(GaiaConstants::kChromeOSSource),
    141                           user_request_context_.get()));
    142   gaia_fetcher_->StartMergeSession(gaia_token_);
    143 }
    144 
    145 void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) {
    146   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    147   VLOG(1) << "MergeSession successful.";
    148   delegate_->OnSessionMergeSuccess();
    149   // Schedule post-merge verification to analyze how many LSID/SID overruns
    150   // were created by the session restore.
    151   SchedulePostMergeVerification();
    152 }
    153 
    154 void OAuth2LoginVerifier::SchedulePostMergeVerification() {
    155   BrowserThread::PostDelayedTask(
    156       BrowserThread::UI,
    157       FROM_HERE,
    158       base::Bind(
    159           &OAuth2LoginVerifier::StartPostRestoreVerification, AsWeakPtr()),
    160       base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay));
    161 }
    162 
    163 void OAuth2LoginVerifier::StartPostRestoreVerification() {
    164   gaia_fetcher_.reset(
    165       new GaiaAuthFetcher(this,
    166                           std::string(GaiaConstants::kChromeOSSource),
    167                           user_request_context_.get()));
    168   gaia_fetcher_->StartListAccounts();
    169 }
    170 
    171 void OAuth2LoginVerifier::OnMergeSessionFailure(
    172     const GoogleServiceAuthError& error) {
    173   LOG(WARNING) << "Failed MergeSession request," << " error: " << error.state();
    174   // If MergeSession from GAIA service token fails, retry the session restore
    175   // from OAuth2 refresh token. If that failed too, signal the delegate.
    176   RetryOnError(
    177       "MergeSession",
    178       error,
    179       base::Bind(&OAuth2LoginVerifier::StartMergeSession,
    180                  AsWeakPtr()),
    181       base::Bind(&Delegate::OnSessionMergeFailure,
    182                  base::Unretained(delegate_)));
    183 }
    184 
    185 void OAuth2LoginVerifier::OnGetTokenSuccess(
    186     const OAuth2TokenService::Request* request,
    187     const std::string& access_token,
    188     const base::Time& expiration_time) {
    189   DCHECK_EQ(login_token_request_.get(), request);
    190   login_token_request_.reset();
    191 
    192   VLOG(1) << "Got OAuth2 access token!";
    193   retry_count_ = 0;
    194   access_token_ = access_token;
    195   StartOAuthLoginForUberToken();
    196 }
    197 
    198 void OAuth2LoginVerifier::OnGetTokenFailure(
    199     const OAuth2TokenService::Request* request,
    200     const GoogleServiceAuthError& error) {
    201   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    202   DCHECK_EQ(login_token_request_.get(), request);
    203   login_token_request_.reset();
    204 
    205   LOG(WARNING) << "Failed to get OAuth2 access token, "
    206                << " error: " << error.state();
    207   UMA_HISTOGRAM_ENUMERATION(
    208       base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
    209       error.state(),
    210       GoogleServiceAuthError::NUM_STATES);
    211   delegate_->OnSessionMergeFailure(IsConnectionOrServiceError(error));
    212 }
    213 
    214 void OAuth2LoginVerifier::OnListAccountsSuccess(
    215     const std::string& data) {
    216   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    217   VLOG(1) << "ListAccounts successful.";
    218   delegate_->OnListAccountsSuccess(data);
    219 }
    220 
    221 void OAuth2LoginVerifier::OnListAccountsFailure(
    222     const GoogleServiceAuthError& error) {
    223   DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
    224   LOG(WARNING) << "Failed to get list of session accounts, "
    225                << " error: " << error.state();
    226   RetryOnError(
    227       "ListAccounts",
    228       error,
    229       base::Bind(&OAuth2LoginVerifier::StartPostRestoreVerification,
    230                  AsWeakPtr()),
    231       base::Bind(&Delegate::OnListAccountsFailure,
    232                  base::Unretained(delegate_)));
    233 }
    234 
    235 void OAuth2LoginVerifier::RetryOnError(const char* operation_id,
    236                                        const GoogleServiceAuthError& error,
    237                                        const base::Closure& task_to_retry,
    238                                        const ErrorHandler& error_handler) {
    239   if (IsConnectionOrServiceError(error) &&
    240       retry_count_ < kMaxRequestAttemptCount) {
    241     retry_count_++;
    242     UMA_HISTOGRAM_ENUMERATION(
    243         base::StringPrintf("OAuth2Login.%sRetry", operation_id),
    244         error.state(),
    245         GoogleServiceAuthError::NUM_STATES);
    246     BrowserThread::PostDelayedTask(
    247         BrowserThread::UI, FROM_HERE, task_to_retry,
    248         base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
    249     return;
    250   }
    251 
    252   LOG(WARNING) << "Unrecoverable error or retry count max reached for "
    253                << operation_id;
    254   UMA_HISTOGRAM_ENUMERATION(
    255       base::StringPrintf("OAuth2Login.%sFailure", operation_id),
    256       error.state(),
    257       GoogleServiceAuthError::NUM_STATES);
    258 
    259   error_handler.Run(IsConnectionOrServiceError(error));
    260 }
    261 
    262 }  // namespace chromeos
    263