// Copyright 2014 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 "google_apis/gaia/ubertoken_fetcher.h" #include #include "base/logging.h" #include "base/metrics/histogram_macros.h" #include "base/rand_util.h" #include "base/time/time.h" #include "google_apis/gaia/gaia_auth_fetcher.h" #include "google_apis/gaia/gaia_constants.h" #include "google_apis/gaia/google_service_auth_error.h" #include "google_apis/gaia/oauth2_token_service.h" namespace { GaiaAuthFetcher* CreateGaiaAuthFetcher( GaiaAuthConsumer* consumer, const std::string& source, net::URLRequestContextGetter* request_context) { return new GaiaAuthFetcher(consumer, source, request_context); } } const int UbertokenFetcher::kMaxRetries = 3; UbertokenFetcher::UbertokenFetcher( OAuth2TokenService* token_service, UbertokenConsumer* consumer, const std::string& source, net::URLRequestContextGetter* request_context) : UbertokenFetcher(token_service, consumer, source, request_context, base::Bind(CreateGaiaAuthFetcher)) { } UbertokenFetcher::UbertokenFetcher( OAuth2TokenService* token_service, UbertokenConsumer* consumer, const std::string& source, net::URLRequestContextGetter* request_context, GaiaAuthFetcherFactory factory) : OAuth2TokenService::Consumer("uber_token_fetcher"), token_service_(token_service), consumer_(consumer), source_(source), request_context_(request_context), gaia_auth_fetcher_factory_(factory), retry_number_(0), second_access_token_request_(false) { DCHECK(token_service); DCHECK(consumer); DCHECK(request_context); } UbertokenFetcher::~UbertokenFetcher() { } void UbertokenFetcher::StartFetchingToken(const std::string& account_id) { DCHECK(!account_id.empty()); account_id_ = account_id; second_access_token_request_ = false; RequestAccessToken(); } void UbertokenFetcher::StartFetchingTokenWithAccessToken( const std::string& account_id, const std::string& access_token) { DCHECK(!account_id.empty()); DCHECK(!access_token.empty()); account_id_ = account_id; access_token_ = access_token; ExchangeTokens(); } void UbertokenFetcher::OnUberAuthTokenSuccess(const std::string& token) { consumer_->OnUbertokenSuccess(token); } void UbertokenFetcher::OnUberAuthTokenFailure( const GoogleServiceAuthError& error) { // Retry only transient errors. bool should_retry = error.state() == GoogleServiceAuthError::CONNECTION_FAILED || error.state() == GoogleServiceAuthError::SERVICE_UNAVAILABLE; if (should_retry) { if (retry_number_ < kMaxRetries) { // Calculate an exponential backoff with randomness of less than 1 sec. double backoff = base::RandDouble() + (1 << retry_number_); ++retry_number_; UMA_HISTOGRAM_ENUMERATION("Signin.UberTokenRetry", error.state(), GoogleServiceAuthError::NUM_STATES); retry_timer_.Stop(); retry_timer_.Start(FROM_HERE, base::TimeDelta::FromSecondsD(backoff), this, &UbertokenFetcher::ExchangeTokens); return; } } else { // The access token is invalid. Tell the token service. OAuth2TokenService::ScopeSet scopes; scopes.insert(GaiaConstants::kOAuth1LoginScope); token_service_->InvalidateAccessToken(account_id_, scopes, access_token_); // In case the access was just stale, try one more time. if (!second_access_token_request_) { second_access_token_request_ = true; RequestAccessToken(); return; } } UMA_HISTOGRAM_ENUMERATION("Signin.UberTokenFailure", error.state(), GoogleServiceAuthError::NUM_STATES); consumer_->OnUbertokenFailure(error); } void UbertokenFetcher::OnGetTokenSuccess( const OAuth2TokenService::Request* request, const std::string& access_token, const base::Time& expiration_time) { DCHECK(!access_token.empty()); access_token_ = access_token; access_token_request_.reset(); ExchangeTokens(); } void UbertokenFetcher::OnGetTokenFailure( const OAuth2TokenService::Request* request, const GoogleServiceAuthError& error) { access_token_request_.reset(); consumer_->OnUbertokenFailure(error); } void UbertokenFetcher::RequestAccessToken() { retry_number_ = 0; gaia_auth_fetcher_.reset(); retry_timer_.Stop(); OAuth2TokenService::ScopeSet scopes; scopes.insert(GaiaConstants::kOAuth1LoginScope); access_token_request_ = token_service_->StartRequest(account_id_, scopes, this); } void UbertokenFetcher::ExchangeTokens() { gaia_auth_fetcher_.reset( gaia_auth_fetcher_factory_.Run(this, source_, request_context_)); gaia_auth_fetcher_->StartTokenFetchForUberAuthExchange(access_token_); }