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 "google_apis/gaia/gaia_oauth_client.h" | 5 #include "google_apis/gaia/gaia_oauth_client.h" |
6 | 6 |
7 #include "base/json/json_reader.h" | 7 #include "base/json/json_reader.h" |
8 #include "base/logging.h" | 8 #include "base/logging.h" |
9 #include "base/memory/scoped_ptr.h" | 9 #include "base/memory/scoped_ptr.h" |
| 10 #include "base/string_util.cc" |
10 #include "base/values.h" | 11 #include "base/values.h" |
11 #include "google_apis/gaia/gaia_urls.h" | 12 #include "google_apis/gaia/gaia_urls.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" |
15 #include "net/url_request/url_fetcher.h" | 16 #include "net/url_request/url_fetcher.h" |
16 #include "net/url_request/url_fetcher_delegate.h" | 17 #include "net/url_request/url_fetcher_delegate.h" |
17 #include "net/url_request/url_request_context_getter.h" | 18 #include "net/url_request/url_request_context_getter.h" |
18 | 19 |
19 namespace { | 20 namespace { |
20 const char kAccessTokenValue[] = "access_token"; | 21 const char kAccessTokenValue[] = "access_token"; |
21 const char kRefreshTokenValue[] = "refresh_token"; | 22 const char kRefreshTokenValue[] = "refresh_token"; |
22 const char kExpiresInValue[] = "expires_in"; | 23 const char kExpiresInValue[] = "expires_in"; |
23 } | 24 } |
24 | 25 |
25 namespace gaia { | 26 namespace gaia { |
26 | 27 |
27 class GaiaOAuthClient::Core | 28 class GaiaOAuthClient::Core |
28 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, | 29 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, |
29 public net::URLFetcherDelegate { | 30 public net::URLFetcherDelegate { |
30 public: | 31 public: |
31 Core(const std::string& gaia_url, | 32 Core(net::URLRequestContextGetter* request_context_getter) |
32 net::URLRequestContextGetter* request_context_getter) | 33 : num_retries_(0), |
33 : gaia_url_(gaia_url), | 34 request_context_getter_(request_context_getter), |
34 num_retries_(0), | 35 delegate_(NULL), |
35 request_context_getter_(request_context_getter), | 36 request_type_(NO_PENDING_REQUEST) { |
36 delegate_(NULL), | |
37 request_type_(NO_PENDING_REQUEST) { | |
38 } | 37 } |
39 | 38 |
40 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, | 39 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, |
41 const std::string& auth_code, | 40 const std::string& auth_code, |
42 int max_retries, | 41 int max_retries, |
43 GaiaOAuthClient::Delegate* delegate); | 42 GaiaOAuthClient::Delegate* delegate); |
44 void RefreshToken(const OAuthClientInfo& oauth_client_info, | 43 void RefreshToken(const OAuthClientInfo& oauth_client_info, |
45 const std::string& refresh_token, | 44 const std::string& refresh_token, |
| 45 const std::vector<std::string>& scopes, |
46 int max_retries, | 46 int max_retries, |
47 GaiaOAuthClient::Delegate* delegate); | 47 GaiaOAuthClient::Delegate* delegate); |
48 void GetUserInfo(const std::string& oauth_access_token, | 48 void GetUserInfo(const std::string& oauth_access_token, |
| 49 int max_retries, |
| 50 Delegate* delegate); |
| 51 void GetTokenInfo(const std::string& oauth_access_token, |
49 int max_retries, | 52 int max_retries, |
50 Delegate* delegate); | 53 Delegate* delegate); |
51 | 54 |
52 // net::URLFetcherDelegate implementation. | 55 // net::URLFetcherDelegate implementation. |
53 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; | 56 virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; |
54 | 57 |
55 private: | 58 private: |
56 friend class base::RefCountedThreadSafe<Core>; | 59 friend class base::RefCountedThreadSafe<Core>; |
57 | 60 |
58 enum RequestType { | 61 enum RequestType { |
59 NO_PENDING_REQUEST, | 62 NO_PENDING_REQUEST, |
60 TOKENS_FROM_AUTH_CODE, | 63 TOKENS_FROM_AUTH_CODE, |
61 REFRESH_TOKEN, | 64 REFRESH_TOKEN, |
| 65 TOKEN_INFO, |
62 USER_INFO, | 66 USER_INFO, |
63 }; | 67 }; |
64 | 68 |
65 virtual ~Core() {} | 69 virtual ~Core() {} |
66 | 70 |
67 void MakeGaiaRequest(const std::string& post_body, | 71 void MakeGaiaRequest(const GURL& url, |
| 72 const std::string& post_body, |
68 int max_retries, | 73 int max_retries, |
69 GaiaOAuthClient::Delegate* delegate); | 74 GaiaOAuthClient::Delegate* delegate); |
70 void HandleResponse(const net::URLFetcher* source, | 75 void HandleResponse(const net::URLFetcher* source, |
71 bool* should_retry_request); | 76 bool* should_retry_request); |
72 | 77 |
73 GURL gaia_url_; | |
74 int num_retries_; | 78 int num_retries_; |
75 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | 79 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; |
76 GaiaOAuthClient::Delegate* delegate_; | 80 GaiaOAuthClient::Delegate* delegate_; |
77 scoped_ptr<net::URLFetcher> request_; | 81 scoped_ptr<net::URLFetcher> request_; |
78 RequestType request_type_; | 82 RequestType request_type_; |
79 }; | 83 }; |
80 | 84 |
81 void GaiaOAuthClient::Core::GetTokensFromAuthCode( | 85 void GaiaOAuthClient::Core::GetTokensFromAuthCode( |
82 const OAuthClientInfo& oauth_client_info, | 86 const OAuthClientInfo& oauth_client_info, |
83 const std::string& auth_code, | 87 const std::string& auth_code, |
84 int max_retries, | 88 int max_retries, |
85 GaiaOAuthClient::Delegate* delegate) { | 89 GaiaOAuthClient::Delegate* delegate) { |
86 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 90 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
87 request_type_ = TOKENS_FROM_AUTH_CODE; | 91 request_type_ = TOKENS_FROM_AUTH_CODE; |
88 std::string post_body = | 92 std::string post_body = |
89 "code=" + net::EscapeUrlEncodedData(auth_code, true) + | 93 "code=" + net::EscapeUrlEncodedData(auth_code, true) + |
90 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 94 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
91 true) + | 95 true) + |
92 "&client_secret=" + | 96 "&client_secret=" + |
93 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 97 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
94 "&redirect_uri=" + | 98 "&redirect_uri=" + |
95 net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) + | 99 net::EscapeUrlEncodedData(oauth_client_info.redirect_uri, true) + |
96 "&grant_type=authorization_code"; | 100 "&grant_type=authorization_code"; |
97 MakeGaiaRequest(post_body, max_retries, delegate); | 101 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), |
| 102 post_body, max_retries, delegate); |
98 } | 103 } |
99 | 104 |
100 void GaiaOAuthClient::Core::RefreshToken( | 105 void GaiaOAuthClient::Core::RefreshToken( |
101 const OAuthClientInfo& oauth_client_info, | 106 const OAuthClientInfo& oauth_client_info, |
102 const std::string& refresh_token, | 107 const std::string& refresh_token, |
| 108 const std::vector<std::string>& scopes, |
103 int max_retries, | 109 int max_retries, |
104 GaiaOAuthClient::Delegate* delegate) { | 110 GaiaOAuthClient::Delegate* delegate) { |
105 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 111 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
106 request_type_ = REFRESH_TOKEN; | 112 request_type_ = REFRESH_TOKEN; |
107 std::string post_body = | 113 std::string post_body = |
108 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | 114 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + |
109 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | 115 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, |
110 true) + | 116 true) + |
111 "&client_secret=" + | 117 "&client_secret=" + |
112 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | 118 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + |
113 "&grant_type=refresh_token"; | 119 "&grant_type=refresh_token"; |
114 MakeGaiaRequest(post_body, max_retries, delegate); | 120 |
| 121 if (!scopes.empty()) { |
| 122 std::string scopes_string = JoinString(scopes, ' '); |
| 123 post_body += "&scope=" + net::EscapeUrlEncodedData(scopes_string, true); |
| 124 } |
| 125 |
| 126 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_url()), |
| 127 post_body, max_retries, delegate); |
115 } | 128 } |
116 | 129 |
117 void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token, | 130 void GaiaOAuthClient::Core::GetUserInfo(const std::string& oauth_access_token, |
118 int max_retries, | 131 int max_retries, |
119 Delegate* delegate) { | 132 Delegate* delegate) { |
120 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); | 133 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
121 DCHECK(!request_.get()); | 134 DCHECK(!request_.get()); |
122 request_type_ = USER_INFO; | 135 request_type_ = USER_INFO; |
123 delegate_ = delegate; | 136 delegate_ = delegate; |
124 num_retries_ = 0; | 137 num_retries_ = 0; |
125 request_.reset(net::URLFetcher::Create( | 138 request_.reset(net::URLFetcher::Create( |
126 0, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()), | 139 0, GURL(GaiaUrls::GetInstance()->oauth_user_info_url()), |
127 net::URLFetcher::GET, this)); | 140 net::URLFetcher::GET, this)); |
128 request_->SetRequestContext(request_context_getter_.get()); | 141 request_->SetRequestContext(request_context_getter_.get()); |
129 request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token); | 142 request_->AddExtraRequestHeader("Authorization: OAuth " + oauth_access_token); |
130 request_->SetMaxRetriesOn5xx(max_retries); | 143 request_->SetMaxRetriesOn5xx(max_retries); |
131 // Fetchers are sometimes cancelled because a network change was detected, | 144 // Fetchers are sometimes cancelled because a network change was detected, |
132 // especially at startup and after sign-in on ChromeOS. Retrying once should | 145 // especially at startup and after sign-in on ChromeOS. Retrying once should |
133 // be enough in those cases; let the fetcher retry up to 3 times just in case. | 146 // be enough in those cases; let the fetcher retry up to 3 times just in case. |
134 // http://crbug.com/163710 | 147 // http://crbug.com/163710 |
135 request_->SetAutomaticallyRetryOnNetworkChanges(3); | 148 request_->SetAutomaticallyRetryOnNetworkChanges(3); |
136 request_->Start(); | 149 request_->Start(); |
137 } | 150 } |
138 | 151 |
| 152 void GaiaOAuthClient::Core::GetTokenInfo(const std::string& oauth_access_token, |
| 153 int max_retries, |
| 154 Delegate* delegate) { |
| 155 DCHECK_EQ(request_type_, NO_PENDING_REQUEST); |
| 156 DCHECK(!request_.get()); |
| 157 request_type_ = TOKEN_INFO; |
| 158 std::string post_body = |
| 159 "access_token=" + net::EscapeUrlEncodedData(oauth_access_token, true); |
| 160 MakeGaiaRequest(GURL(GaiaUrls::GetInstance()->oauth2_token_info_url()), |
| 161 post_body, |
| 162 max_retries, |
| 163 delegate); |
| 164 } |
| 165 |
139 void GaiaOAuthClient::Core::MakeGaiaRequest( | 166 void GaiaOAuthClient::Core::MakeGaiaRequest( |
| 167 const GURL& url, |
140 const std::string& post_body, | 168 const std::string& post_body, |
141 int max_retries, | 169 int max_retries, |
142 GaiaOAuthClient::Delegate* delegate) { | 170 GaiaOAuthClient::Delegate* delegate) { |
143 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | 171 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; |
144 delegate_ = delegate; | 172 delegate_ = delegate; |
145 num_retries_ = 0; | 173 num_retries_ = 0; |
146 request_.reset(net::URLFetcher::Create( | 174 request_.reset(net::URLFetcher::Create( |
147 0, gaia_url_, net::URLFetcher::POST, this)); | 175 0, url, net::URLFetcher::POST, this)); |
148 request_->SetRequestContext(request_context_getter_.get()); | 176 request_->SetRequestContext(request_context_getter_.get()); |
149 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | 177 request_->SetUploadData("application/x-www-form-urlencoded", post_body); |
150 request_->SetMaxRetriesOn5xx(max_retries); | 178 request_->SetMaxRetriesOn5xx(max_retries); |
151 // See comment on SetAutomaticallyRetryOnNetworkChanges() above. | 179 // See comment on SetAutomaticallyRetryOnNetworkChanges() above. |
152 request_->SetAutomaticallyRetryOnNetworkChanges(3); | 180 request_->SetAutomaticallyRetryOnNetworkChanges(3); |
153 request_->Start(); | 181 request_->Start(); |
154 } | 182 } |
155 | 183 |
156 // URLFetcher::Delegate implementation. | 184 // URLFetcher::Delegate implementation. |
157 void GaiaOAuthClient::Core::OnURLFetchComplete( | 185 void GaiaOAuthClient::Core::OnURLFetchComplete( |
(...skipping 10 matching lines...) Expand all Loading... |
168 // We must set our request_context_getter_ again because | 196 // We must set our request_context_getter_ again because |
169 // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... | 197 // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... |
170 request_->SetRequestContext(request_context_getter_.get()); | 198 request_->SetRequestContext(request_context_getter_.get()); |
171 request_->Start(); | 199 request_->Start(); |
172 } | 200 } |
173 } | 201 } |
174 | 202 |
175 void GaiaOAuthClient::Core::HandleResponse( | 203 void GaiaOAuthClient::Core::HandleResponse( |
176 const net::URLFetcher* source, | 204 const net::URLFetcher* source, |
177 bool* should_retry_request) { | 205 bool* should_retry_request) { |
178 // Keep the URLFetcher object in case we need to reuse it. | 206 // Move ownership of the request fetcher into a local scoped_ptr which |
| 207 // will be nuked when we're done handling the request, unless we need |
| 208 // to retry, in which case ownership will be returned to request_. |
179 scoped_ptr<net::URLFetcher> old_request = request_.Pass(); | 209 scoped_ptr<net::URLFetcher> old_request = request_.Pass(); |
180 DCHECK_EQ(source, old_request.get()); | 210 DCHECK_EQ(source, old_request.get()); |
181 | 211 |
182 // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are | 212 // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are |
183 // done here. | 213 // done here. |
184 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { | 214 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { |
185 delegate_->OnOAuthError(); | 215 delegate_->OnOAuthError(); |
186 return; | 216 return; |
187 } | 217 } |
188 | 218 |
(...skipping 27 matching lines...) Expand all Loading... |
216 request_type_ = NO_PENDING_REQUEST; | 246 request_type_ = NO_PENDING_REQUEST; |
217 | 247 |
218 switch (type) { | 248 switch (type) { |
219 case USER_INFO: { | 249 case USER_INFO: { |
220 std::string email; | 250 std::string email; |
221 response_dict->GetString("email", &email); | 251 response_dict->GetString("email", &email); |
222 delegate_->OnGetUserInfoResponse(email); | 252 delegate_->OnGetUserInfoResponse(email); |
223 break; | 253 break; |
224 } | 254 } |
225 | 255 |
| 256 case TOKEN_INFO: { |
| 257 delegate_->OnGetTokenInfoResponse(response_dict.Pass()); |
| 258 break; |
| 259 } |
| 260 |
226 case TOKENS_FROM_AUTH_CODE: | 261 case TOKENS_FROM_AUTH_CODE: |
227 case REFRESH_TOKEN: { | 262 case REFRESH_TOKEN: { |
228 std::string access_token; | 263 std::string access_token; |
229 std::string refresh_token; | 264 std::string refresh_token; |
230 int expires_in_seconds = 0; | 265 int expires_in_seconds = 0; |
231 response_dict->GetString(kAccessTokenValue, &access_token); | 266 response_dict->GetString(kAccessTokenValue, &access_token); |
232 response_dict->GetString(kRefreshTokenValue, &refresh_token); | 267 response_dict->GetString(kRefreshTokenValue, &refresh_token); |
233 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); | 268 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); |
234 | 269 |
235 if (access_token.empty()) { | 270 if (access_token.empty()) { |
236 delegate_->OnOAuthError(); | 271 delegate_->OnOAuthError(); |
237 return; | 272 return; |
238 } | 273 } |
239 | 274 |
240 if (type == REFRESH_TOKEN) { | 275 if (type == REFRESH_TOKEN) { |
241 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); | 276 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); |
242 } else { | 277 } else { |
243 delegate_->OnGetTokensResponse(refresh_token, | 278 delegate_->OnGetTokensResponse(refresh_token, |
244 access_token, | 279 access_token, |
245 expires_in_seconds); | 280 expires_in_seconds); |
246 } | 281 } |
247 break; | 282 break; |
248 } | 283 } |
249 | 284 |
250 default: | 285 default: |
251 NOTREACHED(); | 286 NOTREACHED(); |
252 } | 287 } |
253 } | 288 } |
254 | 289 |
255 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, | 290 GaiaOAuthClient::GaiaOAuthClient(net::URLRequestContextGetter* context_getter) { |
256 net::URLRequestContextGetter* context_getter) { | 291 core_ = new Core(context_getter); |
257 core_ = new Core(gaia_url, context_getter); | |
258 } | 292 } |
259 | 293 |
260 GaiaOAuthClient::~GaiaOAuthClient() { | 294 GaiaOAuthClient::~GaiaOAuthClient() { |
261 } | 295 } |
262 | 296 |
263 void GaiaOAuthClient::GetTokensFromAuthCode( | 297 void GaiaOAuthClient::GetTokensFromAuthCode( |
264 const OAuthClientInfo& oauth_client_info, | 298 const OAuthClientInfo& oauth_client_info, |
265 const std::string& auth_code, | 299 const std::string& auth_code, |
266 int max_retries, | 300 int max_retries, |
267 Delegate* delegate) { | 301 Delegate* delegate) { |
268 return core_->GetTokensFromAuthCode(oauth_client_info, | 302 return core_->GetTokensFromAuthCode(oauth_client_info, |
269 auth_code, | 303 auth_code, |
270 max_retries, | 304 max_retries, |
271 delegate); | 305 delegate); |
272 } | 306 } |
273 | 307 |
274 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | 308 void GaiaOAuthClient::RefreshToken( |
275 const std::string& refresh_token, | 309 const OAuthClientInfo& oauth_client_info, |
276 int max_retries, | 310 const std::string& refresh_token, |
277 Delegate* delegate) { | 311 const std::vector<std::string>& scopes, |
| 312 int max_retries, |
| 313 Delegate* delegate) { |
278 return core_->RefreshToken(oauth_client_info, | 314 return core_->RefreshToken(oauth_client_info, |
279 refresh_token, | 315 refresh_token, |
| 316 scopes, |
280 max_retries, | 317 max_retries, |
281 delegate); | 318 delegate); |
282 } | 319 } |
283 | 320 |
284 void GaiaOAuthClient::GetUserInfo(const std::string& access_token, | 321 void GaiaOAuthClient::GetUserInfo(const std::string& access_token, |
285 int max_retries, | 322 int max_retries, |
286 Delegate* delegate) { | 323 Delegate* delegate) { |
287 return core_->GetUserInfo(access_token, max_retries, delegate); | 324 return core_->GetUserInfo(access_token, max_retries, delegate); |
288 } | 325 } |
289 | 326 |
| 327 void GaiaOAuthClient::GetTokenInfo(const std::string& access_token, |
| 328 int max_retries, |
| 329 Delegate* delegate) { |
| 330 return core_->GetTokenInfo(access_token, max_retries, delegate); |
| 331 } |
| 332 |
290 } // namespace gaia | 333 } // namespace gaia |
OLD | NEW |