| // 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 <vector> |
| |
| #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_); |
| } |