Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(332)

Unified Diff: components/signin/core/browser/access_token_fetcher.cc

Issue 2582573002: Signin/OAuth: Create an AccessTokenFetcher helper class (Closed)
Patch Set: review5 Created 3 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
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());
+}

Powered by Google App Engine
This is Rietveld 408576698