| OLD | NEW |
| (Empty) |
| 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 "remoting/host/gaia_oauth_client.h" | |
| 6 | |
| 7 #include "base/bind.h" | |
| 8 #include "base/compiler_specific.h" | |
| 9 #include "base/json/json_reader.h" | |
| 10 #include "base/logging.h" | |
| 11 #include "base/memory/scoped_ptr.h" | |
| 12 #include "base/values.h" | |
| 13 #include "googleurl/src/gurl.h" | |
| 14 #include "net/base/escape.h" | |
| 15 #include "net/http/http_status_code.h" | |
| 16 #include "net/url_request/url_fetcher.h" | |
| 17 #include "net/url_request/url_fetcher_delegate.h" | |
| 18 #include "net/url_request/url_request_context_getter.h" | |
| 19 #include "net/url_request/url_request_status.h" | |
| 20 | |
| 21 namespace { | |
| 22 | |
| 23 const char kDefaultOAuth2TokenUrl[] = | |
| 24 "https://accounts.google.com/o/oauth2/token"; | |
| 25 const char kDefaultOAuth2UserInfoUrl[] = | |
| 26 "https://www.googleapis.com/oauth2/v1/userinfo"; | |
| 27 | |
| 28 // Values used to parse token response. | |
| 29 const char kAccessTokenValue[] = "access_token"; | |
| 30 const char kRefreshTokenValue[] = "refresh_token"; | |
| 31 const char kExpiresInValue[] = "expires_in"; | |
| 32 | |
| 33 // Values used when parsing userinfo response. | |
| 34 const char kEmailValue[] = "email"; | |
| 35 | |
| 36 } // namespace | |
| 37 | |
| 38 namespace remoting { | |
| 39 | |
| 40 // static | |
| 41 OAuthProviderInfo OAuthProviderInfo::GetDefault() { | |
| 42 OAuthProviderInfo result; | |
| 43 result.access_token_url = kDefaultOAuth2TokenUrl; | |
| 44 result.user_info_url = kDefaultOAuth2UserInfoUrl; | |
| 45 return result; | |
| 46 } | |
| 47 | |
| 48 class GaiaOAuthClient::Core | |
| 49 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, | |
| 50 public net::URLFetcherDelegate { | |
| 51 public: | |
| 52 Core(const OAuthProviderInfo& info, | |
| 53 net::URLRequestContextGetter* request_context_getter) | |
| 54 : provider_info_(info), | |
| 55 request_context_getter_(request_context_getter), | |
| 56 delegate_(NULL), | |
| 57 url_fetcher_type_(URL_FETCHER_NONE) { | |
| 58 } | |
| 59 | |
| 60 void RefreshToken(const OAuthClientInfo& oauth_client_info, | |
| 61 const std::string& refresh_token, | |
| 62 GaiaOAuthClient::Delegate* delegate); | |
| 63 | |
| 64 // net::URLFetcherDelegate interface | |
| 65 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; | |
| 66 | |
| 67 private: | |
| 68 friend class base::RefCountedThreadSafe<Core>; | |
| 69 | |
| 70 enum URLFetcherType { | |
| 71 URL_FETCHER_NONE, | |
| 72 URL_FETCHER_REFRESH_TOKEN, | |
| 73 URL_FETCHER_GET_USER_INFO | |
| 74 }; | |
| 75 | |
| 76 virtual ~Core() {} | |
| 77 | |
| 78 void OnAuthTokenFetchComplete(const net::URLRequestStatus& status, | |
| 79 int response_code, | |
| 80 const std::string& response); | |
| 81 void FetchUserInfoAndInvokeCallback(); | |
| 82 void OnUserInfoFetchComplete(const net::URLRequestStatus& status, | |
| 83 int response_code, | |
| 84 const std::string& response); | |
| 85 | |
| 86 OAuthProviderInfo provider_info_; | |
| 87 | |
| 88 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | |
| 89 GaiaOAuthClient::Delegate* delegate_; | |
| 90 scoped_ptr<net::URLFetcher> request_; | |
| 91 URLFetcherType url_fetcher_type_; | |
| 92 | |
| 93 std::string access_token_; | |
| 94 int expires_in_seconds_; | |
| 95 }; | |
| 96 | |
| 97 void GaiaOAuthClient::Core::RefreshToken( | |
| 98 const OAuthClientInfo& oauth_client_info, | |
| 99 const std::string& refresh_token, | |
| 100 GaiaOAuthClient::Delegate* delegate) { | |
| 101 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | |
| 102 | |
| 103 delegate_ = delegate; | |
| 104 | |
| 105 access_token_.clear(); | |
| 106 expires_in_seconds_ = 0; | |
| 107 | |
| 108 std::string post_body = | |
| 109 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | |
| 110 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | |
| 111 true) + | |
| 112 "&client_secret=" + | |
| 113 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | |
| 114 "&grant_type=refresh_token"; | |
| 115 request_.reset(net::URLFetcher::Create( | |
| 116 GURL(provider_info_.access_token_url), net::URLFetcher::POST, this)); | |
| 117 request_->SetRequestContext(request_context_getter_); | |
| 118 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | |
| 119 url_fetcher_type_ = URL_FETCHER_REFRESH_TOKEN; | |
| 120 request_->Start(); | |
| 121 } | |
| 122 | |
| 123 void GaiaOAuthClient::Core::OnURLFetchComplete( | |
| 124 const net::URLFetcher* source) { | |
| 125 std::string response_string; | |
| 126 source->GetResponseAsString(&response_string); | |
| 127 switch (url_fetcher_type_) { | |
| 128 case URL_FETCHER_REFRESH_TOKEN: | |
| 129 OnAuthTokenFetchComplete(source->GetStatus(), | |
| 130 source->GetResponseCode(), | |
| 131 response_string); | |
| 132 break; | |
| 133 case URL_FETCHER_GET_USER_INFO: | |
| 134 OnUserInfoFetchComplete(source->GetStatus(), | |
| 135 source->GetResponseCode(), | |
| 136 response_string); | |
| 137 break; | |
| 138 default: | |
| 139 LOG(ERROR) << "Unrecognised URLFetcher type: " << url_fetcher_type_; | |
| 140 } | |
| 141 } | |
| 142 | |
| 143 void GaiaOAuthClient::Core::OnAuthTokenFetchComplete( | |
| 144 const net::URLRequestStatus& status, | |
| 145 int response_code, | |
| 146 const std::string& response) { | |
| 147 request_.reset(); | |
| 148 | |
| 149 if (!status.is_success()) { | |
| 150 delegate_->OnNetworkError(response_code); | |
| 151 return; | |
| 152 } | |
| 153 | |
| 154 // HTTP_BAD_REQUEST means the arguments are invalid. | |
| 155 if (response_code == net::HTTP_BAD_REQUEST) { | |
| 156 LOG(ERROR) << "Gaia response: response code=net::HTTP_BAD_REQUEST."; | |
| 157 delegate_->OnOAuthError(); | |
| 158 return; | |
| 159 } | |
| 160 | |
| 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 std::string access_token; | |
| 168 response_dict->GetString(kAccessTokenValue, &access_token); | |
| 169 if (access_token.find("\r\n") != std::string::npos) { | |
| 170 LOG(ERROR) << "Gaia response: access token include CRLF"; | |
| 171 delegate_->OnOAuthError(); | |
| 172 return; | |
| 173 } | |
| 174 access_token_ = access_token; | |
| 175 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds_); | |
| 176 } | |
| 177 VLOG(1) << "Gaia response: acess_token='" << access_token_ | |
| 178 << "', expires in " << expires_in_seconds_ << " second(s)"; | |
| 179 } else { | |
| 180 LOG(ERROR) << "Gaia response: response code=" << response_code; | |
| 181 } | |
| 182 | |
| 183 if (access_token_.empty()) { | |
| 184 delegate_->OnNetworkError(response_code); | |
| 185 } else { | |
| 186 FetchUserInfoAndInvokeCallback(); | |
| 187 } | |
| 188 } | |
| 189 | |
| 190 void GaiaOAuthClient::Core::FetchUserInfoAndInvokeCallback() { | |
| 191 request_.reset(net::URLFetcher::Create( | |
| 192 GURL(provider_info_.user_info_url), net::URLFetcher::GET, this)); | |
| 193 request_->SetRequestContext(request_context_getter_); | |
| 194 request_->AddExtraRequestHeader("Authorization: Bearer " + access_token_); | |
| 195 url_fetcher_type_ = URL_FETCHER_GET_USER_INFO; | |
| 196 request_->Start(); | |
| 197 } | |
| 198 | |
| 199 void GaiaOAuthClient::Core::OnUserInfoFetchComplete( | |
| 200 const net::URLRequestStatus& status, | |
| 201 int response_code, | |
| 202 const std::string& response) { | |
| 203 request_.reset(); | |
| 204 url_fetcher_type_ = URL_FETCHER_NONE; | |
| 205 std::string email; | |
| 206 if (response_code == net::HTTP_OK) { | |
| 207 scoped_ptr<Value> message_value(base::JSONReader::Read(response)); | |
| 208 if (message_value.get() && | |
| 209 message_value->IsType(Value::TYPE_DICTIONARY)) { | |
| 210 scoped_ptr<DictionaryValue> response_dict( | |
| 211 static_cast<DictionaryValue*>(message_value.release())); | |
| 212 response_dict->GetString(kEmailValue, &email); | |
| 213 } | |
| 214 } | |
| 215 | |
| 216 if (email.empty()) { | |
| 217 delegate_->OnNetworkError(response_code); | |
| 218 } else { | |
| 219 delegate_->OnRefreshTokenResponse( | |
| 220 email, access_token_, expires_in_seconds_); | |
| 221 } | |
| 222 } | |
| 223 | |
| 224 GaiaOAuthClient::GaiaOAuthClient(const OAuthProviderInfo& provider_info, | |
| 225 net::URLRequestContextGetter* context_getter) { | |
| 226 core_ = new Core(provider_info, context_getter); | |
| 227 } | |
| 228 | |
| 229 GaiaOAuthClient::~GaiaOAuthClient() { | |
| 230 } | |
| 231 | |
| 232 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | |
| 233 const std::string& refresh_token, | |
| 234 Delegate* delegate) { | |
| 235 return core_->RefreshToken(oauth_client_info, | |
| 236 refresh_token, | |
| 237 delegate); | |
| 238 } | |
| 239 | |
| 240 } // namespace remoting | |
| OLD | NEW |