| Index: components/signin/core/browser/access_token_fetcher.cc
|
| diff --git a/components/signin/core/browser/access_token_fetcher.cc b/components/signin/core/browser/access_token_fetcher.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..d301d3708da009fe37b33e40a805c1f3d7297ebd
|
| --- /dev/null
|
| +++ b/components/signin/core/browser/access_token_fetcher.cc
|
| @@ -0,0 +1,169 @@
|
| +// Copyright 2017 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 "components/signin/core/browser/access_token_fetcher.h"
|
| +
|
| +#include <utility>
|
| +
|
| +#include "base/logging.h"
|
| +
|
| +AccessTokenFetcher::AccessTokenFetcher(
|
| + const std::string& oauth_consumer_name,
|
| + SigninManagerBase* signin_manager,
|
| + OAuth2TokenService* token_service,
|
| + const OAuth2TokenService::ScopeSet& scopes,
|
| + TokenCallback callback)
|
| + : OAuth2TokenService::Consumer(oauth_consumer_name),
|
| + signin_manager_(signin_manager),
|
| + token_service_(token_service),
|
| + scopes_(scopes),
|
| + callback_(std::move(callback)),
|
| + waiting_for_sign_in_(false),
|
| + waiting_for_refresh_token_(false),
|
| + access_token_retried_(false) {
|
| + Start();
|
| +}
|
| +
|
| +AccessTokenFetcher::~AccessTokenFetcher() {
|
| + if (waiting_for_sign_in_) {
|
| + signin_manager_->RemoveObserver(this);
|
| + }
|
| + if (waiting_for_refresh_token_) {
|
| + token_service_->RemoveObserver(this);
|
| + }
|
| +}
|
| +
|
| +void AccessTokenFetcher::Start() {
|
| + if (signin_manager_->IsAuthenticated()) {
|
| + // Already signed in: Make sure we have a refresh token, then get the access
|
| + // token.
|
| + WaitForRefreshToken();
|
| + return;
|
| + }
|
| +
|
| + // Not signed in: Wait for a sign-in to complete (to get the refresh token),
|
| + // then get the access token.
|
| + DCHECK(!waiting_for_sign_in_);
|
| + waiting_for_sign_in_ = true;
|
| + signin_manager_->AddObserver(this);
|
| +}
|
| +
|
| +void AccessTokenFetcher::WaitForRefreshToken() {
|
| + DCHECK(signin_manager_->IsAuthenticated());
|
| + DCHECK(!waiting_for_refresh_token_);
|
| +
|
| + if (token_service_->RefreshTokenIsAvailable(
|
| + signin_manager_->GetAuthenticatedAccountId())) {
|
| + // Already have refresh token: Get the access token directly.
|
| + StartAccessTokenRequest();
|
| + return;
|
| + }
|
| +
|
| + // Signed in, but refresh token isn't there yet: Wait for the refresh
|
| + // token to be loaded, then get the access token.
|
| + waiting_for_refresh_token_ = true;
|
| + token_service_->AddObserver(this);
|
| +}
|
| +
|
| +void AccessTokenFetcher::StartAccessTokenRequest() {
|
| + // Note: We might get here even in cases where we know that there's no refresh
|
| + // token. We're requesting an access token anyway, so that the token service
|
| + // will generate an appropriate error code that we can return to the client.
|
| + DCHECK(!access_token_request_);
|
| + access_token_request_ = token_service_->StartRequest(
|
| + signin_manager_->GetAuthenticatedAccountId(), scopes_, this);
|
| +}
|
| +
|
| +void AccessTokenFetcher::GoogleSigninSucceeded(const std::string& account_id,
|
| + const std::string& username,
|
| + const std::string& password) {
|
| + DCHECK(waiting_for_sign_in_);
|
| + DCHECK(!waiting_for_refresh_token_);
|
| + DCHECK(signin_manager_->IsAuthenticated());
|
| + waiting_for_sign_in_ = false;
|
| + signin_manager_->RemoveObserver(this);
|
| +
|
| + WaitForRefreshToken();
|
| +}
|
| +
|
| +void AccessTokenFetcher::GoogleSigninFailed(
|
| + const GoogleServiceAuthError& error) {
|
| + DCHECK(waiting_for_sign_in_);
|
| + DCHECK(!waiting_for_refresh_token_);
|
| + waiting_for_sign_in_ = false;
|
| + signin_manager_->RemoveObserver(this);
|
| +
|
| + std::move(callback_).Run(error, std::string());
|
| +}
|
| +
|
| +void AccessTokenFetcher::OnRefreshTokenAvailable(
|
| + const std::string& account_id) {
|
| + DCHECK(waiting_for_refresh_token_);
|
| + DCHECK(!waiting_for_sign_in_);
|
| +
|
| + // Only react on tokens for the account the user has signed in with.
|
| + if (account_id != signin_manager_->GetAuthenticatedAccountId()) {
|
| + return;
|
| + }
|
| +
|
| + waiting_for_refresh_token_ = false;
|
| + token_service_->RemoveObserver(this);
|
| + StartAccessTokenRequest();
|
| +}
|
| +
|
| +void AccessTokenFetcher::OnRefreshTokensLoaded() {
|
| + DCHECK(waiting_for_refresh_token_);
|
| + DCHECK(!waiting_for_sign_in_);
|
| + DCHECK(!access_token_request_);
|
| +
|
| + // All refresh tokens were loaded, but we didn't get one for the account we
|
| + // care about. We probably won't get one any time soon.
|
| + // Attempt to fetch an access token anyway, so that the token service will
|
| + // provide us with an appropriate error code.
|
| + waiting_for_refresh_token_ = false;
|
| + token_service_->RemoveObserver(this);
|
| + StartAccessTokenRequest();
|
| +}
|
| +
|
| +void AccessTokenFetcher::OnGetTokenSuccess(
|
| + const OAuth2TokenService::Request* request,
|
| + const std::string& access_token,
|
| + const base::Time& expiration_time) {
|
| + DCHECK_EQ(request, access_token_request_.get());
|
| + std::unique_ptr<OAuth2TokenService::Request> request_deleter(
|
| + std::move(access_token_request_));
|
| +
|
| + std::move(callback_).Run(GoogleServiceAuthError::AuthErrorNone(),
|
| + access_token);
|
| +}
|
| +
|
| +void AccessTokenFetcher::OnGetTokenFailure(
|
| + const OAuth2TokenService::Request* request,
|
| + const GoogleServiceAuthError& error) {
|
| + DCHECK_EQ(request, access_token_request_.get());
|
| + std::unique_ptr<OAuth2TokenService::Request> request_deleter(
|
| + std::move(access_token_request_));
|
| +
|
| + // There is a special case for Android that RefreshTokenIsAvailable and
|
| + // StartRequest are called to pre-fetch the account image and name before
|
| + // sign-in. In that case, our ongoing access token request gets cancelled.
|
| + // Moreover, OnRefreshTokenAvailable might happen after startup when the
|
| + // credentials are changed/updated.
|
| + // To handle these cases, we retry a canceled request once.
|
| + // However, a request may also get cancelled for legitimate reasons, e.g.
|
| + // because the user signed out. In those cases, there's no point in retrying,
|
| + // so only retry if there (still) is a valid refresh token.
|
| + // NOTE: Maybe we should retry for all transient errors here, so that clients
|
| + // don't have to.
|
| + if (!access_token_retried_ &&
|
| + error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED &&
|
| + token_service_->RefreshTokenIsAvailable(
|
| + signin_manager_->GetAuthenticatedAccountId())) {
|
| + access_token_retried_ = true;
|
| + StartAccessTokenRequest();
|
| + return;
|
| + }
|
| +
|
| + std::move(callback_).Run(error, std::string());
|
| +}
|
|
|