123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403 |
- // 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.
- //
- // A complete set of unit tests for OAuth2AccessTokenFetcherImpl.
- #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
- #include <memory>
- #include <string>
- #include "base/bind.h"
- #include "base/run_loop.h"
- #include "base/strings/stringprintf.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/task_environment.h"
- #include "base/time/time.h"
- #include "google_apis/gaia/gaia_access_token_fetcher.h"
- #include "google_apis/gaia/gaia_urls.h"
- #include "google_apis/gaia/google_service_auth_error.h"
- #include "google_apis/gaia/oauth2_access_token_consumer.h"
- #include "net/base/net_errors.h"
- #include "net/http/http_status_code.h"
- #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
- #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
- #include "services/network/public/mojom/url_response_head.mojom.h"
- #include "services/network/test/test_url_loader_factory.h"
- #include "services/network/test/test_utils.h"
- #include "testing/gmock/include/gmock/gmock.h"
- #include "testing/gtest/include/gtest/gtest.h"
- #include "url/gurl.h"
- using testing::_;
- namespace {
- using ScopeList = std::vector<std::string>;
- constexpr char kValidTokenResponse[] = R"(
- {
- "access_token": "at1",
- "expires_in": 3600,
- "token_type": "Bearer",
- "id_token": "id_token"
- })";
- constexpr char kTokenResponseNoAccessToken[] = R"(
- {
- "expires_in": 3600,
- "token_type": "Bearer"
- })";
- constexpr char kValidFailureTokenResponse[] = R"(
- {
- "error": "invalid_grant"
- })";
- class MockOAuth2AccessTokenConsumer : public OAuth2AccessTokenConsumer {
- public:
- MockOAuth2AccessTokenConsumer() = default;
- ~MockOAuth2AccessTokenConsumer() override = default;
- MOCK_METHOD1(OnGetTokenSuccess,
- void(const OAuth2AccessTokenConsumer::TokenResponse&));
- MOCK_METHOD1(OnGetTokenFailure, void(const GoogleServiceAuthError& error));
- std::string GetConsumerName() const override {
- return "oauth2_access_token_fetcher_impl_unittest";
- }
- };
- class URLLoaderFactoryInterceptor {
- public:
- MOCK_METHOD1(Intercept, void(const network::ResourceRequest&));
- };
- MATCHER_P(resourceRequestUrlEquals, url, "") {
- return arg.url == url;
- }
- } // namespace
- class OAuth2AccessTokenFetcherImplTest : public testing::Test {
- public:
- OAuth2AccessTokenFetcherImplTest()
- : fetcher_(GaiaAccessTokenFetcher::
- CreateExchangeRefreshTokenForAccessTokenInstance(
- &consumer_,
- url_loader_factory_.GetSafeWeakWrapper(),
- "refresh_token")) {
- url_loader_factory_.SetInterceptor(base::BindRepeating(
- &URLLoaderFactoryInterceptor::Intercept,
- base::Unretained(&url_loader_factory_interceptor_)));
- base::RunLoop().RunUntilIdle();
- }
- void SetupGetAccessToken(int net_error_code,
- net::HttpStatusCode http_response_code,
- const std::string& body) {
- GURL url(GaiaUrls::GetInstance()->oauth2_token_url());
- if (net_error_code == net::OK) {
- url_loader_factory_.AddResponse(url.spec(), body, http_response_code);
- } else {
- url_loader_factory_.AddResponse(
- url, network::mojom::URLResponseHead::New(), body,
- network::URLLoaderCompletionStatus(net_error_code));
- }
- EXPECT_CALL(url_loader_factory_interceptor_,
- Intercept(resourceRequestUrlEquals(url)));
- }
- void SetupProxyError() {
- GURL url(GaiaUrls::GetInstance()->oauth2_token_url());
- url_loader_factory_.AddResponse(
- url,
- network::CreateURLResponseHead(net::HTTP_PROXY_AUTHENTICATION_REQUIRED),
- std::string(),
- network::URLLoaderCompletionStatus(net::ERR_TUNNEL_CONNECTION_FAILED),
- network::TestURLLoaderFactory::Redirects(),
- network::TestURLLoaderFactory::kSendHeadersOnNetworkError);
- EXPECT_CALL(url_loader_factory_interceptor_,
- Intercept(resourceRequestUrlEquals(url)));
- }
- base::HistogramTester* histogram_tester() { return &histogram_tester_; }
- protected:
- base::test::SingleThreadTaskEnvironment task_environment_;
- MockOAuth2AccessTokenConsumer consumer_;
- URLLoaderFactoryInterceptor url_loader_factory_interceptor_;
- network::TestURLLoaderFactory url_loader_factory_;
- std::unique_ptr<GaiaAccessTokenFetcher> fetcher_;
- base::HistogramTester histogram_tester_;
- };
- // These four tests time out, see http://crbug.com/113446.
- TEST_F(OAuth2AccessTokenFetcherImplTest, GetAccessTokenRequestFailure) {
- SetupGetAccessToken(net::ERR_FAILED, net::HTTP_OK, std::string());
- EXPECT_CALL(consumer_, OnGetTokenFailure(_)).Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName,
- net::ERR_FAILED, 1);
- histogram_tester()->ExpectTotalCount(
- GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName, 0);
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, GetAccessTokenResponseCodeFailure) {
- SetupGetAccessToken(net::OK, net::HTTP_FORBIDDEN, std::string());
- EXPECT_CALL(consumer_, OnGetTokenFailure(_)).Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- // Error tag does not exist in the response body.
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
- OAuth2AccessTokenFetcherImpl::kErrorUnexpectedFormat, 1);
- }
- // Regression test for https://crbug.com/914672
- TEST_F(OAuth2AccessTokenFetcherImplTest, ProxyFailure) {
- GoogleServiceAuthError expected_error =
- GoogleServiceAuthError::FromConnectionError(
- net::ERR_TUNNEL_CONNECTION_FAILED);
- ASSERT_TRUE(expected_error.IsTransientError());
- SetupProxyError();
- EXPECT_CALL(consumer_, OnGetTokenFailure(expected_error)).Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, Success) {
- SetupGetAccessToken(net::OK, net::HTTP_OK, kValidTokenResponse);
- EXPECT_CALL(consumer_, OnGetTokenSuccess(_)).Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName, net::HTTP_OK,
- 1);
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
- OAuth2AccessTokenFetcherImpl::kOk, 1);
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, SuccessUnexpectedFormat) {
- SetupGetAccessToken(net::OK, net::HTTP_OK, std::string());
- EXPECT_CALL(consumer_, OnGetTokenFailure(GoogleServiceAuthError(
- GoogleServiceAuthError::SERVICE_UNAVAILABLE)))
- .Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName, net::HTTP_OK,
- 1);
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
- OAuth2AccessTokenFetcherImpl::kOkUnexpectedFormat, 1);
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, CancelOngoingRequest) {
- SetupGetAccessToken(net::OK, net::HTTP_OK, kValidTokenResponse);
- // `OnGetTokenSuccess()` should not be called.
- EXPECT_CALL(consumer_, OnGetTokenSuccess(_)).Times(0);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- fetcher_->CancelRequest();
- base::RunLoop().RunUntilIdle();
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyNoScope) {
- std::string body =
- "client_id=cid1&"
- "client_secret=cs1&"
- "grant_type=refresh_token&"
- "refresh_token=rt1";
- EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
- "cid1", "cs1", "rt1", std::string(), ScopeList()));
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyOneScope) {
- std::string body =
- "client_id=cid1&"
- "client_secret=cs1&"
- "grant_type=refresh_token&"
- "refresh_token=rt1&"
- "scope=https://www.googleapis.com/foo";
- ScopeList scopes = {"https://www.googleapis.com/foo"};
- EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
- "cid1", "cs1", "rt1", std::string(), scopes));
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyMultipleScopes) {
- std::string body =
- "client_id=cid1&"
- "client_secret=cs1&"
- "grant_type=refresh_token&"
- "refresh_token=rt1&"
- "scope=https://www.googleapis.com/foo+"
- "https://www.googleapis.com/bar+"
- "https://www.googleapis.com/baz";
- ScopeList scopes = {"https://www.googleapis.com/foo",
- "https://www.googleapis.com/bar",
- "https://www.googleapis.com/baz"};
- EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
- "cid1", "cs1", "rt1", std::string(), scopes));
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseNoBody) {
- OAuth2AccessTokenConsumer::TokenResponse token_response;
- EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
- "", &token_response));
- EXPECT_TRUE(token_response.access_token.empty());
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseBadJson) {
- OAuth2AccessTokenConsumer::TokenResponse token_response;
- EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
- "foo", &token_response));
- EXPECT_TRUE(token_response.access_token.empty());
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest,
- ParseGetAccessTokenResponseNoAccessToken) {
- OAuth2AccessTokenConsumer::TokenResponse token_response;
- EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
- kTokenResponseNoAccessToken, &token_response));
- EXPECT_TRUE(token_response.access_token.empty());
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseSuccess) {
- OAuth2AccessTokenConsumer::TokenResponse token_response;
- EXPECT_TRUE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
- kValidTokenResponse, &token_response));
- EXPECT_EQ("at1", token_response.access_token);
- base::TimeDelta expires_in =
- token_response.expiration_time - base::Time::Now();
- EXPECT_LT(0, expires_in.InSeconds());
- EXPECT_GT(3600, expires_in.InSeconds());
- EXPECT_EQ("id_token", token_response.id_token);
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest,
- ParseGetAccessTokenFailureInvalidError) {
- std::string error;
- EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
- kTokenResponseNoAccessToken, &error));
- EXPECT_TRUE(error.empty());
- }
- TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenFailure) {
- std::string error;
- EXPECT_TRUE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
- kValidFailureTokenResponse, &error));
- EXPECT_EQ("invalid_grant", error);
- }
- struct OAuth2ErrorCodesTestParam {
- const char* error_code;
- net::HttpStatusCode httpStatusCode;
- OAuth2AccessTokenFetcherImpl::OAuth2Response expected_sample;
- GoogleServiceAuthError::State expected_error_state;
- };
- class OAuth2ErrorCodesTest
- : public OAuth2AccessTokenFetcherImplTest,
- public ::testing::WithParamInterface<OAuth2ErrorCodesTestParam> {
- public:
- OAuth2ErrorCodesTest() = default;
- OAuth2ErrorCodesTest(const OAuth2ErrorCodesTest&) = delete;
- OAuth2ErrorCodesTest& operator=(const OAuth2ErrorCodesTest&) = delete;
- GoogleServiceAuthError GetGoogleServiceAuthError(
- const std::string& error_message) {
- switch (GetParam().expected_error_state) {
- case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
- return GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
- GoogleServiceAuthError::InvalidGaiaCredentialsReason::
- CREDENTIALS_REJECTED_BY_SERVER);
- case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
- return GoogleServiceAuthError::FromServiceUnavailable(error_message);
- case GoogleServiceAuthError::SERVICE_ERROR:
- return GoogleServiceAuthError::FromServiceError(error_message);
- case GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR:
- return GoogleServiceAuthError::FromScopeLimitedUnrecoverableError(
- error_message);
- case GoogleServiceAuthError::NONE:
- case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
- case GoogleServiceAuthError::CONNECTION_FAILED:
- case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
- case GoogleServiceAuthError::REQUEST_CANCELED:
- case GoogleServiceAuthError::NUM_STATES:
- NOTREACHED();
- return GoogleServiceAuthError::AuthErrorNone();
- }
- }
- };
- const OAuth2ErrorCodesTestParam kOAuth2ErrorCodesTable[] = {
- {"invalid_request", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kInvalidRequest,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"invalid_client", net::HTTP_UNAUTHORIZED,
- OAuth2AccessTokenFetcherImpl::kInvalidClient,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"invalid_grant", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kInvalidGrant,
- GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS},
- {"unauthorized_client", net::HTTP_UNAUTHORIZED,
- OAuth2AccessTokenFetcherImpl::kUnauthorizedClient,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"unsupported_grant_type", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kUnsuportedGrantType,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"invalid_scope", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kInvalidScope,
- GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR},
- {"restricted_client", net::HTTP_FORBIDDEN,
- OAuth2AccessTokenFetcherImpl::kRestrictedClient,
- GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR},
- {"rate_limit_exceeded", net::HTTP_FORBIDDEN,
- OAuth2AccessTokenFetcherImpl::kRateLimitExceeded,
- GoogleServiceAuthError::SERVICE_UNAVAILABLE},
- {"internal_failure", net::HTTP_INTERNAL_SERVER_ERROR,
- OAuth2AccessTokenFetcherImpl::kInternalFailure,
- GoogleServiceAuthError::SERVICE_UNAVAILABLE},
- {"", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kErrorUnexpectedFormat,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"", net::HTTP_OK, OAuth2AccessTokenFetcherImpl::kOkUnexpectedFormat,
- GoogleServiceAuthError::SERVICE_UNAVAILABLE},
- {"unknown_error", net::HTTP_BAD_REQUEST,
- OAuth2AccessTokenFetcherImpl::kUnknownError,
- GoogleServiceAuthError::SERVICE_ERROR},
- {"unknown_error", net::HTTP_INTERNAL_SERVER_ERROR,
- OAuth2AccessTokenFetcherImpl::kUnknownError,
- GoogleServiceAuthError::SERVICE_UNAVAILABLE}};
- TEST_P(OAuth2ErrorCodesTest, TableRowTest) {
- std::string response_body = base::StringPrintf(R"(
- {
- "error": "%s"
- })",
- GetParam().error_code);
- GoogleServiceAuthError expected_error =
- GetGoogleServiceAuthError(response_body);
- SetupGetAccessToken(net::OK, GetParam().httpStatusCode, response_body);
- EXPECT_CALL(consumer_, OnGetTokenFailure(expected_error)).Times(1);
- fetcher_->Start("client_id", "client_secret", ScopeList());
- base::RunLoop().RunUntilIdle();
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
- GetParam().expected_sample, 1);
- histogram_tester()->ExpectUniqueSample(
- GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName,
- GetParam().httpStatusCode, 1);
- }
- INSTANTIATE_TEST_SUITE_P(OAuth2ErrorCodesTableTest,
- OAuth2ErrorCodesTest,
- ::testing::ValuesIn(kOAuth2ErrorCodesTable));
|