• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2011 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 "chrome/common/net/gaia/gaia_oauth_client.h"
6 
7 #include "base/json/json_reader.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/values.h"
10 #include "chrome/common/net/http_return.h"
11 #include "chrome/common/net/url_fetcher.h"
12 #include "googleurl/src/gurl.h"
13 #include "net/base/escape.h"
14 #include "net/url_request/url_request_context_getter.h"
15 
16 namespace {
17 const char kAccessTokenValue[] = "access_token";
18 const char kRefreshTokenValue[] = "refresh_token";
19 const char kExpiresInValue[] = "expires_in";
20 }
21 
22 namespace gaia {
23 
24 class GaiaOAuthClient::Core
25     : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>,
26       public URLFetcher::Delegate {
27  public:
Core(const std::string & gaia_url,net::URLRequestContextGetter * request_context_getter)28   Core(const std::string& gaia_url,
29        net::URLRequestContextGetter* request_context_getter)
30            : gaia_url_(gaia_url),
31              num_retries_(0),
32              request_context_getter_(request_context_getter),
33              delegate_(NULL) {}
34 
~Core()35   virtual ~Core() { }
36 
37   void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info,
38                              const std::string& auth_code,
39                              int max_retries,
40                              GaiaOAuthClient::Delegate* delegate);
41   void RefreshToken(const OAuthClientInfo& oauth_client_info,
42                     const std::string& refresh_token,
43                     int max_retries,
44                     GaiaOAuthClient::Delegate* delegate);
45 
46   // URLFetcher::Delegate implementation.
47   virtual void OnURLFetchComplete(
48     const URLFetcher* source,
49     const GURL& url,
50     const net::URLRequestStatus& status,
51     int response_code,
52     const ResponseCookies& cookies,
53     const std::string& data);
54 
55  private:
56   void MakeGaiaRequest(std::string post_body,
57                        int max_retries,
58                        GaiaOAuthClient::Delegate* delegate);
59   void HandleResponse(const URLFetcher* source,
60                       const GURL& url,
61                       const net::URLRequestStatus& status,
62                       int response_code,
63                       const std::string& data,
64                       bool* should_retry_request);
65 
66   GURL gaia_url_;
67   int num_retries_;
68   scoped_refptr<net::URLRequestContextGetter> request_context_getter_;
69   GaiaOAuthClient::Delegate* delegate_;
70   scoped_ptr<URLFetcher> request_;
71 };
72 
GetTokensFromAuthCode(const OAuthClientInfo & oauth_client_info,const std::string & auth_code,int max_retries,GaiaOAuthClient::Delegate * delegate)73 void GaiaOAuthClient::Core::GetTokensFromAuthCode(
74     const OAuthClientInfo& oauth_client_info,
75     const std::string& auth_code,
76     int max_retries,
77     GaiaOAuthClient::Delegate* delegate) {
78   std::string post_body =
79       "code=" + EscapeUrlEncodedData(auth_code) +
80       "&client_id=" + EscapeUrlEncodedData(oauth_client_info.client_id) +
81       "&client_secret=" +
82       EscapeUrlEncodedData(oauth_client_info.client_secret) +
83       "&redirect_uri=oob&grant_type=authorization_code";
84   MakeGaiaRequest(post_body, max_retries, delegate);
85 }
86 
RefreshToken(const OAuthClientInfo & oauth_client_info,const std::string & refresh_token,int max_retries,GaiaOAuthClient::Delegate * delegate)87 void GaiaOAuthClient::Core::RefreshToken(
88     const OAuthClientInfo& oauth_client_info,
89     const std::string& refresh_token,
90     int max_retries,
91     GaiaOAuthClient::Delegate* delegate) {
92   std::string post_body =
93       "refresh_token=" + EscapeUrlEncodedData(refresh_token) +
94       "&client_id=" + EscapeUrlEncodedData(oauth_client_info.client_id) +
95       "&client_secret=" +
96       EscapeUrlEncodedData(oauth_client_info.client_secret) +
97       "&grant_type=refresh_token";
98   MakeGaiaRequest(post_body, max_retries, delegate);
99 }
100 
MakeGaiaRequest(std::string post_body,int max_retries,GaiaOAuthClient::Delegate * delegate)101 void GaiaOAuthClient::Core::MakeGaiaRequest(
102     std::string post_body,
103     int max_retries,
104     GaiaOAuthClient::Delegate* delegate) {
105   DCHECK(!request_.get()) << "Tried to fetch two things at once!";
106   delegate_ = delegate;
107   num_retries_ = 0;
108   request_.reset(URLFetcher::Create(0, gaia_url_, URLFetcher::POST, this));
109   request_->set_request_context(request_context_getter_);
110   request_->set_upload_data("application/x-www-form-urlencoded", post_body);
111   request_->set_max_retries(max_retries);
112   request_->Start();
113 }
114 
115 // URLFetcher::Delegate implementation.
OnURLFetchComplete(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const ResponseCookies & cookies,const std::string & data)116 void GaiaOAuthClient::Core::OnURLFetchComplete(
117     const URLFetcher* source,
118     const GURL& url,
119     const net::URLRequestStatus& status,
120     int response_code,
121     const ResponseCookies& cookies,
122     const std::string& data) {
123   bool should_retry = false;
124   HandleResponse(source, url, status, response_code, data, &should_retry);
125   if (should_retry) {
126     // If the response code is greater than or equal to 500, then the back-off
127     // period has been increased at the network level; otherwise, explicitly
128     // call ReceivedContentWasMalformed() to count the current request as a
129     // failure and increase the back-off period.
130     if (response_code < 500)
131       request_->ReceivedContentWasMalformed();
132     num_retries_++;
133     request_->Start();
134   } else {
135     request_.reset();
136   }
137 }
138 
HandleResponse(const URLFetcher * source,const GURL & url,const net::URLRequestStatus & status,int response_code,const std::string & data,bool * should_retry_request)139 void GaiaOAuthClient::Core::HandleResponse(
140     const URLFetcher* source,
141     const GURL& url,
142     const net::URLRequestStatus& status,
143     int response_code,
144     const std::string& data,
145     bool* should_retry_request) {
146   *should_retry_request = false;
147   // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are
148   // done here.
149   if (response_code == RC_BAD_REQUEST) {
150     delegate_->OnOAuthError();
151     return;
152   }
153   std::string access_token;
154   std::string refresh_token;
155   int expires_in_seconds = 0;
156   if (response_code == RC_REQUEST_OK) {
157     scoped_ptr<Value> message_value(
158         base::JSONReader::Read(data, false));
159     if (message_value.get() &&
160         message_value->IsType(Value::TYPE_DICTIONARY)) {
161       scoped_ptr<DictionaryValue> response_dict(
162           static_cast<DictionaryValue*>(message_value.release()));
163       response_dict->GetString(kAccessTokenValue, &access_token);
164       response_dict->GetString(kRefreshTokenValue, &refresh_token);
165       response_dict->GetInteger(kExpiresInValue, &expires_in_seconds);
166     }
167   }
168   if (access_token.empty()) {
169     // If we don't have an access token yet and the the error was not
170     // RC_BAD_REQUEST, we may need to retry.
171     if ((-1 != source->max_retries()) &&
172         (num_retries_ > source->max_retries())) {
173       // Retry limit reached. Give up.
174       delegate_->OnNetworkError(response_code);
175     } else {
176       *should_retry_request = true;
177     }
178   } else if (refresh_token.empty()) {
179     // If we only have an access token, then this was a refresh request.
180     delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds);
181   } else {
182     delegate_->OnGetTokensResponse(refresh_token,
183                                    access_token,
184                                    expires_in_seconds);
185   }
186 }
187 
GaiaOAuthClient(const std::string & gaia_url,net::URLRequestContextGetter * context_getter)188 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url,
189                                  net::URLRequestContextGetter* context_getter) {
190   core_ = new Core(gaia_url, context_getter);
191 }
192 
~GaiaOAuthClient()193 GaiaOAuthClient::~GaiaOAuthClient() {
194 }
195 
196 
GetTokensFromAuthCode(const OAuthClientInfo & oauth_client_info,const std::string & auth_code,int max_retries,Delegate * delegate)197 void GaiaOAuthClient::GetTokensFromAuthCode(
198     const OAuthClientInfo& oauth_client_info,
199     const std::string& auth_code,
200     int max_retries,
201     Delegate* delegate) {
202   return core_->GetTokensFromAuthCode(oauth_client_info,
203                                       auth_code,
204                                       max_retries,
205                                       delegate);
206 }
207 
RefreshToken(const OAuthClientInfo & oauth_client_info,const std::string & refresh_token,int max_retries,Delegate * delegate)208 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info,
209                                    const std::string& refresh_token,
210                                    int max_retries,
211                                    Delegate* delegate) {
212   return core_->RefreshToken(oauth_client_info,
213                              refresh_token,
214                              max_retries,
215                              delegate);
216 }
217 
218 }  // namespace gaia
219