| OLD | NEW | 
|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be | 
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. | 
| 4 | 4 | 
| 5 #include "remoting/host/gaia_oauth_client.h" | 5 #include "remoting/host/gaia_oauth_client.h" | 
| 6 | 6 | 
| 7 #include "base/bind.h" | 7 #include "base/bind.h" | 
| 8 #include "base/json/json_reader.h" | 8 #include "base/json/json_reader.h" | 
| 9 #include "base/logging.h" | 9 #include "base/logging.h" | 
| 10 #include "base/memory/scoped_ptr.h" | 10 #include "base/memory/scoped_ptr.h" | 
| 11 #include "base/values.h" | 11 #include "base/values.h" | 
| 12 #include "googleurl/src/gurl.h" | 12 #include "googleurl/src/gurl.h" | 
| 13 #include "net/base/escape.h" | 13 #include "net/base/escape.h" | 
| 14 #include "net/http/http_status_code.h" | 14 #include "net/http/http_status_code.h" | 
| 15 #include "net/url_request/url_request_context_getter.h" | 15 #include "net/url_request/url_request_context_getter.h" | 
| 16 #include "net/url_request/url_request_status.h" | 16 #include "net/url_request/url_request_status.h" | 
| 17 #include "remoting/host/url_fetcher.h" | 17 #include "remoting/host/url_fetcher.h" | 
| 18 | 18 | 
| 19 namespace { | 19 namespace { | 
|  | 20 | 
|  | 21 const char kDefaultOAuth2TokenUrl[] = | 
|  | 22     "https://accounts.google.com/o/oauth2/token"; | 
|  | 23 const char kDefaultOAuth2UserInfoUrl[] = | 
|  | 24     "https://www.googleapis.com/oauth2/v1/userinfo"; | 
|  | 25 | 
|  | 26 // Values used to parse token response. | 
| 20 const char kAccessTokenValue[] = "access_token"; | 27 const char kAccessTokenValue[] = "access_token"; | 
| 21 const char kRefreshTokenValue[] = "refresh_token"; | 28 const char kRefreshTokenValue[] = "refresh_token"; | 
| 22 const char kExpiresInValue[] = "expires_in"; | 29 const char kExpiresInValue[] = "expires_in"; | 
|  | 30 | 
|  | 31 // Values used when parsing userinfo response. | 
|  | 32 const char kEmailValue[] = "email"; | 
|  | 33 | 
| 23 }  // namespace | 34 }  // namespace | 
| 24 | 35 | 
| 25 namespace remoting { | 36 namespace remoting { | 
| 26 | 37 | 
|  | 38 // static | 
|  | 39 OAuthProviderInfo OAuthProviderInfo::GetDefault() { | 
|  | 40   OAuthProviderInfo result; | 
|  | 41   result.access_token_url = kDefaultOAuth2TokenUrl; | 
|  | 42   result.user_info_url = kDefaultOAuth2UserInfoUrl; | 
|  | 43   return result; | 
|  | 44 } | 
|  | 45 | 
| 27 class GaiaOAuthClient::Core | 46 class GaiaOAuthClient::Core | 
| 28     : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { | 47     : public base::RefCountedThreadSafe<GaiaOAuthClient::Core> { | 
| 29  public: | 48  public: | 
| 30   Core(const std::string& gaia_url, | 49   Core(const OAuthProviderInfo& info, | 
| 31        net::URLRequestContextGetter* request_context_getter) | 50        net::URLRequestContextGetter* request_context_getter) | 
| 32       : gaia_url_(gaia_url), | 51       : request_context_getter_(request_context_getter), | 
| 33         request_context_getter_(request_context_getter), |  | 
| 34         delegate_(NULL) { | 52         delegate_(NULL) { | 
| 35   } | 53   } | 
| 36 | 54 | 
| 37   void RefreshToken(const OAuthClientInfo& oauth_client_info, | 55   void RefreshToken(const OAuthClientInfo& oauth_client_info, | 
| 38                     const std::string& refresh_token, | 56                     const std::string& refresh_token, | 
| 39                     GaiaOAuthClient::Delegate* delegate); | 57                     GaiaOAuthClient::Delegate* delegate); | 
| 40 | 58 | 
| 41  private: | 59  private: | 
| 42   friend class base::RefCountedThreadSafe<Core>; | 60   friend class base::RefCountedThreadSafe<Core>; | 
| 43   virtual ~Core() {} | 61   virtual ~Core() {} | 
| 44 | 62 | 
| 45   void OnUrlFetchComplete(const net::URLRequestStatus& status, | 63   void OnAuthTokenFetchComplete(const net::URLRequestStatus& status, | 
| 46                           int response_code, | 64                                 int response_code, | 
| 47                           const std::string& response); | 65                                 const std::string& response); | 
|  | 66   void FetchUserInfoAndInvokeCallback(); | 
|  | 67   void OnUserInfoFetchComplete(const net::URLRequestStatus& status, | 
|  | 68                                int response_code, | 
|  | 69                                const std::string& response); | 
| 48 | 70 | 
| 49   GURL gaia_url_; | 71   OAuthProviderInfo provider_info_; | 
|  | 72 | 
| 50   scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 73   scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 
| 51   GaiaOAuthClient::Delegate* delegate_; | 74   GaiaOAuthClient::Delegate* delegate_; | 
| 52   scoped_ptr<UrlFetcher> request_; | 75   scoped_ptr<UrlFetcher> request_; | 
|  | 76 | 
|  | 77   std::string access_token_; | 
|  | 78   int expires_in_seconds_; | 
| 53 }; | 79 }; | 
| 54 | 80 | 
| 55 void GaiaOAuthClient::Core::RefreshToken( | 81 void GaiaOAuthClient::Core::RefreshToken( | 
| 56     const OAuthClientInfo& oauth_client_info, | 82     const OAuthClientInfo& oauth_client_info, | 
| 57     const std::string& refresh_token, | 83     const std::string& refresh_token, | 
| 58     GaiaOAuthClient::Delegate* delegate) { | 84     GaiaOAuthClient::Delegate* delegate) { | 
| 59   DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 85   DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 
| 60 | 86 | 
|  | 87   delegate_ = delegate; | 
|  | 88 | 
|  | 89   access_token_.clear(); | 
|  | 90   expires_in_seconds_ = 0; | 
|  | 91 | 
| 61   std::string post_body = | 92   std::string post_body = | 
| 62       "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 93       "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 
| 63       "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 94       "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 
| 64                                                 true) + | 95                                                 true) + | 
| 65       "&client_secret=" + | 96       "&client_secret=" + | 
| 66       net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 97       net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 
| 67       "&grant_type=refresh_token"; | 98       "&grant_type=refresh_token"; | 
| 68   delegate_ = delegate; | 99   request_.reset(new UrlFetcher(GURL(provider_info_.access_token_url), | 
| 69   request_.reset(new UrlFetcher(gaia_url_, UrlFetcher::POST)); | 100                                 UrlFetcher::POST)); | 
| 70   request_->SetRequestContext(request_context_getter_); | 101   request_->SetRequestContext(request_context_getter_); | 
| 71   request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 102   request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 
| 72   request_->Start(base::Bind(&GaiaOAuthClient::Core::OnUrlFetchComplete, this)); | 103   request_->Start( | 
|  | 104       base::Bind(&GaiaOAuthClient::Core::OnAuthTokenFetchComplete, this)); | 
| 73 } | 105 } | 
| 74 | 106 | 
| 75 void GaiaOAuthClient::Core::OnUrlFetchComplete( | 107 void GaiaOAuthClient::Core::OnAuthTokenFetchComplete( | 
| 76     const net::URLRequestStatus& status, | 108     const net::URLRequestStatus& status, | 
| 77     int response_code, | 109     int response_code, | 
| 78     const std::string& response) { | 110     const std::string& response) { | 
| 79   request_.reset(); | 111   request_.reset(); | 
| 80 | 112 | 
| 81   if (!status.is_success()) { | 113   if (!status.is_success()) { | 
| 82     delegate_->OnNetworkError(response_code); | 114     delegate_->OnNetworkError(response_code); | 
| 83     return; | 115     return; | 
| 84   } | 116   } | 
| 85 | 117 | 
| 86   // HTTP_BAD_REQUEST means the arguments are invalid. | 118   // HTTP_BAD_REQUEST means the arguments are invalid. | 
| 87   if (response_code == net::HTTP_BAD_REQUEST) { | 119   if (response_code == net::HTTP_BAD_REQUEST) { | 
| 88     LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; | 120     LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; | 
| 89     delegate_->OnOAuthError(); | 121     delegate_->OnOAuthError(); | 
| 90     return; | 122     return; | 
| 91   } | 123   } | 
| 92 | 124 | 
| 93   std::string access_token; |  | 
| 94   std::string refresh_token; |  | 
| 95   int expires_in_seconds = 0; |  | 
| 96   if (response_code == net::HTTP_OK) { | 125   if (response_code == net::HTTP_OK) { | 
| 97     scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | 126     scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | 
| 98     if (message_value.get() && | 127     if (message_value.get() && | 
| 99         message_value->IsType(Value::TYPE_DICTIONARY)) { | 128         message_value->IsType(Value::TYPE_DICTIONARY)) { | 
| 100       scoped_ptr<DictionaryValue> response_dict( | 129       scoped_ptr<DictionaryValue> response_dict( | 
| 101           static_cast<DictionaryValue*>(message_value.release())); | 130           static_cast<DictionaryValue*>(message_value.release())); | 
| 102       response_dict->GetString(kAccessTokenValue, &access_token); | 131       response_dict->GetString(kAccessTokenValue, &access_token_); | 
| 103       response_dict->GetString(kRefreshTokenValue, &refresh_token); | 132       response_dict->GetInteger(kExpiresInValue, &expires_in_seconds_); | 
| 104       response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); |  | 
| 105     } | 133     } | 
| 106     VLOG(1) << "Gaia response: acess_token='" << access_token | 134     VLOG(1) << "Gaia response: acess_token='" << access_token_ | 
| 107             << "', refresh_token='" << refresh_token | 135             << "', expires in " << expires_in_seconds_ << " second(s)"; | 
| 108             << "', expires in " << expires_in_seconds << " second(s)"; |  | 
| 109   } else { | 136   } else { | 
| 110     LOG(ERROR) << "Gaia response: response code=" << response_code; | 137     LOG(ERROR) << "Gaia response: response code=" << response_code; | 
| 111   } | 138   } | 
| 112 | 139 | 
| 113   if (access_token.empty()) { | 140   if (access_token_.empty()) { | 
| 114     delegate_->OnNetworkError(response_code); | 141     delegate_->OnNetworkError(response_code); | 
| 115   } else if (refresh_token.empty()) { | 142   } else { | 
| 116     // If we only have an access token, then this was a refresh request. | 143     FetchUserInfoAndInvokeCallback(); | 
| 117     delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); |  | 
| 118   } | 144   } | 
| 119 } | 145 } | 
| 120 | 146 | 
| 121 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, | 147 void GaiaOAuthClient::Core::FetchUserInfoAndInvokeCallback() { | 
|  | 148   request_.reset(new UrlFetcher( | 
|  | 149       GURL(provider_info_.user_info_url), UrlFetcher::GET)); | 
|  | 150   request_->SetRequestContext(request_context_getter_); | 
|  | 151   request_->SetHeader("Authorization", "Bearer " + access_token_); | 
|  | 152   request_->Start( | 
|  | 153       base::Bind(&GaiaOAuthClient::Core::OnUserInfoFetchComplete, this)); | 
|  | 154 } | 
|  | 155 | 
|  | 156 void GaiaOAuthClient::Core::OnUserInfoFetchComplete( | 
|  | 157     const net::URLRequestStatus& status, | 
|  | 158     int response_code, | 
|  | 159     const std::string& response) { | 
|  | 160   std::string email; | 
|  | 161   if (response_code == net::HTTP_OK) { | 
|  | 162     scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | 
|  | 163     if (message_value.get() && | 
|  | 164         message_value->IsType(Value::TYPE_DICTIONARY)) { | 
|  | 165       scoped_ptr<DictionaryValue> response_dict( | 
|  | 166           static_cast<DictionaryValue*>(message_value.release())); | 
|  | 167       response_dict->GetString(kEmailValue, &email); | 
|  | 168     } | 
|  | 169   } | 
|  | 170 | 
|  | 171   if (email.empty()) { | 
|  | 172     delegate_->OnNetworkError(response_code); | 
|  | 173   } else { | 
|  | 174     delegate_->OnRefreshTokenResponse( | 
|  | 175         email, access_token_, expires_in_seconds_); | 
|  | 176   } | 
|  | 177 } | 
|  | 178 | 
|  | 179 GaiaOAuthClient::GaiaOAuthClient(const OAuthProviderInfo& provider_info, | 
| 122                                  net::URLRequestContextGetter* context_getter) { | 180                                  net::URLRequestContextGetter* context_getter) { | 
| 123   core_ = new Core(gaia_url, context_getter); | 181   core_ = new Core(provider_info, context_getter); | 
| 124 } | 182 } | 
| 125 | 183 | 
| 126 GaiaOAuthClient::~GaiaOAuthClient() { | 184 GaiaOAuthClient::~GaiaOAuthClient() { | 
| 127 } | 185 } | 
| 128 | 186 | 
| 129 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 187 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 
| 130                                    const std::string& refresh_token, | 188                                    const std::string& refresh_token, | 
| 131                                    Delegate* delegate) { | 189                                    Delegate* delegate) { | 
| 132   return core_->RefreshToken(oauth_client_info, | 190   return core_->RefreshToken(oauth_client_info, | 
| 133                              refresh_token, | 191                              refresh_token, | 
| 134                              delegate); | 192                              delegate); | 
| 135 } | 193 } | 
| 136 | 194 | 
| 137 }  // namespace remoting | 195 }  // namespace remoting | 
| OLD | NEW | 
|---|