root/chrome/browser/chromeos/login/oauth2_login_verifier.cc

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. IsConnectionOrServiceError
  2. retry_count_
  3. VerifyUserCookies
  4. VerifyProfileTokens
  5. VerifyProfileTokensImpl
  6. StartFetchingOAuthLoginAccessToken
  7. StartOAuthLoginForUberToken
  8. OnUberAuthTokenSuccess
  9. OnUberAuthTokenFailure
  10. StartMergeSession
  11. OnMergeSessionSuccess
  12. SchedulePostMergeVerification
  13. StartAuthCookiesVerification
  14. OnMergeSessionFailure
  15. OnGetTokenSuccess
  16. OnGetTokenFailure
  17. OnListAccountsSuccess
  18. OnListAccountsFailure
  19. RetryOnError

// Copyright (c) 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/chromeos/login/oauth2_login_verifier.h"

#include <vector>

#include "base/logging.h"
#include "base/metrics/histogram.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/time/time.h"
#include "chrome/browser/chromeos/net/delay_network_call.h"
#include "chrome/browser/signin/profile_oauth2_token_service_factory.h"
#include "chrome/browser/signin/signin_manager_factory.h"
#include "components/signin/core/browser/profile_oauth2_token_service.h"
#include "components/signin/core/browser/signin_manager.h"
#include "content/public/browser/browser_thread.h"
#include "google_apis/gaia/gaia_constants.h"

using content::BrowserThread;

namespace {

// OAuth token request max retry count.
const int kMaxRequestAttemptCount = 5;

// OAuth token request retry delay in milliseconds.
const int kRequestRestartDelay = 3000;

// Post merge session verification delay.
#ifndef NDEBUG
const int kPostResoreVerificationDelay = 1000;
#else
const int kPostResoreVerificationDelay = 1000*60*3;
#endif

bool IsConnectionOrServiceError(const GoogleServiceAuthError& error) {
  return error.state() == GoogleServiceAuthError::CONNECTION_FAILED ||
         error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE ||
         error.state() == GoogleServiceAuthError::REQUEST_CANCELED;
}

}  // namespace

namespace chromeos {

OAuth2LoginVerifier::OAuth2LoginVerifier(
    OAuth2LoginVerifier::Delegate* delegate,
    net::URLRequestContextGetter* system_request_context,
    net::URLRequestContextGetter* user_request_context,
    const std::string& oauthlogin_access_token)
    : OAuth2TokenService::Consumer("cros_login_verifier"),
      delegate_(delegate),
      system_request_context_(system_request_context),
      user_request_context_(user_request_context),
      access_token_(oauthlogin_access_token),
      retry_count_(0) {
  DCHECK(delegate);
}

OAuth2LoginVerifier::~OAuth2LoginVerifier() {
}

void OAuth2LoginVerifier::VerifyUserCookies(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Delay the verification if the network is not connected or on a captive
  // portal.
  DelayNetworkCall(
      base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification,
                 AsWeakPtr()),
      base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
}

void OAuth2LoginVerifier::VerifyProfileTokens(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  // Delay the verification if the network is not connected or on a captive
  // portal.
  DelayNetworkCall(
      base::Bind(
          &OAuth2LoginVerifier::VerifyProfileTokensImpl, AsWeakPtr(), profile),
      base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
}

void OAuth2LoginVerifier::VerifyProfileTokensImpl(Profile* profile) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));

  gaia_token_.clear();
  if (access_token_.empty()) {
    // Fetch /OAuthLogin scoped access token.
    StartFetchingOAuthLoginAccessToken(profile);
  } else {
    // If OAuthLogin-scoped access token already exists (if it's generated
    // together with freshly minted refresh token), then fetch GAIA uber token.
    StartOAuthLoginForUberToken();
  }
}

void OAuth2LoginVerifier::StartFetchingOAuthLoginAccessToken(Profile* profile) {
  OAuth2TokenService::ScopeSet scopes;
  scopes.insert(GaiaConstants::kOAuth1LoginScope);
  ProfileOAuth2TokenService* token_service =
      ProfileOAuth2TokenServiceFactory::GetForProfile(profile);
  SigninManagerBase* signin_manager =
      SigninManagerFactory::GetForProfile(profile);
  login_token_request_ = token_service->StartRequestWithContext(
      signin_manager->GetAuthenticatedAccountId(),
      system_request_context_.get(),
      scopes,
      this);
}

void OAuth2LoginVerifier::StartOAuthLoginForUberToken() {
  // No service will fetch us uber auth token.
  gaia_fetcher_.reset(
      new GaiaAuthFetcher(this,
                          std::string(GaiaConstants::kChromeOSSource),
                          user_request_context_.get()));
  gaia_fetcher_->StartTokenFetchForUberAuthExchange(access_token_);
}


void OAuth2LoginVerifier::OnUberAuthTokenSuccess(
    const std::string& uber_token) {
  VLOG(1) << "OAuthLogin(uber_token) successful!";
  retry_count_ = 0;
  gaia_token_ = uber_token;
  StartMergeSession();
}

