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 "chrome/common/net/gaia/gaia_oauth_client.h" | |
6 | |
7 #include "base/json/json_reader.h" | |
8 #include "base/logging.h" | |
9 #include "base/memory/scoped_ptr.h" | |
10 #include "base/values.h" | |
11 #include "googleurl/src/gurl.h" | |
12 #include "net/base/escape.h" | |
13 #include "net/http/http_status_code.h" | |
14 #include "net/url_request/url_fetcher.h" | |
15 #include "net/url_request/url_fetcher_delegate.h" | |
16 #include "net/url_request/url_request_context_getter.h" | |
17 | |
18 namespace { | |
19 const char kAccessTokenValue[] = "access_token"; | |
20 const char kRefreshTokenValue[] = "refresh_token"; | |
21 const char kExpiresInValue[] = "expires_in"; | |
22 } | |
23 | |
24 namespace gaia { | |
25 | |
26 class GaiaOAuthClient::Core | |
27 : public base::RefCountedThreadSafe<GaiaOAuthClient::Core>, | |
28 public net::URLFetcherDelegate { | |
29 public: | |
30 Core(const std::string& gaia_url, | |
31 net::URLRequestContextGetter* request_context_getter) | |
32 : gaia_url_(gaia_url), | |
33 num_retries_(0), | |
34 request_context_getter_(request_context_getter), | |
35 delegate_(NULL) {} | |
36 | |
37 void GetTokensFromAuthCode(const OAuthClientInfo& oauth_client_info, | |
38 const std::string& auth_code, | |
39 int max_retries, | |
40 GaiaOAuthClient::Delegate* delegate); | |
41 void RefreshToken(const OAuthClientInfo& oauth_client_info, | |
42 const std::string& refresh_token, | |
43 int max_retries, | |
44 GaiaOAuthClient::Delegate* delegate); | |
45 | |
46 // net::URLFetcherDelegate implementation. | |
47 virtual void OnURLFetchComplete(const net::URLFetcher* source); | |
48 | |
49 private: | |
50 friend class base::RefCountedThreadSafe<Core>; | |
51 virtual ~Core() {} | |
52 | |
53 void MakeGaiaRequest(const std::string& post_body, | |
54 int max_retries, | |
55 GaiaOAuthClient::Delegate* delegate); | |
56 void HandleResponse(const net::URLFetcher* source, | |
57 bool* should_retry_request); | |
58 | |
59 GURL gaia_url_; | |
60 int num_retries_; | |
61 scoped_refptr<net::URLRequestContextGetter> request_context_getter_; | |
62 GaiaOAuthClient::Delegate* delegate_; | |
63 scoped_ptr<net::URLFetcher> request_; | |
64 }; | |
65 | |
66 void GaiaOAuthClient::Core::GetTokensFromAuthCode( | |
67 const OAuthClientInfo& oauth_client_info, | |
68 const std::string& auth_code, | |
69 int max_retries, | |
70 GaiaOAuthClient::Delegate* delegate) { | |
71 std::string post_body = | |
72 "code=" + net::EscapeUrlEncodedData(auth_code, true) + | |
73 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | |
74 true) + | |
75 "&client_secret=" + | |
76 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | |
77 "&redirect_uri=oob&grant_type=authorization_code"; | |
78 MakeGaiaRequest(post_body, max_retries, delegate); | |
79 } | |
80 | |
81 void GaiaOAuthClient::Core::RefreshToken( | |
82 const OAuthClientInfo& oauth_client_info, | |
83 const std::string& refresh_token, | |
84 int max_retries, | |
85 GaiaOAuthClient::Delegate* delegate) { | |
86 std::string post_body = | |
87 "refresh_token=" + net::EscapeUrlEncodedData(refresh_token, true) + | |
88 "&client_id=" + net::EscapeUrlEncodedData(oauth_client_info.client_id, | |
89 true) + | |
90 "&client_secret=" + | |
91 net::EscapeUrlEncodedData(oauth_client_info.client_secret, true) + | |
92 "&grant_type=refresh_token"; | |
93 MakeGaiaRequest(post_body, max_retries, delegate); | |
94 } | |
95 | |
96 void GaiaOAuthClient::Core::MakeGaiaRequest( | |
97 const std::string& post_body, | |
98 int max_retries, | |
99 GaiaOAuthClient::Delegate* delegate) { | |
100 DCHECK(!request_.get()) << "Tried to fetch two things at once!"; | |
101 delegate_ = delegate; | |
102 num_retries_ = 0; | |
103 request_.reset(net::URLFetcher::Create( | |
104 0, gaia_url_, net::URLFetcher::POST, this)); | |
105 request_->SetRequestContext(request_context_getter_); | |
106 request_->SetUploadData("application/x-www-form-urlencoded", post_body); | |
107 request_->SetMaxRetries(max_retries); | |
108 request_->Start(); | |
109 } | |
110 | |
111 // URLFetcher::Delegate implementation. | |
112 void GaiaOAuthClient::Core::OnURLFetchComplete( | |
113 const net::URLFetcher* source) { | |
114 bool should_retry = false; | |
115 HandleResponse(source, &should_retry); | |
116 if (should_retry) { | |
117 // Explicitly call ReceivedContentWasMalformed() to ensure the current | |
118 // request gets counted as a failure for calculation of the back-off | |
119 // period. If it was already a failure by status code, this call will | |
120 // be ignored. | |
121 request_->ReceivedContentWasMalformed(); | |
122 num_retries_++; | |
123 // We must set our request_context_getter_ again because | |
124 // URLFetcher::Core::RetryOrCompleteUrlFetch resets it to NULL... | |
125 request_->SetRequestContext(request_context_getter_); | |
126 request_->Start(); | |
127 } else { | |
128 request_.reset(); | |
129 } | |
130 } | |
131 | |
132 void GaiaOAuthClient::Core::HandleResponse( | |
133 const net::URLFetcher* source, | |
134 bool* should_retry_request) { | |
135 *should_retry_request = false; | |
136 // RC_BAD_REQUEST means the arguments are invalid. No point retrying. We are | |
137 // done here. | |
138 if (source->GetResponseCode() == net::HTTP_BAD_REQUEST) { | |
139 delegate_->OnOAuthError(); | |
140 return; | |
141 } | |
142 std::string access_token; | |
143 std::string refresh_token; | |
144 int expires_in_seconds = 0; | |
145 if (source->GetResponseCode() == net::HTTP_OK) { | |
146 std::string data; | |
147 source->GetResponseAsString(&data); | |
148 scoped_ptr<Value> message_value(base::JSONReader::Read(data)); | |
149 if (message_value.get() && | |
150 message_value->IsType(Value::TYPE_DICTIONARY)) { | |
151 scoped_ptr<DictionaryValue> response_dict( | |
152 static_cast<DictionaryValue*>(message_value.release())); | |
153 response_dict->GetString(kAccessTokenValue, &access_token); | |
154 response_dict->GetString(kRefreshTokenValue, &refresh_token); | |
155 response_dict->GetInteger(kExpiresInValue, &expires_in_seconds); | |
156 } | |
157 } | |
158 if (access_token.empty()) { | |
159 // If we don't have an access token yet and the the error was not | |
160 // RC_BAD_REQUEST, we may need to retry. | |
161 if ((-1 != source->GetMaxRetries()) && | |
162 (num_retries_ > source->GetMaxRetries())) { | |
163 // Retry limit reached. Give up. | |
164 delegate_->OnNetworkError(source->GetResponseCode()); | |
165 } else { | |
166 *should_retry_request = true; | |
167 } | |
168 } else if (refresh_token.empty()) { | |
169 // If we only have an access token, then this was a refresh request. | |
170 delegate_->OnRefreshTokenResponse(access_token, expires_in_seconds); | |
171 } else { | |
172 delegate_->OnGetTokensResponse(refresh_token, | |
173 access_token, | |
174 expires_in_seconds); | |
175 } | |
176 } | |
177 | |
178 GaiaOAuthClient::GaiaOAuthClient(const std::string& gaia_url, | |
179 net::URLRequestContextGetter* context_getter) { | |
180 core_ = new Core(gaia_url, context_getter); | |
181 } | |
182 | |
183 GaiaOAuthClient::~GaiaOAuthClient() { | |
184 } | |
185 | |
186 | |
187 void GaiaOAuthClient::GetTokensFromAuthCode( | |
188 const OAuthClientInfo& oauth_client_info, | |
189 const std::string& auth_code, | |
190 int max_retries, | |
191 Delegate* delegate) { | |
192 return core_->GetTokensFromAuthCode(oauth_client_info, | |
193 auth_code, | |
194 max_retries, | |
195 delegate); | |
196 } | |
197 | |
198 void GaiaOAuthClient::RefreshToken(const OAuthClientInfo& oauth_client_info, | |
199 const std::string& refresh_token, | |
200 int max_retries, | |
201 Delegate* delegate) { | |
202 return core_->RefreshToken(oauth_client_info, | |
203 refresh_token, | |
204 max_retries, | |
205 delegate); | |
206 } | |
207 | |
208 } // namespace gaia | |
OLD | NEW |