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 |