oauth2_access_token_fetcher_impl.cc 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  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. #include "google_apis/gaia/oauth2_access_token_fetcher_impl.h"
  5. #include <algorithm>
  6. #include <string>
  7. #include <vector>
  8. #include "base/bind.h"
  9. #include "base/feature_list.h"
  10. #include "base/json/json_reader.h"
  11. #include "base/strings/escape.h"
  12. #include "base/strings/string_util.h"
  13. #include "base/strings/stringprintf.h"
  14. #include "base/time/time.h"
  15. #include "base/values.h"
  16. #include "google_apis/gaia/gaia_auth_util.h"
  17. #include "google_apis/gaia/google_service_auth_error.h"
  18. #include "net/http/http_status_code.h"
  19. #include "services/network/public/cpp/resource_request.h"
  20. #include "services/network/public/cpp/shared_url_loader_factory.h"
  21. #include "services/network/public/cpp/simple_url_loader.h"
  22. #include "services/network/public/mojom/url_response_head.mojom.h"
  23. namespace {
  24. const base::Feature kParseOauth2ErrorCode{"ParseOAuth2ErrorCode",
  25. base::FEATURE_ENABLED_BY_DEFAULT};
  26. constexpr char kGetAccessTokenBodyFormat[] =
  27. "client_id=%s&"
  28. "client_secret=%s&"
  29. "grant_type=%s&"
  30. "%s=%s";
  31. constexpr char kGetAccessTokenBodyWithScopeFormat[] =
  32. "client_id=%s&"
  33. "client_secret=%s&"
  34. "grant_type=%s&"
  35. "%s=%s&"
  36. "scope=%s";
  37. constexpr char kGrantTypeAuthCode[] = "authorization_code";
  38. constexpr char kGrantTypeRefreshToken[] = "refresh_token";
  39. constexpr char kKeyAuthCode[] = "code";
  40. constexpr char kKeyRefreshToken[] = "refresh_token";
  41. constexpr char kAccessTokenKey[] = "access_token";
  42. constexpr char krefreshTokenKey[] = "refresh_token";
  43. constexpr char kExpiresInKey[] = "expires_in";
  44. constexpr char kIdTokenKey[] = "id_token";
  45. constexpr char kErrorKey[] = "error";
  46. OAuth2AccessTokenFetcherImpl::OAuth2Response
  47. OAuth2ResponseErrorToOAuth2Response(const std::string& error) {
  48. if (error.empty())
  49. return OAuth2AccessTokenFetcherImpl::kErrorUnexpectedFormat;
  50. if (error == "invalid_request")
  51. return OAuth2AccessTokenFetcherImpl::kInvalidRequest;
  52. if (error == "invalid_client")
  53. return OAuth2AccessTokenFetcherImpl::kInvalidClient;
  54. if (error == "invalid_grant")
  55. return OAuth2AccessTokenFetcherImpl::kInvalidGrant;
  56. if (error == "unauthorized_client")
  57. return OAuth2AccessTokenFetcherImpl::kUnauthorizedClient;
  58. if (error == "unsupported_grant_type")
  59. return OAuth2AccessTokenFetcherImpl::kUnsuportedGrantType;
  60. if (error == "invalid_scope")
  61. return OAuth2AccessTokenFetcherImpl::kInvalidScope;
  62. if (error == "restricted_client")
  63. return OAuth2AccessTokenFetcherImpl::kRestrictedClient;
  64. if (error == "rate_limit_exceeded")
  65. return OAuth2AccessTokenFetcherImpl::kRateLimitExceeded;
  66. if (error == "internal_failure")
  67. return OAuth2AccessTokenFetcherImpl::kInternalFailure;
  68. return OAuth2AccessTokenFetcherImpl::kUnknownError;
  69. }
  70. static std::unique_ptr<network::SimpleURLLoader> CreateURLLoader(
  71. const GURL& url,
  72. const std::string& body,
  73. const net::NetworkTrafficAnnotationTag& traffic_annotation) {
  74. auto resource_request = std::make_unique<network::ResourceRequest>();
  75. resource_request->url = url;
  76. resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
  77. if (!body.empty())
  78. resource_request->method = "POST";
  79. auto url_loader = network::SimpleURLLoader::Create(
  80. std::move(resource_request), traffic_annotation);
  81. if (!body.empty())
  82. url_loader->AttachStringForUpload(body,
  83. "application/x-www-form-urlencoded");
  84. // We want to receive the body even on error, as it may contain the reason for
  85. // failure.
  86. url_loader->SetAllowHttpErrorResults(true);
  87. // Fetchers are sometimes cancelled because a network change was detected,
  88. // especially at startup and after sign-in on ChromeOS. Retrying once should
  89. // be enough in those cases; let the fetcher retry up to 3 times just in case.
  90. // http://crbug.com/163710
  91. url_loader->SetRetryOptions(
  92. 3, network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE);
  93. return url_loader;
  94. }
  95. } // namespace
  96. OAuth2AccessTokenFetcherImpl::OAuth2AccessTokenFetcherImpl(
  97. OAuth2AccessTokenConsumer* consumer,
  98. scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
  99. const std::string& refresh_token,
  100. const std::string& auth_code)
  101. : OAuth2AccessTokenFetcher(consumer),
  102. url_loader_factory_(url_loader_factory),
  103. refresh_token_(refresh_token),
  104. auth_code_(auth_code),
  105. state_(INITIAL) {
  106. // It's an error to specify neither a refresh token nor an auth code, or
  107. // to specify both at the same time.
  108. CHECK_NE(refresh_token_.empty(), auth_code_.empty());
  109. }
  110. OAuth2AccessTokenFetcherImpl::~OAuth2AccessTokenFetcherImpl() = default;
  111. void OAuth2AccessTokenFetcherImpl::CancelRequest() {
  112. state_ = GET_ACCESS_TOKEN_CANCELED;
  113. url_loader_.reset();
  114. }
  115. void OAuth2AccessTokenFetcherImpl::Start(
  116. const std::string& client_id,
  117. const std::string& client_secret,
  118. const std::vector<std::string>& scopes) {
  119. client_id_ = client_id;
  120. client_secret_ = client_secret;
  121. scopes_ = scopes;
  122. StartGetAccessToken();
  123. }
  124. void OAuth2AccessTokenFetcherImpl::StartGetAccessToken() {
  125. CHECK_EQ(INITIAL, state_);
  126. state_ = GET_ACCESS_TOKEN_STARTED;
  127. url_loader_ = CreateURLLoader(
  128. GetAccessTokenURL(),
  129. MakeGetAccessTokenBody(client_id_, client_secret_, refresh_token_,
  130. auth_code_, scopes_),
  131. GetTrafficAnnotationTag());
  132. // It's safe to use Unretained below as the |url_loader_| is owned by |this|.
  133. url_loader_->DownloadToString(
  134. url_loader_factory_.get(),
  135. base::BindOnce(&OAuth2AccessTokenFetcherImpl::OnURLLoadComplete,
  136. base::Unretained(this)),
  137. 1024 * 1024);
  138. }
  139. void OAuth2AccessTokenFetcherImpl::EndGetAccessToken(
  140. std::unique_ptr<std::string> response_body) {
  141. CHECK_EQ(GET_ACCESS_TOKEN_STARTED, state_);
  142. state_ = GET_ACCESS_TOKEN_DONE;
  143. bool net_failure = url_loader_->NetError() != net::OK ||
  144. !url_loader_->ResponseInfo() ||
  145. !url_loader_->ResponseInfo()->headers;
  146. if (net_failure) {
  147. int net_error = url_loader_->NetError();
  148. DLOG(WARNING) << "Could not reach Authorization servers: errno "
  149. << net_error;
  150. RecordResponseCodeUma(net_error);
  151. OnGetTokenFailure(GoogleServiceAuthError::FromConnectionError(net_error));
  152. return;
  153. }
  154. int response_code = url_loader_->ResponseInfo()->headers->response_code();
  155. RecordResponseCodeUma(response_code);
  156. std::string response_str = response_body ? *response_body : "";
  157. if (response_code == net::HTTP_OK) {
  158. OAuth2AccessTokenConsumer::TokenResponse token_response;
  159. if (ParseGetAccessTokenSuccessResponse(response_str, &token_response)) {
  160. RecordOAuth2Response(OAuth2Response::kOk);
  161. OnGetTokenSuccess(token_response);
  162. } else {
  163. // Successful (net::HTTP_OK) unexpected format is considered as a
  164. // transient error.
  165. DLOG(WARNING) << "Response doesn't match expected format";
  166. RecordOAuth2Response(OAuth2Response::kOkUnexpectedFormat);
  167. OnGetTokenFailure(
  168. GoogleServiceAuthError::FromServiceUnavailable(response_str));
  169. }
  170. return;
  171. }
  172. // Request failed
  173. std::string oauth2_error;
  174. ParseGetAccessTokenFailureResponse(response_str, &oauth2_error);
  175. OAuth2Response response = OAuth2ResponseErrorToOAuth2Response(oauth2_error);
  176. RecordOAuth2Response(response);
  177. absl::optional<GoogleServiceAuthError> error;
  178. if (base::FeatureList::IsEnabled(kParseOauth2ErrorCode)) {
  179. switch (response) {
  180. case kOk:
  181. case kOkUnexpectedFormat:
  182. NOTREACHED();
  183. break;
  184. case kRateLimitExceeded:
  185. case kInternalFailure:
  186. // Transient error.
  187. error = GoogleServiceAuthError::FromServiceUnavailable(response_str);
  188. break;
  189. case kInvalidGrant:
  190. // Persistent error requiring the user to sign in again.
  191. error = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
  192. GoogleServiceAuthError::InvalidGaiaCredentialsReason::
  193. CREDENTIALS_REJECTED_BY_SERVER);
  194. break;
  195. case kInvalidScope:
  196. case kRestrictedClient:
  197. // Scope persistent error that can't be fixed by user action.
  198. error = GoogleServiceAuthError::FromScopeLimitedUnrecoverableError(
  199. response_str);
  200. break;
  201. case kInvalidRequest:
  202. case kInvalidClient:
  203. case kUnauthorizedClient:
  204. case kUnsuportedGrantType:
  205. DLOG(ERROR) << "Unexpected persistent error: error code = "
  206. << oauth2_error;
  207. error = GoogleServiceAuthError::FromServiceError(response_str);
  208. break;
  209. case kUnknownError:
  210. case kErrorUnexpectedFormat:
  211. // Failed request with unknown error code or unexpected format is
  212. // treated as a persistent error case.
  213. DLOG(ERROR) << "Unexpected error/format: error code = " << oauth2_error;
  214. break;
  215. }
  216. }
  217. if (!error.has_value()) {
  218. // Fallback to http status code.
  219. if (response_code == net::HTTP_OK) {
  220. NOTREACHED();
  221. } else if (response_code == net::HTTP_FORBIDDEN ||
  222. response_code == net::HTTP_PROXY_AUTHENTICATION_REQUIRED ||
  223. response_code >= net::HTTP_INTERNAL_SERVER_ERROR) {
  224. // HTTP_FORBIDDEN (403): is treated as transient error, because it may be
  225. // '403 Rate Limit Exeeded.'
  226. // HTTP_PROXY_AUTHENTICATION_REQUIRED (407): is treated as a network error
  227. // HTTP_INTERNAL_SERVER_ERROR: 5xx is always treated as transient.
  228. error = GoogleServiceAuthError::FromServiceUnavailable(response_str);
  229. } else {
  230. // HTTP_BAD_REQUEST (400) or other response codes are treated as
  231. // persistent errors.
  232. // HTTP_BAD_REQUEST errors usually contains errors as per
  233. // http://tools.ietf.org/html/rfc6749#section-5.2.
  234. if (response == kInvalidGrant) {
  235. error = GoogleServiceAuthError::FromInvalidGaiaCredentialsReason(
  236. GoogleServiceAuthError::InvalidGaiaCredentialsReason::
  237. CREDENTIALS_REJECTED_BY_SERVER);
  238. } else {
  239. error = GoogleServiceAuthError::FromServiceError(response_str);
  240. }
  241. }
  242. }
  243. if (error.has_value())
  244. OnGetTokenFailure(error.value());
  245. }
  246. void OAuth2AccessTokenFetcherImpl::OnGetTokenSuccess(
  247. const OAuth2AccessTokenConsumer::TokenResponse& token_response) {
  248. FireOnGetTokenSuccess(token_response);
  249. }
  250. void OAuth2AccessTokenFetcherImpl::OnGetTokenFailure(
  251. const GoogleServiceAuthError& error) {
  252. state_ = ERROR_STATE;
  253. FireOnGetTokenFailure(error);
  254. }
  255. void OAuth2AccessTokenFetcherImpl::OnURLLoadComplete(
  256. std::unique_ptr<std::string> response_body) {
  257. CHECK_EQ(state_, GET_ACCESS_TOKEN_STARTED);
  258. EndGetAccessToken(std::move(response_body));
  259. }
  260. // static
  261. std::string OAuth2AccessTokenFetcherImpl::MakeGetAccessTokenBody(
  262. const std::string& client_id,
  263. const std::string& client_secret,
  264. const std::string& refresh_token,
  265. const std::string& auth_code,
  266. const std::vector<std::string>& scopes) {
  267. // It's an error to specify neither a refresh token nor an auth code, or
  268. // to specify both at the same time.
  269. CHECK_NE(refresh_token.empty(), auth_code.empty());
  270. std::string enc_client_id = base::EscapeUrlEncodedData(client_id, true);
  271. std::string enc_client_secret =
  272. base::EscapeUrlEncodedData(client_secret, true);
  273. const char* key = nullptr;
  274. const char* grant_type = nullptr;
  275. std::string enc_value;
  276. if (refresh_token.empty()) {
  277. key = kKeyAuthCode;
  278. grant_type = kGrantTypeAuthCode;
  279. enc_value = base::EscapeUrlEncodedData(auth_code, true);
  280. } else {
  281. key = kKeyRefreshToken;
  282. grant_type = kGrantTypeRefreshToken;
  283. enc_value = base::EscapeUrlEncodedData(refresh_token, true);
  284. }
  285. if (scopes.empty()) {
  286. return base::StringPrintf(kGetAccessTokenBodyFormat, enc_client_id.c_str(),
  287. enc_client_secret.c_str(), grant_type, key,
  288. enc_value.c_str());
  289. } else {
  290. std::string scopes_string = base::JoinString(scopes, " ");
  291. return base::StringPrintf(
  292. kGetAccessTokenBodyWithScopeFormat, enc_client_id.c_str(),
  293. enc_client_secret.c_str(), grant_type, key, enc_value.c_str(),
  294. base::EscapeUrlEncodedData(scopes_string, true).c_str());
  295. }
  296. }
  297. // static
  298. bool OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenSuccessResponse(
  299. const std::string& response_body,
  300. OAuth2AccessTokenConsumer::TokenResponse* token_response) {
  301. CHECK(token_response);
  302. auto value = base::JSONReader::Read(response_body);
  303. if (!value.has_value() || !value->is_dict())
  304. return false;
  305. const base::Value::Dict* dict = value->GetIfDict();
  306. // Refresh and id token are optional and don't cause an error if missing.
  307. const std::string* refresh_token = dict->FindString(krefreshTokenKey);
  308. if (refresh_token)
  309. token_response->refresh_token = *refresh_token;
  310. const std::string* id_token = dict->FindString(kIdTokenKey);
  311. if (id_token)
  312. token_response->id_token = *id_token;
  313. const std::string* access_token = dict->FindString(kAccessTokenKey);
  314. if (access_token)
  315. token_response->access_token = *access_token;
  316. absl::optional<int> expires_in = dict->FindInt(kExpiresInKey);
  317. bool ok = access_token && expires_in.has_value();
  318. if (ok) {
  319. // The token will expire in |expires_in| seconds. Take a 10% error margin to
  320. // prevent reusing a token too close to its expiration date.
  321. token_response->expiration_time =
  322. base::Time::Now() + base::Seconds(9 * expires_in.value() / 10);
  323. }
  324. return ok;
  325. }
  326. // static
  327. bool OAuth2AccessTokenFetcherImpl::ParseGetAccessTokenFailureResponse(
  328. const std::string& response_body,
  329. std::string* error) {
  330. CHECK(error);
  331. auto value = base::JSONReader::Read(response_body);
  332. if (!value.has_value() || !value->is_dict())
  333. return false;
  334. const base::Value::Dict* dict = value->GetIfDict();
  335. const std::string* error_value = dict->FindString(kErrorKey);
  336. if (!error_value)
  337. return false;
  338. *error = *error_value;
  339. return true;
  340. }