Chromium Code Reviews| 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..30d0aa060db7eaf360ab74c6ca65d55bb5ba8e98 |
| --- /dev/null |
| +++ b/components/signin/core/browser/access_token_fetcher.cc |
| @@ -0,0 +1,122 @@ |
| +// 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" |
| +#include "base/threading/thread_task_runner_handle.h" |
| +#include "components/signin/core/browser/signin_manager_base.h" |
| +#include "google_apis/gaia/google_service_auth_error.h" |
| + |
| +AccessTokenFetcher::AccessTokenFetcher( |
| + const std::string& oauth_consumer_name, |
| + const SigninManagerBase* signin_manager, |
| + OAuth2TokenService* token_service, |
| + const OAuth2TokenService::ScopeSet& scopes, |
| + const TokenCallback& callback) |
| + : OAuth2TokenService::Consumer(oauth_consumer_name), |
| + signin_manager_(signin_manager), |
| + token_service_(token_service), |
| + scopes_(scopes), |
| + callback_(callback), |
| + weak_ptr_factory_(this) { |
| + Start(); |
| +} |
| + |
| +AccessTokenFetcher::~AccessTokenFetcher() { |
| + if (waiting_for_refresh_token_) { |
| + token_service_->RemoveObserver(this); |
| + } |
| +} |
| + |
| +void AccessTokenFetcher::Start() { |
| + if (signin_manager_->IsAuthenticated() && |
| + token_service_->RefreshTokenIsAvailable( |
| + signin_manager_->GetAuthenticatedAccountId())) { |
| + // Already have refresh token: Get the access token directly. |
| + StartAccessTokenRequest(); |
| + } else if (signin_manager_->IsAuthenticated() || |
| + signin_manager_->AuthInProgress()) { |
| + // Currently signing in, or signed in but refresh token isn't there yet: |
| + // Wait for auth to finish (to get the refresh token), then get the access |
| + // token. |
| + // TODO(treib): In the AuthInProgress case, should we wait for |
| + // GoogleSigninSucceeded/Failed (from SigninManagerBase::Observer) first? |
|
msarda
2017/01/18 21:55:19
I agree this is a tricky question: currently the r
Marc Treib
2017/01/19 15:03:55
Done.
However, I'm still not super confident that
msarda
2017/01/23 17:08:09
I think using a timeout is not a good API.
This c
Marc Treib
2017/01/24 10:17:32
Not calling the callback after we're destroyed is
|
| + DCHECK(!waiting_for_refresh_token_); |
| + waiting_for_refresh_token_ = true; |
| + token_service_->AddObserver(this); |
| + } else { |
| + // Not signed in, no access token. Make sure not to run the callback |
| + // synchronously, and not to run it if we get deleted in the meantime. |
| + base::ThreadTaskRunnerHandle::Get()->PostTask( |
|
msarda
2017/01/18 21:55:19
It is not clear to me how you would use this API:
Marc Treib
2017/01/19 15:03:55
There are some (future) clients of this class that
|
| + FROM_HERE, base::Bind(&AccessTokenFetcher::NotSignedInCallback, |
| + weak_ptr_factory_.GetWeakPtr())); |
| + } |
| +} |
| + |
| +void AccessTokenFetcher::NotSignedInCallback() { |
| + callback_.Run(std::string()); |
|
msarda
2017/01/18 21:55:19
We should to pass an error to the callback here a
msarda
2017/01/18 21:55:19
We should use a callback that may be called only o
Marc Treib
2017/01/19 15:03:55
I actually did use OnceCallback in a previous iter
Marc Treib
2017/01/19 15:03:55
So far, none of the callers I'm aware of care abou
msarda
2017/01/23 17:08:09
I think the current API is fine for discussion, bu
|
| +} |
| + |
| +void AccessTokenFetcher::OnRefreshTokenAvailable( |
| + const std::string& account_id) { |
| + DCHECK(waiting_for_refresh_token_); |
|
msarda
2017/01/18 21:55:19
Just to make sure what I said in my previous comme
Marc Treib
2017/01/19 15:03:55
Done.
|
| + |
| + // Only react on tokens for the account the user has signed in with. |
| + if (account_id != signin_manager_->GetAuthenticatedAccountId()) { |
| + return; |
| + } |
| + |
|
msarda
2017/01/18 21:55:18
Nit: I would set waiting_for_refresh_token_ before
Marc Treib
2017/01/19 15:03:55
Done.
|
| + token_service_->RemoveObserver(this); |
| + waiting_for_refresh_token_ = false; |
| + StartAccessTokenRequest(); |
| +} |
| + |
| +void AccessTokenFetcher::OnRefreshTokensLoaded() { |
| + DCHECK(waiting_for_refresh_token_); |
| + |
| + // 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. |
| + token_service_->RemoveObserver(this); |
| + waiting_for_refresh_token_ = false; |
| + callback_.Run(std::string()); |
| +} |
| + |
| +void AccessTokenFetcher::StartAccessTokenRequest() { |
| + access_token_request_ = token_service_->StartRequest( |
| + signin_manager_->GetAuthenticatedAccountId(), scopes_, this); |
| +} |
| + |
| +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_)); |
| + |
| + callback_.Run(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_)); |
| + |
| + if (!access_token_retried_ && |
| + error.state() == GoogleServiceAuthError::State::REQUEST_CANCELED) { |
| + // The request can get reset by loading the refresh token (happens during |
| + // startup) - try one more time. |
| + access_token_retried_ = true; |
| + StartAccessTokenRequest(); |
|
msarda
2017/01/18 21:55:19
It is not clear to me yet why we need to retry. I
Marc Treib
2017/01/19 15:03:55
We had a case on Android where sometimes during st
jkrcal
2017/01/19 15:48:45
I agree it is ugly. Reasoning for the retry: https
msarda
2017/01/23 17:08:09
Thank you for the pointer. I think we should not d
Marc Treib
2017/01/24 10:17:32
The thread is not public unfortunately, so we can'
|
| + return; |
| + } |
| + |
| + DLOG(WARNING) << "Unable to get token: " << error.ToString(); |
| + callback_.Run(std::string()); |
|
msarda
2017/01/18 21:55:18
We need an API that would allow us to pass the err
Marc Treib
2017/01/24 10:17:32
I'd actually prefer if any retrying were handled w
msarda
2017/01/25 09:54:28
I agree with this - we should hide the nasty thing
|
| +} |