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