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