oauth2_access_token_fetcher_impl_unittest.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403
  1. // Copyright 2014 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. //
  5. // A complete set of unit tests for OAuth2AccessTokenFetcherImpl.
  6. #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
  7. #include <memory>
  8. #include <string>
  9. #include "base/bind.h"
  10. #include "base/run_loop.h"
  11. #include "base/strings/stringprintf.h"
  12. #include "base/test/metrics/histogram_tester.h"
  13. #include "base/test/task_environment.h"
  14. #include "base/time/time.h"
  15. #include "google_apis/gaia/gaia_access_token_fetcher.h"
  16. #include "google_apis/gaia/gaia_urls.h"
  17. #include "google_apis/gaia/google_service_auth_error.h"
  18. #include "google_apis/gaia/oauth2_access_token_consumer.h"
  19. #include "net/base/net_errors.h"
  20. #include "net/http/http_status_code.h"
  21. #include "net/traffic_annotation/network_traffic_annotation_test_helper.h"
  22. #include "services/network/public/cpp/weak_wrapper_shared_url_loader_factory.h"
  23. #include "services/network/public/mojom/url_response_head.mojom.h"
  24. #include "services/network/test/test_url_loader_factory.h"
  25. #include "services/network/test/test_utils.h"
  26. #include "testing/gmock/include/gmock/gmock.h"
  27. #include "testing/gtest/include/gtest/gtest.h"
  28. #include "url/gurl.h"
  29. using testing::_;
  30. namespace {
  31. using ScopeList = std::vector<std::string>;
  32. constexpr char kValidTokenResponse[] = R"(
  33. {
  34. "access_token": "at1",
  35. "expires_in": 3600,
  36. "token_type": "Bearer",
  37. "id_token": "id_token"
  38. })";
  39. constexpr char kTokenResponseNoAccessToken[] = R"(
  40. {
  41. "expires_in": 3600,
  42. "token_type": "Bearer"
  43. })";
  44. constexpr char kValidFailureTokenResponse[] = R"(
  45. {
  46. "error": "invalid_grant"
  47. })";
  48. class MockOAuth2AccessTokenConsumer : public OAuth2AccessTokenConsumer {
  49. public:
  50. MockOAuth2AccessTokenConsumer() = default;
  51. ~MockOAuth2AccessTokenConsumer() override = default;
  52. MOCK_METHOD1(OnGetTokenSuccess,
  53. void(const OAuth2AccessTokenConsumer::TokenResponse&));
  54. MOCK_METHOD1(OnGetTokenFailure, void(const GoogleServiceAuthError& error));
  55. std::string GetConsumerName() const override {
  56. return "oauth2_access_token_fetcher_impl_unittest";
  57. }
  58. };
  59. class URLLoaderFactoryInterceptor {
  60. public:
  61. MOCK_METHOD1(Intercept, void(const network::ResourceRequest&));
  62. };
  63. MATCHER_P(resourceRequestUrlEquals, url, "") {
  64. return arg.url == url;
  65. }
  66. } // namespace
  67. class OAuth2AccessTokenFetcherImplTest : public testing::Test {
  68. public:
  69. OAuth2AccessTokenFetcherImplTest()
  70. : fetcher_(GaiaAccessTokenFetcher::
  71. CreateExchangeRefreshTokenForAccessTokenInstance(
  72. &consumer_,
  73. url_loader_factory_.GetSafeWeakWrapper(),
  74. "refresh_token")) {
  75. url_loader_factory_.SetInterceptor(base::BindRepeating(
  76. &URLLoaderFactoryInterceptor::Intercept,
  77. base::Unretained(&url_loader_factory_interceptor_)));
  78. base::RunLoop().RunUntilIdle();
  79. }
  80. void SetupGetAccessToken(int net_error_code,
  81. net::HttpStatusCode http_response_code,
  82. const std::string& body) {
  83. GURL url(GaiaUrls::GetInstance()->oauth2_token_url());
  84. if (net_error_code == net::OK) {
  85. url_loader_factory_.AddResponse(url.spec(), body, http_response_code);
  86. } else {
  87. url_loader_factory_.AddResponse(
  88. url, network::mojom::URLResponseHead::New(), body,
  89. network::URLLoaderCompletionStatus(net_error_code));
  90. }
  91. EXPECT_CALL(url_loader_factory_interceptor_,
  92. Intercept(resourceRequestUrlEquals(url)));
  93. }
  94. void SetupProxyError() {
  95. GURL url(GaiaUrls::GetInstance()->oauth2_token_url());
  96. url_loader_factory_.AddResponse(
  97. url,
  98. network::CreateURLResponseHead(net::HTTP_PROXY_AUTHENTICATION_REQUIRED),
  99. std::string(),
  100. network::URLLoaderCompletionStatus(net::ERR_TUNNEL_CONNECTION_FAILED),
  101. network::TestURLLoaderFactory::Redirects(),
  102. network::TestURLLoaderFactory::kSendHeadersOnNetworkError);
  103. EXPECT_CALL(url_loader_factory_interceptor_,
  104. Intercept(resourceRequestUrlEquals(url)));
  105. }
  106. base::HistogramTester* histogram_tester() { return &histogram_tester_; }
  107. protected:
  108. base::test::SingleThreadTaskEnvironment task_environment_;
  109. MockOAuth2AccessTokenConsumer consumer_;
  110. URLLoaderFactoryInterceptor url_loader_factory_interceptor_;
  111. network::TestURLLoaderFactory url_loader_factory_;
  112. std::unique_ptr<GaiaAccessTokenFetcher> fetcher_;
  113. base::HistogramTester histogram_tester_;
  114. };
  115. // These four tests time out, see http://crbug.com/113446.
  116. TEST_F(OAuth2AccessTokenFetcherImplTest, GetAccessTokenRequestFailure) {
  117. SetupGetAccessToken(net::ERR_FAILED, net::HTTP_OK, std::string());
  118. EXPECT_CALL(consumer_, OnGetTokenFailure(_)).Times(1);
  119. fetcher_->Start("client_id", "client_secret", ScopeList());
  120. base::RunLoop().RunUntilIdle();
  121. histogram_tester()->ExpectUniqueSample(
  122. GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName,
  123. net::ERR_FAILED, 1);
  124. histogram_tester()->ExpectTotalCount(
  125. GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName, 0);
  126. }
  127. TEST_F(OAuth2AccessTokenFetcherImplTest, GetAccessTokenResponseCodeFailure) {
  128. SetupGetAccessToken(net::OK, net::HTTP_FORBIDDEN, std::string());
  129. EXPECT_CALL(consumer_, OnGetTokenFailure(_)).Times(1);
  130. fetcher_->Start("client_id", "client_secret", ScopeList());
  131. base::RunLoop().RunUntilIdle();
  132. // Error tag does not exist in the response body.
  133. histogram_tester()->ExpectUniqueSample(
  134. GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
  135. OAuth2AccessTokenFetcherImpl::kErrorUnexpectedFormat, 1);
  136. }
  137. // Regression test for https://crbug.com/914672
  138. TEST_F(OAuth2AccessTokenFetcherImplTest, ProxyFailure) {
  139. GoogleServiceAuthError expected_error =
  140. GoogleServiceAuthError::FromConnectionError(
  141. net::ERR_TUNNEL_CONNECTION_FAILED);
  142. ASSERT_TRUE(expected_error.IsTransientError());
  143. SetupProxyError();
  144. EXPECT_CALL(consumer_, OnGetTokenFailure(expected_error)).Times(1);
  145. fetcher_->Start("client_id", "client_secret", ScopeList());
  146. base::RunLoop().RunUntilIdle();
  147. }
  148. TEST_F(OAuth2AccessTokenFetcherImplTest, Success) {
  149. SetupGetAccessToken(net::OK, net::HTTP_OK, kValidTokenResponse);
  150. EXPECT_CALL(consumer_, OnGetTokenSuccess(_)).Times(1);
  151. fetcher_->Start("client_id", "client_secret", ScopeList());
  152. base::RunLoop().RunUntilIdle();
  153. histogram_tester()->ExpectUniqueSample(
  154. GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName, net::HTTP_OK,
  155. 1);
  156. histogram_tester()->ExpectUniqueSample(
  157. GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
  158. OAuth2AccessTokenFetcherImpl::kOk, 1);
  159. }
  160. TEST_F(OAuth2AccessTokenFetcherImplTest, SuccessUnexpectedFormat) {
  161. SetupGetAccessToken(net::OK, net::HTTP_OK, std::string());
  162. EXPECT_CALL(consumer_, OnGetTokenFailure(GoogleServiceAuthError(
  163. GoogleServiceAuthError::SERVICE_UNAVAILABLE)))
  164. .Times(1);
  165. fetcher_->Start("client_id", "client_secret", ScopeList());
  166. base::RunLoop().RunUntilIdle();
  167. histogram_tester()->ExpectUniqueSample(
  168. GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName, net::HTTP_OK,
  169. 1);
  170. histogram_tester()->ExpectUniqueSample(
  171. GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
  172. OAuth2AccessTokenFetcherImpl::kOkUnexpectedFormat, 1);
  173. }
  174. TEST_F(OAuth2AccessTokenFetcherImplTest, CancelOngoingRequest) {
  175. SetupGetAccessToken(net::OK, net::HTTP_OK, kValidTokenResponse);
  176. // `OnGetTokenSuccess()` should not be called.
  177. EXPECT_CALL(consumer_, OnGetTokenSuccess(_)).Times(0);
  178. fetcher_->Start("client_id", "client_secret", ScopeList());
  179. fetcher_->CancelRequest();
  180. base::RunLoop().RunUntilIdle();
  181. }
  182. TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyNoScope) {
  183. std::string body =
  184. "client_id=cid1&"
  185. "client_secret=cs1&"
  186. "grant_type=refresh_token&"
  187. "refresh_token=rt1";
  188. EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
  189. "cid1", "cs1", "rt1", std::string(), ScopeList()));
  190. }
  191. TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyOneScope) {
  192. std::string body =
  193. "client_id=cid1&"
  194. "client_secret=cs1&"
  195. "grant_type=refresh_token&"
  196. "refresh_token=rt1&"
  197. "scope=https://www.googleapis.com/foo";
  198. ScopeList scopes = {"https://www.googleapis.com/foo"};
  199. EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
  200. "cid1", "cs1", "rt1", std::string(), scopes));
  201. }
  202. TEST_F(OAuth2AccessTokenFetcherImplTest, MakeGetAccessTokenBodyMultipleScopes) {
  203. std::string body =
  204. "client_id=cid1&"
  205. "client_secret=cs1&"
  206. "grant_type=refresh_token&"
  207. "refresh_token=rt1&"
  208. "scope=https://www.googleapis.com/foo+"
  209. "https://www.googleapis.com/bar+"
  210. "https://www.googleapis.com/baz";
  211. ScopeList scopes = {"https://www.googleapis.com/foo",
  212. "https://www.googleapis.com/bar",
  213. "https://www.googleapis.com/baz"};
  214. EXPECT_EQ(body, OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
  215. "cid1", "cs1", "rt1", std::string(), scopes));
  216. }
  217. TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseNoBody) {
  218. OAuth2AccessTokenConsumer::TokenResponse token_response;
  219. EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
  220. "", &token_response));
  221. EXPECT_TRUE(token_response.access_token.empty());
  222. }
  223. TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseBadJson) {
  224. OAuth2AccessTokenConsumer::TokenResponse token_response;
  225. EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
  226. "foo", &token_response));
  227. EXPECT_TRUE(token_response.access_token.empty());
  228. }
  229. TEST_F(OAuth2AccessTokenFetcherImplTest,
  230. ParseGetAccessTokenResponseNoAccessToken) {
  231. OAuth2AccessTokenConsumer::TokenResponse token_response;
  232. EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
  233. kTokenResponseNoAccessToken, &token_response));
  234. EXPECT_TRUE(token_response.access_token.empty());
  235. }
  236. TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenResponseSuccess) {
  237. OAuth2AccessTokenConsumer::TokenResponse token_response;
  238. EXPECT_TRUE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
  239. kValidTokenResponse, &token_response));
  240. EXPECT_EQ("at1", token_response.access_token);
  241. base::TimeDelta expires_in =
  242. token_response.expiration_time - base::Time::Now();
  243. EXPECT_LT(0, expires_in.InSeconds());
  244. EXPECT_GT(3600, expires_in.InSeconds());
  245. EXPECT_EQ("id_token", token_response.id_token);
  246. }
  247. TEST_F(OAuth2AccessTokenFetcherImplTest,
  248. ParseGetAccessTokenFailureInvalidError) {
  249. std::string error;
  250. EXPECT_FALSE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
  251. kTokenResponseNoAccessToken, &error));
  252. EXPECT_TRUE(error.empty());
  253. }
  254. TEST_F(OAuth2AccessTokenFetcherImplTest, ParseGetAccessTokenFailure) {
  255. std::string error;
  256. EXPECT_TRUE(OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
  257. kValidFailureTokenResponse, &error));
  258. EXPECT_EQ("invalid_grant", error);
  259. }
  260. struct OAuth2ErrorCodesTestParam {
  261. const char* error_code;
  262. net::HttpStatusCode httpStatusCode;
  263. OAuth2AccessTokenFetcherImpl::OAuth2Response expected_sample;
  264. GoogleServiceAuthError::State expected_error_state;
  265. };
  266. class OAuth2ErrorCodesTest
  267. : public OAuth2AccessTokenFetcherImplTest,
  268. public ::testing::WithParamInterface<OAuth2ErrorCodesTestParam> {
  269. public:
  270. OAuth2ErrorCodesTest() = default;
  271. OAuth2ErrorCodesTest(const OAuth2ErrorCodesTest&) = delete;
  272. OAuth2ErrorCodesTest& operator=(const OAuth2ErrorCodesTest&) = delete;
  273. GoogleServiceAuthError GetGoogleServiceAuthError(
  274. const std::string& error_message) {
  275. switch (GetParam().expected_error_state) {
  276. case GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS:
  277. return GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
  278. GoogleServiceAuthError::InvalidGaiaCredentialsReason::
  279. CREDENTIALS_REJECTED_BY_SERVER);
  280. case GoogleServiceAuthError::SERVICE_UNAVAILABLE:
  281. return GoogleServiceAuthError::FromServiceUnavailable(error_message);
  282. case GoogleServiceAuthError::SERVICE_ERROR:
  283. return GoogleServiceAuthError::FromServiceError(error_message);
  284. case GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR:
  285. return GoogleServiceAuthError::FromScopeLimitedUnrecoverableError(
  286. error_message);
  287. case GoogleServiceAuthError::NONE:
  288. case GoogleServiceAuthError::USER_NOT_SIGNED_UP:
  289. case GoogleServiceAuthError::CONNECTION_FAILED:
  290. case GoogleServiceAuthError::UNEXPECTED_SERVICE_RESPONSE:
  291. case GoogleServiceAuthError::REQUEST_CANCELED:
  292. case GoogleServiceAuthError::NUM_STATES:
  293. NOTREACHED();
  294. return GoogleServiceAuthError::AuthErrorNone();
  295. }
  296. }
  297. };
  298. const OAuth2ErrorCodesTestParam kOAuth2ErrorCodesTable[] = {
  299. {"invalid_request", net::HTTP_BAD_REQUEST,
  300. OAuth2AccessTokenFetcherImpl::kInvalidRequest,
  301. GoogleServiceAuthError::SERVICE_ERROR},
  302. {"invalid_client", net::HTTP_UNAUTHORIZED,
  303. OAuth2AccessTokenFetcherImpl::kInvalidClient,
  304. GoogleServiceAuthError::SERVICE_ERROR},
  305. {"invalid_grant", net::HTTP_BAD_REQUEST,
  306. OAuth2AccessTokenFetcherImpl::kInvalidGrant,
  307. GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS},
  308. {"unauthorized_client", net::HTTP_UNAUTHORIZED,
  309. OAuth2AccessTokenFetcherImpl::kUnauthorizedClient,
  310. GoogleServiceAuthError::SERVICE_ERROR},
  311. {"unsupported_grant_type", net::HTTP_BAD_REQUEST,
  312. OAuth2AccessTokenFetcherImpl::kUnsuportedGrantType,
  313. GoogleServiceAuthError::SERVICE_ERROR},
  314. {"invalid_scope", net::HTTP_BAD_REQUEST,
  315. OAuth2AccessTokenFetcherImpl::kInvalidScope,
  316. GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR},
  317. {"restricted_client", net::HTTP_FORBIDDEN,
  318. OAuth2AccessTokenFetcherImpl::kRestrictedClient,
  319. GoogleServiceAuthError::SCOPE_LIMITED_UNRECOVERABLE_ERROR},
  320. {"rate_limit_exceeded", net::HTTP_FORBIDDEN,
  321. OAuth2AccessTokenFetcherImpl::kRateLimitExceeded,
  322. GoogleServiceAuthError::SERVICE_UNAVAILABLE},
  323. {"internal_failure", net::HTTP_INTERNAL_SERVER_ERROR,
  324. OAuth2AccessTokenFetcherImpl::kInternalFailure,
  325. GoogleServiceAuthError::SERVICE_UNAVAILABLE},
  326. {"", net::HTTP_BAD_REQUEST,
  327. OAuth2AccessTokenFetcherImpl::kErrorUnexpectedFormat,
  328. GoogleServiceAuthError::SERVICE_ERROR},
  329. {"", net::HTTP_OK, OAuth2AccessTokenFetcherImpl::kOkUnexpectedFormat,
  330. GoogleServiceAuthError::SERVICE_UNAVAILABLE},
  331. {"unknown_error", net::HTTP_BAD_REQUEST,
  332. OAuth2AccessTokenFetcherImpl::kUnknownError,
  333. GoogleServiceAuthError::SERVICE_ERROR},
  334. {"unknown_error", net::HTTP_INTERNAL_SERVER_ERROR,
  335. OAuth2AccessTokenFetcherImpl::kUnknownError,
  336. GoogleServiceAuthError::SERVICE_UNAVAILABLE}};
  337. TEST_P(OAuth2ErrorCodesTest, TableRowTest) {
  338. std::string response_body = base::StringPrintf(R"(
  339. {
  340. "error": "%s"
  341. })",
  342. GetParam().error_code);
  343. GoogleServiceAuthError expected_error =
  344. GetGoogleServiceAuthError(response_body);
  345. SetupGetAccessToken(net::OK, GetParam().httpStatusCode, response_body);
  346. EXPECT_CALL(consumer_, OnGetTokenFailure(expected_error)).Times(1);
  347. fetcher_->Start("client_id", "client_secret", ScopeList());
  348. base::RunLoop().RunUntilIdle();
  349. histogram_tester()->ExpectUniqueSample(
  350. GaiaAccessTokenFetcher::kOAuth2ResponseHistogramName,
  351. GetParam().expected_sample, 1);
  352. histogram_tester()->ExpectUniqueSample(
  353. GaiaAccessTokenFetcher::kOAuth2NetResponseCodeHistogramName,
  354. GetParam().httpStatusCode, 1);
  355. }
  356. INSTANTIATE_TEST_SUITE_P(OAuth2ErrorCodesTableTest,
  357. OAuth2ErrorCodesTest,
  358. ::testing::ValuesIn(kOAuth2ErrorCodesTable));