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