15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/gaia_oauth_client.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
75821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/json/json_reader.h"
85821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
95821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/memory/scoped_ptr.h"
10eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch#include "base/strings/string_util.h"
115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/values.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "google_apis/gaia/gaia_urls.h"
135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/base/escape.h"
145d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)#include "net/base/load_flags.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/http/http_status_code.h"
165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher.h"
175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_fetcher_delegate.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "net/url_request/url_request_context_getter.h"
197dbb3d5cf0c15f500944d211057644d6a2f37371Ben Murdoch#include "url/gurl.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace {
225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kAccessTokenValue[] = "access_token";
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kRefreshTokenValue[] = "refresh_token";
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)const char kExpiresInValue[] = "expires_in";
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace gaia {
285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// Use a non-zero number, so unit tests can differentiate the URLFetcher used by
30eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch// this class from other fetchers (most other code just hardcodes the ID to 0).
31eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochconst int GaiaOAuthClient::kUrlFetcherId = 17109006;
32eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)class GaiaOAuthClient::Core
345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>,
355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      public net::URLFetcherDelegate {
365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) public:
37eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  Core(net::URLRequestContextGetter* request_context_getter)
38eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      : num_retries_(0),
39eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        request_context_getter_(request_context_getter),
40eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        delegate_(NULL),
41eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        request_type_(NO_PENDING_REQUEST) {
425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info,
455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             const std::string& auth_code,
465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             int max_retries,
475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             GaiaOAuthClient::Delegate* delegate);
485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void RefreshToken(const OAuthClientInfo& oauth_client_info,
495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    const std::string& refresh_token,
50eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    const std::vector<std::string>& scopes,
515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int max_retries,
525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    GaiaOAuthClient::Delegate* delegate);
53eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void GetUserEmail(const std::string& oauth_access_token,
54eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    int max_retries,
55eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                    Delegate* delegate);
568bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  void GetUserId(const std::string& oauth_access_token,
578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                 int max_retries,
588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                 Delegate* delegate);
596e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void GetUserInfo(const std::string& oauth_access_token,
606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   int max_retries,
616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                   Delegate* delegate);
62eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void GetTokenInfo(const std::string& oauth_access_token,
635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    int max_retries,
645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                    Delegate* delegate);
655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // net::URLFetcherDelegate implementation.
675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE;
685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles) private:
705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  friend class base::RefCountedThreadSafe<Core>;
715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  enum RequestType {
735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    NO_PENDING_REQUEST,
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    TOKENS_FROM_AUTH_CODE,
755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    REFRESH_TOKEN,
76eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    TOKEN_INFO,
778bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    USER_EMAIL,
788bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    USER_ID,
796e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    USER_INFO,
805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  };
815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  virtual ~Core() {}
835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
846e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  void GetUserInfoImpl(RequestType type,
856e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                       const std::string& oauth_access_token,
866e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                       int max_retries,
876e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                       Delegate* delegate);
88eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  void MakeGaiaRequest(const GURL& url,
89eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                       const std::string& post_body,
905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       int max_retries,
915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                       GaiaOAuthClient::Delegate* delegate);
925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  void HandleResponse(const net::URLFetcher* source,
935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                      bool* should_retry_request);
945821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
955821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  int num_retries_;
965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  GaiaOAuthClient::Delegate* delegate_;
985821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::URLFetcher> request_;
995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequestType request_type_;
1005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)};
1015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::Core::GetTokensFromAuthCode(
1035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const OAuthClientInfo& oauth_client_info,
1045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& auth_code,
1055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_retries,
1065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GaiaOAuthClient::Delegate* delegate) {
1075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
1085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_type_ = TOKENS_FROM_AUTH_CODE;
1095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string post_body =
1105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "code=" + net::EscapeUrlEncodedData(auth_code, true) +
1115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
1125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                true) +
1135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&client_secret=" +
1145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) +
1155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&redirect_uri=" +
1165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) +
1175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&grant_type=authorization_code";
118eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()),
119eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  post_body, max_retries, delegate);
1205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::Core::RefreshToken(
1235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const OAuthClientInfo& oauth_client_info,
1245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& refresh_token,
125eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::vector<std::string>& scopes,
1265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_retries,
1275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GaiaOAuthClient::Delegate* delegate) {
1285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
1295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_type_ = REFRESH_TOKEN;
1305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  std::string post_body =
1315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) +
1325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id,
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                true) +
1345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&client_secret=" +
1355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) +
1365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      "&grant_type=refresh_token";
137eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
138eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!scopes.empty()) {
139eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    std::string scopes_string = JoinString(scopes, ' ');
140eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    post_body += "&scope=" + net::EscapeUrlEncodedData(scopes_string, true);
141eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
142eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
143eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()),
144eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  post_body, max_retries, delegate);
1455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
147eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid GaiaOAuthClient::Core::GetUserEmail(const std::string& oauth_access_token,
148eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         int max_retries,
149eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         Delegate* delegate) {
1506e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  GetUserInfoImpl(USER_EMAIL, oauth_access_token, max_retries, delegate);
1518bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1528bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1538bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void GaiaOAuthClient::Core::GetUserId(const std::string& oauth_access_token,
1548bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                      int max_retries,
1558bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                      Delegate* delegate) {
1566e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  GetUserInfoImpl(USER_ID, oauth_access_token, max_retries, delegate);
1578bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
1588bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
1598bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token,
1606e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                      int max_retries,
1616e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                      Delegate* delegate) {
1626e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  GetUserInfoImpl(USER_INFO, oauth_access_token, max_retries, delegate);
1636e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
1646e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
1656e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void GaiaOAuthClient::Core::GetUserInfoImpl(
1666e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    RequestType type,
1676e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    const std::string& oauth_access_token,
1686e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    int max_retries,
1696e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    Delegate* delegate) {
1706e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
1716e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  DCHECK(!request_.get());
1726e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  request_type_ = type;
1735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_ = delegate;
1745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  num_retries_ = 0;
1755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_.reset(net::URLFetcher::Create(
176116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      kUrlFetcherId, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()),
1775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      net::URLFetcher::GET, this));
178868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  request_->SetRequestContext(request_context_getter_.get());
179868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token);
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_->SetMaxRetriesOn5xx(max_retries);
1815d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
1825d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         net::LOAD_DO_NOT_SAVE_COOKIES);
1835d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Fetchers are sometimes cancelled because a network change was detected,
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // especially at startup and after sign-in on ChromeOS. Retrying once should
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // be enough in those cases; let the fetcher retry up to 3 times just in case.
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // http://crbug.com/163710
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_->SetAutomaticallyRetryOnNetworkChanges(3);
1895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_->Start();
1905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
1915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
192eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid GaiaOAuthClient::Core::GetTokenInfo(const std::string& oauth_access_token,
193eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         int max_retries,
194eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                         Delegate* delegate) {
195eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK_EQ(request_type_, NO_PENDING_REQUEST);
196eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(!request_.get());
197eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  request_type_ = TOKEN_INFO;
198eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  std::string post_body =
199eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      "access_token=" + net::EscapeUrlEncodedData(oauth_access_token, true);
200eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()),
201eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  post_body,
202eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  max_retries,
203eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                  delegate);
204eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
205eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::Core::MakeGaiaRequest(
207eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const GURL& url,
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& post_body,
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_retries,
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    GaiaOAuthClient::Delegate* delegate) {
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK(!request_.get()) << "Tried to fetch two things at once!";
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  delegate_ = delegate;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  num_retries_ = 0;
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_.reset(net::URLFetcher::Create(
215eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      kUrlFetcherId, url, net::URLFetcher::POST, this));
216868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)  request_->SetRequestContext(request_context_getter_.get());
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_->SetUploadData("application/x-www-form-urlencoded", post_body);
2182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_->SetMaxRetriesOn5xx(max_retries);
2195d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  request_->SetLoadFlags(net::LOAD_DO_NOT_SEND_COOKIES |
2205d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                         net::LOAD_DO_NOT_SAVE_COOKIES);
2212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See comment on SetAutomaticallyRetryOnNetworkChanges() above.
2222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  request_->SetAutomaticallyRetryOnNetworkChanges(3);
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_->Start();
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// URLFetcher::Delegate implementation.
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::Core::OnURLFetchComplete(
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source) {
2295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  bool should_retry = false;
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HandleResponse(source, &should_retry);
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (should_retry) {
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // Explicitly call ReceivedContentWasMalformed() to ensure the current
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // request gets counted as a failure for calculation of the back-off
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // period.  If it was already a failure by status code, this call will
2355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // be ignored.
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_->ReceivedContentWasMalformed();
2375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    num_retries_++;
2385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // We must set our request_context_getter_ again because
2395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL...
240868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)    request_->SetRequestContext(request_context_getter_.get());
2415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    request_->Start();
2425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::Core::HandleResponse(
2465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const net::URLFetcher* source,
2475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    bool* should_retry_request) {
248eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // Move ownership of the request fetcher into a local scoped_ptr which
249eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // will be nuked when we're done handling the request, unless we need
250eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  // to retry, in which case ownership will be returned to request_.
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  scoped_ptr<net::URLFetcher> old_request = request_.Pass();
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DCHECK_EQ(source, old_request.get());
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2545d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // HTTP_BAD_REQUEST means the arguments are invalid.  HTTP_UNAUTHORIZED means
2555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  // the access or refresh token is invalid. No point retrying. We are
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // done here.
2575d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  int response_code = source->GetResponseCode();
2585d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  if (response_code == net::HTTP_BAD_REQUEST ||
2595d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      response_code == net::HTTP_UNAUTHORIZED) {
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    delegate_->OnOAuthError();
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
264eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  scoped_ptr<base::DictionaryValue> response_dict;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (source->GetResponseCode() == net::HTTP_OK) {
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    std::string data;
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    source->GetResponseAsString(&data);
268eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    scoped_ptr<base::Value> message_value(base::JSONReader::Read(data));
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (message_value.get() &&
270eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch        message_value->IsType(base::Value::TYPE_DICTIONARY)) {
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_dict.reset(
272eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch          static_cast<base::DictionaryValue*>(message_value.release()));
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!response_dict.get()) {
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // If we don't have an access token yet and the the error was not
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    // RC_BAD_REQUEST, we may need to retry.
2792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if ((source->GetMaxRetriesOn5xx() != -1) &&
280868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)        (num_retries_ >= source->GetMaxRetriesOn5xx())) {
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      // Retry limit reached. Give up.
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      delegate_->OnNetworkError(source->GetResponseCode());
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    } else {
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      request_ = old_request.Pass();
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      *should_retry_request = true;
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2905821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  RequestType type = request_type_;
2915821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  request_type_ = NO_PENDING_REQUEST;
2925821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2935821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  switch (type) {
2948bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    case USER_EMAIL: {
295116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      std::string email;
296116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      response_dict->GetString("email", &email);
297116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch      delegate_->OnGetUserEmailResponse(email);
298eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      break;
299eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    }
300eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
3018bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    case USER_ID: {
3028bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      std::string id;
3038bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      response_dict->GetString("id", &id);
3048bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      delegate_->OnGetUserIdResponse(id);
3058bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)      break;
3068bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)    }
3078bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3086e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    case USER_INFO: {
3096e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      delegate_->OnGetUserInfoResponse(response_dict.Pass());
3106e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)      break;
3116e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)    }
3126e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
313eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    case TOKEN_INFO: {
314eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch      delegate_->OnGetTokenInfoResponse(response_dict.Pass());
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case TOKENS_FROM_AUTH_CODE:
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    case REFRESH_TOKEN: {
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string access_token;
3215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      std::string refresh_token;
3225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      int expires_in_seconds = 0;
3235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_dict->GetString(kAccessTokenValue, &access_token);
3245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_dict->GetString(kRefreshTokenValue, &refresh_token);
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      response_dict->GetInteger(kExpiresInValue, &expires_in_seconds);
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (access_token.empty()) {
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delegate_->OnOAuthError();
3295821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        return;
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      if (type == REFRESH_TOKEN) {
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds);
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      } else {
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)        delegate_->OnGetTokensResponse(refresh_token,
3365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       access_token,
3375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                       expires_in_seconds);
3385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      }
3395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      break;
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    default:
3435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      NOTREACHED();
3445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
347eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochGaiaOAuthClient::GaiaOAuthClient(net::URLRequestContextGetter* context_getter) {
348eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  core_ = new Core(context_getter);
3495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)GaiaOAuthClient::~GaiaOAuthClient() {
3525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)void GaiaOAuthClient::GetTokensFromAuthCode(
3555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const OAuthClientInfo& oauth_client_info,
3565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& auth_code,
3575821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    int max_retries,
3585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    Delegate* delegate) {
3595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return core_->GetTokensFromAuthCode(oauth_client_info,
3605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      auth_code,
3615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      max_retries,
3625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      delegate);
3635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
365eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid GaiaOAuthClient::RefreshToken(
366eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const OAuthClientInfo& oauth_client_info,
367eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& refresh_token,
368eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::vector<std::string>& scopes,
369eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    int max_retries,
370eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    Delegate* delegate) {
3715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return core_->RefreshToken(oauth_client_info,
3725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             refresh_token,
373eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                             scopes,
3745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             max_retries,
3755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                             delegate);
3765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
378eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid GaiaOAuthClient::GetUserEmail(const std::string& access_token,
3795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  int max_retries,
3805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                  Delegate* delegate) {
381eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return core_->GetUserEmail(access_token, max_retries, delegate);
382eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
383eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
3848bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)void GaiaOAuthClient::GetUserId(const std::string& access_token,
3858bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                int max_retries,
3868bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)                                Delegate* delegate) {
3878bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)  return core_->GetUserId(access_token, max_retries, delegate);
3888bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)}
3898bcbed890bc3ce4d7a057a8f32cab53fa534672eTorne (Richard Coles)
3906e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)void GaiaOAuthClient::GetUserInfo(const std::string& access_token,
3916e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                  int max_retries,
3926e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)                                  Delegate* delegate) {
3936e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)  return core_->GetUserInfo(access_token, max_retries, delegate);
3946e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)}
3956e8cce623b6e4fe0c9e4af605d675dd9d0338c38Torne (Richard Coles)
396eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdochvoid GaiaOAuthClient::GetTokenInfo(const std::string& access_token,
397eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   int max_retries,
398eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                   Delegate* delegate) {
399eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return core_->GetTokenInfo(access_token, max_retries, delegate);
4005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace gaia
403