| // Copyright (c) 2013 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 "chrome/browser/chromeos/policy/policy_oauth2_token_fetcher.h" |
| |
| #include <memory> |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/strings/string_util.h" |
| #include "chromeos/chromeos_switches.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "google_apis/gaia/gaia_auth_fetcher.h" |
| #include "google_apis/gaia/gaia_constants.h" |
| #include "google_apis/gaia/gaia_urls.h" |
| #include "google_apis/gaia/google_service_auth_error.h" |
| #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| |
| using content::BrowserThread; |
| |
| namespace policy { |
| |
| namespace { |
| |
| // If true, fake policy tokens will be sent instead of making network requests. |
| bool use_fake_tokens_for_testing_ = false; |
| |
| // Max retry count for token fetching requests. |
| const int kMaxRequestAttemptCount = 5; |
| |
| // OAuth token request retry delay in milliseconds. |
| const int kRequestRestartDelay = 3000; |
| |
| class PolicyOAuth2TokenFetcherImpl : public PolicyOAuth2TokenFetcher, |
| public GaiaAuthConsumer, |
| public OAuth2AccessTokenConsumer { |
| public: |
| PolicyOAuth2TokenFetcherImpl(); |
| ~PolicyOAuth2TokenFetcherImpl() override; |
| |
| private: |
| // PolicyOAuth2TokenFetcher overrides. |
| void StartWithSigninURLLoaderFactory( |
| scoped_refptr<network::SharedURLLoaderFactory> auth_url_loader_factory, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override; |
| void StartWithAuthCode( |
| const std::string& auth_code, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override; |
| void StartWithRefreshToken( |
| const std::string& oauth2_refresh_token, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override; |
| |
| // Returns true if we have previously attempted to fetch tokens with this |
| // class and failed. |
| bool Failed() const override { return failed_; } |
| |
| const std::string& OAuth2RefreshToken() const override { |
| return oauth2_refresh_token_; |
| } |
| const std::string& OAuth2AccessToken() const override { |
| return oauth2_access_token_; |
| } |
| |
| // GaiaAuthConsumer overrides. |
| void OnClientOAuthSuccess( |
| const GaiaAuthConsumer::ClientOAuthResult& oauth_tokens) override; |
| void OnClientOAuthFailure(const GoogleServiceAuthError& error) override; |
| |
| // OAuth2AccessTokenConsumer overrides. |
| void OnGetTokenSuccess(const std::string& access_token, |
| const base::Time& expiration_time) override; |
| void OnGetTokenFailure(const GoogleServiceAuthError& error) override; |
| |
| // Starts fetching OAuth2 refresh token. |
| void StartFetchingRefreshToken(); |
| |
| // Starts fetching OAuth2 access token for the device management service. |
| void StartFetchingAccessToken(); |
| |
| // Decides how to proceed on GAIA |error|. If the error looks temporary, |
| // retries |task| until max retry count is reached. |
| // If retry count runs out, or error condition is unrecoverable, it calls |
| // Delegate::OnOAuth2TokenFetchFailed(). |
| void RetryOnError(const GoogleServiceAuthError& error, |
| const base::Closure& task); |
| |
| // Passes |token| and |error| to the |callback_|. |
| void ForwardPolicyToken(const std::string& token, |
| const GoogleServiceAuthError& error); |
| |
| // Auth code which is used to retreive a refresh token. |
| std::string auth_code_; |
| |
| scoped_refptr<network::SharedURLLoaderFactory> auth_url_loader_factory_; |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory_; |
| std::unique_ptr<GaiaAuthFetcher> refresh_token_fetcher_; |
| std::unique_ptr<OAuth2AccessTokenFetcher> access_token_fetcher_; |
| |
| // OAuth2 refresh token. Could come either from the outside or through |
| // refresh token fetching flow within this class. |
| std::string oauth2_refresh_token_; |
| |
| // OAuth2 access token. |
| std::string oauth2_access_token_; |
| |
| // The retry counter. Increment this only when failure happened. |
| int retry_count_ = 0; |
| |
| // True if we have already failed to fetch the policy. |
| bool failed_ = false; |
| |
| // The callback to invoke when done. |
| TokenCallback callback_; |
| |
| base::WeakPtrFactory<PolicyOAuth2TokenFetcherImpl> weak_ptr_factory_; |
| |
| DISALLOW_COPY_AND_ASSIGN(PolicyOAuth2TokenFetcherImpl); |
| }; |
| |
| PolicyOAuth2TokenFetcherImpl::PolicyOAuth2TokenFetcherImpl() |
| : weak_ptr_factory_(this) {} |
| |
| PolicyOAuth2TokenFetcherImpl::~PolicyOAuth2TokenFetcherImpl() {} |
| |
| void PolicyOAuth2TokenFetcherImpl::StartWithSigninURLLoaderFactory( |
| scoped_refptr<network::SharedURLLoaderFactory> auth_url_loader_factory, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) { |
| DCHECK(!refresh_token_fetcher_ && !access_token_fetcher_); |
| |
| auth_url_loader_factory_ = auth_url_loader_factory; |
| system_url_loader_factory_ = system_url_loader_factory; |
| callback_ = callback; |
| StartFetchingRefreshToken(); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::StartWithAuthCode( |
| const std::string& auth_code, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) { |
| DCHECK(!refresh_token_fetcher_ && !access_token_fetcher_); |
| |
| auth_code_ = auth_code; |
| system_url_loader_factory_ = system_url_loader_factory; |
| callback_ = callback; |
| StartFetchingRefreshToken(); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::StartWithRefreshToken( |
| const std::string& oauth2_refresh_token, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) { |
| DCHECK(!refresh_token_fetcher_ && !access_token_fetcher_); |
| |
| oauth2_refresh_token_ = oauth2_refresh_token; |
| system_url_loader_factory_ = system_url_loader_factory; |
| callback_ = callback; |
| StartFetchingAccessToken(); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::StartFetchingRefreshToken() { |
| // Don't fetch tokens for test. |
| if (base::CommandLine::ForCurrentProcess()->HasSwitch( |
| chromeos::switches::kDisableGaiaServices)) { |
| failed_ = true; |
| ForwardPolicyToken( |
| std::string(), |
| GoogleServiceAuthError(GoogleServiceAuthError::CONNECTION_FAILED)); |
| return; |
| } |
| |
| if (auth_code_.empty()) { |
| refresh_token_fetcher_.reset(new GaiaAuthFetcher( |
| this, GaiaConstants::kChromeSource, auth_url_loader_factory_)); |
| refresh_token_fetcher_->DeprecatedStartCookieForOAuthLoginTokenExchange( |
| std::string()); |
| } else { |
| refresh_token_fetcher_.reset(new GaiaAuthFetcher( |
| this, GaiaConstants::kChromeSource, system_url_loader_factory_)); |
| refresh_token_fetcher_->StartAuthCodeForOAuth2TokenExchange(auth_code_); |
| } |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::StartFetchingAccessToken() { |
| std::vector<std::string> scopes; |
| scopes.push_back(GaiaConstants::kDeviceManagementServiceOAuth); |
| scopes.push_back(GaiaConstants::kOAuthWrapBridgeUserInfoScope); |
| access_token_fetcher_.reset(new OAuth2AccessTokenFetcherImpl( |
| this, system_url_loader_factory_, oauth2_refresh_token_)); |
| access_token_fetcher_->Start( |
| GaiaUrls::GetInstance()->oauth2_chrome_client_id(), |
| GaiaUrls::GetInstance()->oauth2_chrome_client_secret(), |
| scopes); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::OnClientOAuthSuccess( |
| const GaiaAuthConsumer::ClientOAuthResult& oauth2_tokens) { |
| VLOG(1) << "OAuth2 tokens for policy fetching succeeded."; |
| oauth2_refresh_token_ = oauth2_tokens.refresh_token; |
| retry_count_ = 0; |
| StartFetchingAccessToken(); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::OnClientOAuthFailure( |
| const GoogleServiceAuthError& error) { |
| VLOG(1) << "OAuth2 tokens fetch for policy fetch failed! (error = " |
| << error.state() << ")"; |
| RetryOnError( |
| error, |
| base::Bind(&PolicyOAuth2TokenFetcherImpl::StartFetchingRefreshToken, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::OnGetTokenSuccess( |
| const std::string& access_token, |
| const base::Time& expiration_time) { |
| VLOG(1) << "OAuth2 access token (device management) fetching succeeded."; |
| oauth2_access_token_ = access_token; |
| ForwardPolicyToken(access_token, |
| GoogleServiceAuthError(GoogleServiceAuthError::NONE)); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::OnGetTokenFailure( |
| const GoogleServiceAuthError& error) { |
| LOG(ERROR) << "OAuth2 access token (device management) fetching failed!"; |
| RetryOnError( |
| error, base::Bind(&PolicyOAuth2TokenFetcherImpl::StartFetchingAccessToken, |
| weak_ptr_factory_.GetWeakPtr())); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::RetryOnError( |
| const GoogleServiceAuthError& error, |
| const base::Closure& task) { |
| DCHECK_CURRENTLY_ON(BrowserThread::UI); |
| if (error.IsTransientError() && retry_count_ < kMaxRequestAttemptCount) { |
| retry_count_++; |
| BrowserThread::PostDelayedTask( |
| BrowserThread::UI, FROM_HERE, task, |
| base::TimeDelta::FromMilliseconds(kRequestRestartDelay)); |
| return; |
| } |
| LOG(ERROR) << "Unrecoverable error or retry count max reached: " |
| << error.state(); |
| failed_ = true; |
| // Invoking the |callback_| signals to the owner of this object that it has |
| // completed, and the owner may delete this object on the callback method. |
| // So don't rely on |this| still being valid after ForwardPolicyToken() |
| // returns i.e. don't write to |failed_| or other fields. |
| ForwardPolicyToken(std::string(), error); |
| } |
| |
| void PolicyOAuth2TokenFetcherImpl::ForwardPolicyToken( |
| const std::string& token, |
| const GoogleServiceAuthError& error) { |
| if (!callback_.is_null()) |
| callback_.Run(token, error); |
| } |
| |
| // Fake token fetcher that immediately returns tokens without making network |
| // requests. |
| class PolicyOAuth2TokenFetcherFake : public PolicyOAuth2TokenFetcher { |
| public: |
| PolicyOAuth2TokenFetcherFake() {} |
| ~PolicyOAuth2TokenFetcherFake() override {} |
| |
| private: |
| // PolicyOAuth2TokenFetcher overrides. |
| void StartWithSigninURLLoaderFactory( |
| scoped_refptr<network::SharedURLLoaderFactory> auth_url_loader_factory, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override { |
| ForwardPolicyToken(callback); |
| } |
| |
| void StartWithAuthCode( |
| const std::string& auth_code, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override { |
| ForwardPolicyToken(callback); |
| } |
| |
| void StartWithRefreshToken( |
| const std::string& oauth2_refresh_token, |
| scoped_refptr<network::SharedURLLoaderFactory> system_url_loader_factory, |
| const TokenCallback& callback) override { |
| ForwardPolicyToken(callback); |
| } |
| |
| bool Failed() const override { return false; } |
| const std::string& OAuth2RefreshToken() const override { |
| return refresh_token_; |
| } |
| const std::string& OAuth2AccessToken() const override { |
| return access_token_; |
| } |
| |
| private: |
| void ForwardPolicyToken(const TokenCallback& callback) { |
| if (!callback.is_null()) |
| callback.Run(access_token_, GoogleServiceAuthError::AuthErrorNone()); |
| } |
| |
| const std::string refresh_token_ = "fake_refresh_token"; |
| const std::string access_token_ = "fake_access_token"; |
| |
| DISALLOW_COPY_AND_ASSIGN(PolicyOAuth2TokenFetcherFake); |
| }; |
| |
| } // namespace |
| |
| // static |
| void PolicyOAuth2TokenFetcher::UseFakeTokensForTesting() { |
| use_fake_tokens_for_testing_ = true; |
| } |
| |
| // static |
| PolicyOAuth2TokenFetcher* PolicyOAuth2TokenFetcher::CreateInstance() { |
| if (use_fake_tokens_for_testing_) |
| return new PolicyOAuth2TokenFetcherFake(); |
| return new PolicyOAuth2TokenFetcherImpl(); |
| } |
| |
| PolicyOAuth2TokenFetcher::PolicyOAuth2TokenFetcher() {} |
| |
| PolicyOAuth2TokenFetcher::~PolicyOAuth2TokenFetcher() {} |
| |
| } // namespace policy |