void OAuth2LoginVerifier::OnUberAuthTokenFailure(
    const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "OAuthLogin(uber_token) failed,"
               << " error: " << error.state();
  RetryOnError("OAuthLoginUberToken", error,
               base::Bind(&OAuth2LoginVerifier::StartOAuthLoginForUberToken,
                          AsWeakPtr()),
               base::Bind(&Delegate::OnSessionMergeFailure,
                          base::Unretained(delegate_)));
}

void OAuth2LoginVerifier::StartMergeSession() {
  DCHECK(!gaia_token_.empty());
  gaia_fetcher_.reset(
      new GaiaAuthFetcher(this,
                          std::string(GaiaConstants::kChromeOSSource),
                          user_request_context_.get()));
  gaia_fetcher_->StartMergeSession(gaia_token_);
}

void OAuth2LoginVerifier::OnMergeSessionSuccess(const std::string& data) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "MergeSession successful.";
  delegate_->OnSessionMergeSuccess();
  // Schedule post-merge verification to analyze how many LSID/SID overruns
  // were created by the session restore.
  SchedulePostMergeVerification();
}

void OAuth2LoginVerifier::SchedulePostMergeVerification() {
  BrowserThread::PostDelayedTask(
      BrowserThread::UI,
      FROM_HERE,
      base::Bind(
          &OAuth2LoginVerifier::StartAuthCookiesVerification, AsWeakPtr()),
      base::TimeDelta::FromMilliseconds(kPostResoreVerificationDelay));
}

void OAuth2LoginVerifier::StartAuthCookiesVerification() {
  gaia_fetcher_.reset(
      new GaiaAuthFetcher(this,
                          std::string(GaiaConstants::kChromeOSSource),
                          user_request_context_.get()));
  gaia_fetcher_->StartListAccounts();
}

void OAuth2LoginVerifier::OnMergeSessionFailure(
    const GoogleServiceAuthError& error) {
  LOG(WARNING) << "Failed MergeSession request," << " error: " << error.state();
  // If MergeSession from GAIA service token fails, retry the session restore
  // from OAuth2 refresh token. If that failed too, signal the delegate.
  RetryOnError(
      "MergeSession",
      error,
      base::Bind(&OAuth2LoginVerifier::StartMergeSession,
                 AsWeakPtr()),
      base::Bind(&Delegate::OnSessionMergeFailure,
                 base::Unretained(delegate_)));
}

void OAuth2LoginVerifier::OnGetTokenSuccess(
    const OAuth2TokenService::Request* request,
    const std::string& access_token,
    const base::Time& expiration_time) {
  DCHECK_EQ(login_token_request_.get(), request);
  login_token_request_.reset();

  VLOG(1) << "Got OAuth2 access token!";
  retry_count_ = 0;
  access_token_ = access_token;
  StartOAuthLoginForUberToken();
}

void OAuth2LoginVerifier::OnGetTokenFailure(
    const OAuth2TokenService::Request* request,
    const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  DCHECK_EQ(login_token_request_.get(), request);
  login_token_request_.reset();

  LOG(WARNING) << "Failed to get OAuth2 access token, "
               << " error: " << error.state();
  UMA_HISTOGRAM_ENUMERATION(
      base::StringPrintf("OAuth2Login.%sFailure", "GetOAuth2AccessToken"),
      error.state(),
      GoogleServiceAuthError::NUM_STATES);
  delegate_->OnSessionMergeFailure(IsConnectionOrServiceError(error));
}

void OAuth2LoginVerifier::OnListAccountsSuccess(
    const std::string& data) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  VLOG(1) << "ListAccounts successful.";
  delegate_->OnListAccountsSuccess(data);
}

void OAuth2LoginVerifier::OnListAccountsFailure(
    const GoogleServiceAuthError& error) {
  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
  LOG(WARNING) << "Failed to get list of session accounts, "
               << " error: " << error.state();
  RetryOnError(
      "ListAccounts",
      error,
      base::Bind(&OAuth2LoginVerifier::StartAuthCookiesVerification,
                 AsWeakPtr()),
      base::Bind(&Delegate::OnListAccountsFailure,
                 base::Unretained(delegate_)));
}

void OAuth2LoginVerifier::RetryOnError(const char* operation_id,
                                       const GoogleServiceAuthError& error,
                                       const base::Closure& task_to_retry,
                                       const ErrorHandler& error_handler) {
  if (IsConnectionOrServiceError(error) &&
      retry_count_ < kMaxRequestAttemptCount) {
    retry_count_++;
    UMA_HISTOGRAM_ENUMERATION(
        base::StringPrintf("OAuth2Login.%sRetry", operation_id),
        error.state(),
        GoogleServiceAuthError::NUM_STATES);
    BrowserThread::PostDelayedTask(
        BrowserThread::UI, FROM_HERE, task_to_retry,
        base::TimeDelta::FromMilliseconds(kRequestRestartDelay));
    return;
  }

  LOG(WARNING) << "Unrecoverable error or retry count max reached for "
               << operation_id;
  UMA_HISTOGRAM_ENUMERATION(
      base::StringPrintf("OAuth2Login.%sFailure", operation_id),
      error.state(),
      GoogleServiceAuthError::NUM_STATES);

  error_handler.Run(IsConnectionOrServiceError(error));
}

}  // namespace chromeos

/* [<][>][^][v][top][bottom][index][help] */