1 // Copyright (c) 2012 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 #include "google_apis/gaia/gaia_oauth_client.h"
6
7 #include <memory>
8 #include <utility>
9
10 #include "base/bind.h"
11 #include "base/check_op.h"
12 #include "base/json/json_reader.h"
13 #include "base/memory/weak_ptr.h"
14 #include "base/notreached.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "google_apis/gaia/gaia_auth_util.h"
18 #include "google_apis/gaia/gaia_urls.h"
19 #include "net/base/backoff_entry.h"
20 #include "net/base/escape.h"
21 #include "net/base/load_flags.h"
22 #include "net/http/http_status_code.h"
23 #include "net/traffic_annotation/network_traffic_annotation.h"
24 #include "net/url_request/url_request_throttler_entry.h"
25 #include "services/network/public/cpp/resource_request.h"
26 #include "services/network/public/cpp/shared_url_loader_factory.h"
27 #include "services/network/public/cpp/simple_url_loader.h"
28 #include "url/gurl.h"
29
30 namespace {
31 const char kAccessTokenValue[] = "access_token";
32 const char kRefreshTokenValue[] = "refresh_token";
33 const char kExpiresInValue[] = "expires_in";
34 }
35
36 namespace gaia {
37
38 class GaiaOAuthClient::Core
39 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> {
40 public:
Core(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)41 Core(scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory)
42 : backoff_entry_(&backoff_policy_),
43 num_retries_(0),
44 max_retries_(0),
45 url_loader_factory_(url_loader_factory),
46 delegate_(nullptr),
47 request_type_(NO_PENDING_REQUEST) {
48 backoff_policy_.num_errors_to_ignore =
49 net::URLRequestThrottlerEntry::kDefaultNumErrorsToIgnore;
50 backoff_policy_.initial_delay_ms =
51 net::URLRequestThrottlerEntry::kDefaultInitialDelayMs;
52 backoff_policy_.multiply_factor =
53 net::URLRequestThrottlerEntry::kDefaultMultiplyFactor;
54 backoff_policy_.jitter_factor =
55 net::URLRequestThrottlerEntry::kDefaultJitterFactor;
56 backoff_policy_.maximum_backoff_ms =
57 net::URLRequestThrottlerEntry::kDefaultMaximumBackoffMs;
58 backoff_policy_.entry_lifetime_ms =
59 net::URLRequestThrottlerEntry::kDefaultEntryLifetimeMs;
60 backoff_policy_.always_use_initial_delay = false;
61 }
62
63 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info,
64 const std::string& auth_code,
65 int max_retries,
66 GaiaOAuthClient::Delegate* delegate);
67 void RefreshToken(const OAuthClientInfo& oauth_client_info,
68 const std::string& refresh_token,
69 const std::vector<std::string>& scopes,
70 int max_retries,
71 GaiaOAuthClient::Delegate* delegate);
72 void GetUserEmail(const std::string& oauth_access_token,
73 int max_retries,
74 Delegate* delegate);
75 void GetUserId(const std::string& oauth_access_token,
76 int max_retries,
77 Delegate* delegate);
78 void GetUserInfo(const std::string& oauth_access_token,
79 int max_retries,
80 Delegate* delegate);
81 void GetTokenInfo(const std::string& qualifier,
82 const std::string& query,
83 int max_retries,
84 Delegate* delegate);
85
86 // Called as a SimpleURLLoader callback
87 void OnURLLoadComplete(std::unique_ptr<std::string> body);
88
89 private:
90 friend class base::RefCountedThreadSafe<Core>;
91
92 enum RequestType {
93 NO_PENDING_REQUEST,
94 TOKENS_FROM_AUTH_CODE,
95 REFRESH_TOKEN,
96 TOKEN_INFO,
97 USER_EMAIL,
98 USER_ID,
99 USER_INFO,
100 };
101
~Core()102 ~Core() {}
103
104 void GetUserInfoImpl(RequestType type,
105 const std::string& oauth_access_token,
106 int max_retries,
107 Delegate* delegate);
108
109 // Stores request settings into |this| and calls SendRequest().
110 void MakeRequest(
111 RequestType type,
112 const GURL& url,
113 std::string post_body /* may be empty if not needed*/,
114 std::string authorization_header /* empty if not needed */,
115 int max_retries,
116 GaiaOAuthClient::Delegate* delegate,
117 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation);
118
119 // Sends out a request based on things MakeRequest() stored...
120 // once |backoff_entry_| says it's OK. Can be called many times to retry,
121 // assuming |request_| is destroyed first.
122 void SendRequest();
123
124 // Actually sends the request.
125 void SendRequestImpl();
126
127 void HandleResponse(std::unique_ptr<std::string> body,
128 bool* should_retry_request);
129
130 net::BackoffEntry::Policy backoff_policy_;
131 net::BackoffEntry backoff_entry_;
132
133 int num_retries_;
134 int max_retries_;
135 GURL url_;
136 net::MutableNetworkTrafficAnnotationTag traffic_annotation_;
137 std::string post_body_;
138 std::string authorization_header_;
139
140 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
141 GaiaOAuthClient::Delegate* delegate_;
142 std::unique_ptr<network::SimpleURLLoader> request_;
143 RequestType request_type_;
144
145 base::WeakPtrFactory<Core> weak_ptr_factory_{this};
146 };
147
GetTokensFromAuthCode(const OAuthClientInfo & oauth_client_info,const std::string & auth_code,int max_retries,GaiaOAuthClient::Delegate * delegate)148 void GaiaOAuthClient::Core::GetTokensFromAuthCode(
149 const OAuthClientInfo& oauth_client_info,
150 const std::string& auth_code,
151 int max_retries,
152 GaiaOAuthClient::Delegate* delegate) {
153 std::string post_body =
154 "code=" + net::EscapeUrlEncodedData(auth_code, true) +
155 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
156 true) +
157 "&client_secret=" +
158 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) +
159 "&redirect_uri=" +
160 net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) +
161 "&grant_type=authorization_code";
162 net::MutableNetworkTrafficAnnotationTag traffic_annotation(
163 net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_tokens", R"(
164 semantics {
165 sender: "OAuth 2.0 calls"
166 description:
167 "This request exchanges an authorization code for an OAuth 2.0 "
168 "refresh token and an OAuth 2.0 access token."
169 trigger:
170 "This request is triggered when a Chrome service requires an "
171 "access token and a refresh token (e.g. Cloud Print, Chrome Remote "
172 "Desktop etc.) See https://developers.google.com/identity/protocols"
173 "/OAuth2 for more information about the Google implementation of "
174 "the OAuth 2.0 protocol."
175 data:
176 "The Google console client ID and client secret of the caller, the "
177 "OAuth authorization code and the redirect URI."
178 destination: GOOGLE_OWNED_SERVICE
179 }
180 policy {
181 cookies_allowed: NO
182 setting:
183 "This feature cannot be disabled in settings, but if the user "
184 "signs out of Chrome, this request would not be made."
185 chrome_policy {
186 SigninAllowed {
187 policy_options {mode: MANDATORY}
188 SigninAllowed: false
189 }
190 }
191 })"));
192 MakeRequest(TOKENS_FROM_AUTH_CODE,
193 GURL(GaiaUrls::GetInstance()->oauth2_token_url()), post_body,
194 /* authorization_header = */ std::string(), max_retries, delegate,
195 traffic_annotation);
196 }
197
RefreshToken(const OAuthClientInfo & oauth_client_info,const std::string & refresh_token,const std::vector<std::string> & scopes,int max_retries,GaiaOAuthClient::Delegate * delegate)198 void GaiaOAuthClient::Core::RefreshToken(
199 const OAuthClientInfo& oauth_client_info,
200 const std::string& refresh_token,
201 const std::vector<std::string>& scopes,
202 int max_retries,
203 GaiaOAuthClient::Delegate* delegate) {
204 std::string post_body =
205 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) +
206 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
207 true) +
208 "&client_secret=" +
209 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) +
210 "&grant_type=refresh_token";
211
212 if (!scopes.empty()) {
213 std::string scopes_string = base::JoinString(scopes, " ");
214 post_body += "&scope=" + net::EscapeUrlEncodedData(scopes_string, true);
215 }
216
217 net::MutableNetworkTrafficAnnotationTag traffic_annotation(
218 net::DefineNetworkTrafficAnnotation("gaia_oauth_client_refresh_token", R"(
219 semantics {
220 sender: "OAuth 2.0 calls"
221 description:
222 "This request fetches a fresh access token that can be used to "
223 "authenticate an API call to a Google web endpoint."
224 trigger:
225 "This is called whenever the caller needs a fresh OAuth 2.0 access "
226 "token."
227 data:
228 "The OAuth 2.0 refresh token, the Google console client ID and "
229 "client secret of the caller, and optionally the scopes of the API "
230 "for which the access token should be authorized."
231 destination: GOOGLE_OWNED_SERVICE
232 }
233 policy {
234 cookies_allowed: NO
235 setting:
236 "This feature cannot be disabled in settings, but if the user "
237 "signs out of Chrome, this request would not be made."
238 chrome_policy {
239 SigninAllowed {
240 policy_options {mode: MANDATORY}
241 SigninAllowed: false
242 }
243 }
244 })"));
245 MakeRequest(REFRESH_TOKEN, GURL(GaiaUrls::GetInstance()->oauth2_token_url()),
246 post_body,
247 /* authorization_header = */ std::string(), max_retries, delegate,
248 traffic_annotation);
249 }
250
GetUserEmail(const std::string & oauth_access_token,int max_retries,Delegate * delegate)251 void GaiaOAuthClient::Core::GetUserEmail(const std::string& oauth_access_token,
252 int max_retries,
253 Delegate* delegate) {
254 GetUserInfoImpl(USER_EMAIL, oauth_access_token, max_retries, delegate);
255 }
256
257 void GaiaOAuthClient::Core::GetUserId(const std::string& oauth_access_token,
258 int max_retries,
259 Delegate* delegate) {
260 GetUserInfoImpl(USER_ID, oauth_access_token, max_retries, delegate);
261 }
262
263 void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token,
264 int max_retries,
265 Delegate* delegate) {
266 GetUserInfoImpl(USER_INFO, oauth_access_token, max_retries, delegate);
267 }
268
269 void GaiaOAuthClient::Core::GetUserInfoImpl(
270 RequestType type,
271 const std::string& oauth_access_token,
272 int max_retries,
273 Delegate* delegate) {
274 net::MutableNetworkTrafficAnnotationTag traffic_annotation(
275 net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_user_info", R"(
276 semantics {
277 sender: "OAuth 2.0 calls"
278 description:
279 "This request is used to fetch profile information about the user, "
280 "like the email, the ID of the account, the full name, and the "
281 "profile picture."
282 trigger:
283 "The main trigger for this request is in the AccountTrackerService "
284 "that fetches the user info soon after the user signs in."
285 data:
286 "The OAuth 2.0 access token of the account."
287 destination: GOOGLE_OWNED_SERVICE
288 }
289 policy {
290 cookies_allowed: NO
291 setting:
292 "This feature cannot be disabled in settings, but if the user "
293 "signs out of Chrome, this request would not be made."
294 chrome_policy {
295 SigninAllowed {
296 policy_options {mode: MANDATORY}
297 SigninAllowed: false
298 }
299 }
300 })"));
301 std::string auth = "OAuth " + oauth_access_token;
302 MakeRequest(type, GaiaUrls::GetInstance()->oauth_user_info_url(),
303 /* post_body = */ std::string(), auth, max_retries, delegate,
304 traffic_annotation);
305 }
306
GetTokenInfo(const std::string & qualifier,const std::string & query,int max_retries,Delegate * delegate)307 void GaiaOAuthClient::Core::GetTokenInfo(const std::string& qualifier,
308 const std::string& query,
309 int max_retries,
310 Delegate* delegate) {
311 std::string post_body =
312 qualifier + "=" + net::EscapeUrlEncodedData(query, true);
313 net::MutableNetworkTrafficAnnotationTag traffic_annotation(
314 net::DefineNetworkTrafficAnnotation("gaia_oauth_client_get_token_info",
315 R"(
316 semantics {
317 sender: "OAuth 2.0 calls"
318 description:
319 "This request fetches information about an OAuth 2.0 access token. "
320 "The response is a dictionary of response values. The provided "
321 "access token may have any scope, and basic results will be "
322 "returned: issued_to, audience, scope, expires_in, access_type. In "
323 "addition, if the https://www.googleapis.com/auth/userinfo.email "
324 "scope is present, the email and verified_email fields will be "
325 "returned. If the https://www.googleapis.com/auth/userinfo.profile "
326 "scope is present, the user_id field will be returned."
327 trigger:
328 "This is triggered after a Google account is added to the browser. "
329 "It it also triggered after each successful fetch of an OAuth 2.0 "
330 "access token."
331 data: "The OAuth 2.0 access token."
332 destination: GOOGLE_OWNED_SERVICE
333 }
334 policy {
335 cookies_allowed: NO
336 setting:
337 "This feature cannot be disabled in settings, but if the user "
338 "signs out of Chrome, this request would not be made."
339 chrome_policy {
340 SigninAllowed {
341 policy_options {mode: MANDATORY}
342 SigninAllowed: false
343 }
344 }
345 })"));
346 MakeRequest(TOKEN_INFO,
347 GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()), post_body,
348 /* authorization_header = */ std::string(), max_retries, delegate,
349 traffic_annotation);
350 }
351
MakeRequest(RequestType type,const GURL & url,std::string post_body,std::string authorization_header,int max_retries,GaiaOAuthClient::Delegate * delegate,const net::MutableNetworkTrafficAnnotationTag & traffic_annotation)352 void GaiaOAuthClient::Core::MakeRequest(
353 RequestType type,
354 const GURL& url,
355 std::string post_body,
356 std::string authorization_header,
357 int max_retries,
358 GaiaOAuthClient::Delegate* delegate,
359 const net::MutableNetworkTrafficAnnotationTag& traffic_annotation) {
360 DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
361 request_type_ = type;
362 delegate_ = delegate;
363 num_retries_ = 0;
364 max_retries_ = max_retries;
365 url_ = url;
366 traffic_annotation_ = traffic_annotation;
367 post_body_ = std::move(post_body);
368 authorization_header_ = std::move(authorization_header);
369 SendRequest();
370 }
371
372 void GaiaOAuthClient::Core::SendRequest() {
373 if (backoff_entry_.ShouldRejectRequest()) {
374 base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
375 FROM_HERE,
376 base::BindOnce(&GaiaOAuthClient::Core::SendRequestImpl,
377 weak_ptr_factory_.GetWeakPtr()),
378 backoff_entry_.GetTimeUntilRelease());
379 } else {
380 SendRequestImpl();
381 }
382 }
383
384 void GaiaOAuthClient::Core::SendRequestImpl() {
385 DCHECK(!request_.get()) << "Tried to fetch two things at once!";
386
387 auto resource_request = std::make_unique<network::ResourceRequest>();
388 resource_request->url = url_;
389 resource_request->method = post_body_.empty() ? "GET" : "POST";
390 resource_request->credentials_mode = network::mojom::CredentialsMode::kOmit;
391 if (!authorization_header_.empty())
392 resource_request->headers.SetHeader("Authorization", authorization_header_);
393
394 request_ = network::SimpleURLLoader::Create(
395 std::move(resource_request),
396 static_cast<net::NetworkTrafficAnnotationTag>(traffic_annotation_));
397
398 if (!post_body_.empty()) {
399 request_->AttachStringForUpload(post_body_,
400 "application/x-www-form-urlencoded");
401 }
402
403 // Retry is implemented internally.
404 request_->SetRetryOptions(0, network::SimpleURLLoader::RETRY_NEVER);
405
406 request_->DownloadToStringOfUnboundedSizeUntilCrashAndDie(
407 url_loader_factory_.get(),
408 // Unretained(this) is safe since |this| owns |request_|, and its deletion
409 // will cancel the callback.
410 base::BindOnce(&GaiaOAuthClient::Core::OnURLLoadComplete,
411 base::Unretained(this)));
412 }
413
414 void GaiaOAuthClient::Core::OnURLLoadComplete(
415 std::unique_ptr<std::string> body) {
416 bool should_retry = false;
417 base::WeakPtr<GaiaOAuthClient::Core> weak_this =
418 weak_ptr_factory_.GetWeakPtr();
419 // HandleResponse() may delete |this| if it assigns |should_retry| == false.
420 HandleResponse(std::move(body), &should_retry);
421 if (should_retry) {
422 num_retries_++;
423 backoff_entry_.InformOfRequest(false);
424 SendRequest();
425 } else {
426 if (weak_this)
427 backoff_entry_.InformOfRequest(true);
428 }
429 }
430
431 void GaiaOAuthClient::Core::HandleResponse(std::unique_ptr<std::string> body,
432 bool* should_retry_request) {
433 *should_retry_request = false;
434 // Move ownership of the request fetcher into a local scoped_ptr which
435 // will be nuked when we're done handling the request.
436 std::unique_ptr<network::SimpleURLLoader> source = std::move(request_);
437
438 int response_code = -1;
439 if (source->ResponseInfo() && source->ResponseInfo()->headers)
440 response_code = source->ResponseInfo()->headers->response_code();
441
442 // HTTP_BAD_REQUEST means the arguments are invalid. HTTP_UNAUTHORIZED means
443 // the access or refresh token is invalid. No point retrying. We are
444 // done here.
445 if (response_code == net::HTTP_BAD_REQUEST ||
446 response_code == net::HTTP_UNAUTHORIZED) {
447 delegate_->OnOAuthError();
448 return;
449 }
450
451 std::unique_ptr<base::DictionaryValue> response_dict;
452 if (response_code == net::HTTP_OK && body) {
453 std::string data = std::move(*body);
454 std::unique_ptr<base::Value> message_value =
455 base::JSONReader::ReadDeprecated(data);
456 if (message_value.get() && message_value->is_dict()) {
457 response_dict.reset(
458 static_cast<base::DictionaryValue*>(message_value.release()));
459 }
460 }
461
462 if (!response_dict.get()) {
463 // If we don't have an access token yet and the the error was not
464 // RC_BAD_REQUEST, we may need to retry.
465 if ((max_retries_ != -1) && (num_retries_ >= max_retries_)) {
466 // Retry limit reached. Give up.
467 request_type_ = NO_PENDING_REQUEST;
468 delegate_->OnNetworkError(response_code);
469 } else {
470 *should_retry_request = true;
471 }
472 return;
473 }
474
475 RequestType type = request_type_;
476 request_type_ = NO_PENDING_REQUEST;
477
478 switch (type) {
479 case USER_EMAIL: {
480 std::string email;
481 response_dict->GetString("email", &email);
482 delegate_->OnGetUserEmailResponse(email);
483 break;
484 }
485
486 case USER_ID: {
487 std::string id;
488 response_dict->GetString("id", &id);
489 delegate_->OnGetUserIdResponse(id);
490 break;
491 }
492
493 case USER_INFO: {
494 delegate_->OnGetUserInfoResponse(std::move(response_dict));
495 break;
496 }
497
498 case TOKEN_INFO: {
499 delegate_->OnGetTokenInfoResponse(std::move(response_dict));
500 break;
501 }
502
503 case TOKENS_FROM_AUTH_CODE:
504 case REFRESH_TOKEN: {
505 std::string access_token;
506 std::string refresh_token;
507 int expires_in_seconds = 0;
508 response_dict->GetString(kAccessTokenValue, &access_token);
509 response_dict->GetString(kRefreshTokenValue, &refresh_token);
510 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds);
511
512 if (access_token.empty()) {
513 delegate_->OnOAuthError();
514 return;
515 }
516
517 if (type == REFRESH_TOKEN) {
518 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds);
519 } else {
520 delegate_->OnGetTokensResponse(refresh_token,
521 access_token,
522 expires_in_seconds);
523 }
524 break;
525 }
526
527 default:
528 NOTREACHED();
529 }
530 }
531
532 GaiaOAuthClient::GaiaOAuthClient(
533 scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory) {
534 core_ = new Core(std::move(url_loader_factory));
535 }
536
537 GaiaOAuthClient::~GaiaOAuthClient() {
538 }
539
540 void GaiaOAuthClient::GetTokensFromAuthCode(
541 const OAuthClientInfo& oauth_client_info,
542 const std::string& auth_code,
543 int max_retries,
544 Delegate* delegate) {
545 return core_->GetTokensFromAuthCode(oauth_client_info,
546 auth_code,
547 max_retries,
548 delegate);
549 }
550
551 void GaiaOAuthClient::RefreshToken(
552 const OAuthClientInfo& oauth_client_info,
553 const std::string& refresh_token,
554 const std::vector<std::string>& scopes,
555 int max_retries,
556 Delegate* delegate) {
557 return core_->RefreshToken(oauth_client_info,
558 refresh_token,
559 scopes,
560 max_retries,
561 delegate);
562 }
563
564 void GaiaOAuthClient::GetUserEmail(const std::string& access_token,
565 int max_retries,
566 Delegate* delegate) {
567 return core_->GetUserEmail(access_token, max_retries, delegate);
568 }
569
570 void GaiaOAuthClient::GetUserId(const std::string& access_token,
571 int max_retries,
572 Delegate* delegate) {
573 return core_->GetUserId(access_token, max_retries, delegate);
574 }
575
576 void GaiaOAuthClient::GetUserInfo(const std::string& access_token,
577 int max_retries,
578 Delegate* delegate) {
579 return core_->GetUserInfo(access_token, max_retries, delegate);
580 }
581
582 void GaiaOAuthClient::GetTokenInfo(const std::string& access_token,
583 int max_retries,
584 Delegate* delegate) {
585 return core_->GetTokenInfo("access_token", access_token, max_retries,
586 delegate);
587 }
588
589 void GaiaOAuthClient::GetTokenHandleInfo(const std::string& token_handle,
590 int max_retries,
591 Delegate* delegate) {
592 return core_->GetTokenInfo("token_handle", token_handle, max_retries,
593 delegate);
594 }
595
596 } // namespace gaia
597