Home | History | Annotate | Download | only in gaia
      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:
     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 
     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 
     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 
     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 
    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.
    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 
    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 
    188 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url,
    189                                  net::URLRequestContextGetter* context_getter) {
    190   core_ = new Core(gaia_url, context_getter);
    191 }
    192 
    193 GaiaOAuthClient::~GaiaOAuthClient() {
    194 }
    195 
    196 
    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 
    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