Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(375)

Side by Side Diff: chrome/common/net/gaia/gaia_authenticator.cc

Issue 10928017: Moving google_apis and GaiaClient to src/google_apis. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Merge to head Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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_authenticator.h"
6
7 #include <string>
8 #include <utility>
9 #include <vector>
10
11 #include "base/basictypes.h"
12 #include "base/port.h"
13 #include "base/string_split.h"
14 #include "googleurl/src/gurl.h"
15 #include "net/base/escape.h"
16 #include "net/http/http_status_code.h"
17
18 using std::pair;
19 using std::string;
20 using std::vector;
21
22 namespace gaia {
23
24 static const char kGaiaV1IssueAuthTokenPath[] = "/accounts/IssueAuthToken";
25
26 static const char kGetUserInfoPath[] = "/accounts/GetUserInfo";
27
28 GaiaAuthenticator::AuthResults::AuthResults() : auth_error(None) {}
29
30 GaiaAuthenticator::AuthResults::AuthResults(const AuthResults& other)
31 : email(other.email),
32 password(other.password),
33 sid(other.sid),
34 lsid(other.lsid),
35 auth_token(other.auth_token),
36 primary_email(other.primary_email),
37 error_msg(other.error_msg),
38 auth_error(other.auth_error),
39 auth_error_url(other.auth_error_url),
40 captcha_token(other.captcha_token),
41 captcha_url(other.captcha_url) {
42 }
43
44 GaiaAuthenticator::AuthResults::~AuthResults() {}
45
46 GaiaAuthenticator::AuthParams::AuthParams() : authenticator(NULL),
47 request_id(0) {}
48
49 GaiaAuthenticator::AuthParams::~AuthParams() {}
50
51 // Sole constructor with initializers for all fields.
52 GaiaAuthenticator::GaiaAuthenticator(const string& user_agent,
53 const string& service_id,
54 const string& gaia_url)
55 : user_agent_(user_agent),
56 service_id_(service_id),
57 gaia_url_(gaia_url),
58 request_count_(0),
59 delay_(0),
60 next_allowed_auth_attempt_time_(0),
61 early_auth_attempt_count_(0),
62 message_loop_(NULL) {
63 }
64
65 GaiaAuthenticator::~GaiaAuthenticator() {
66 }
67
68 // mutex_ must be entered before calling this function.
69 GaiaAuthenticator::AuthParams GaiaAuthenticator::MakeParams(
70 const string& user_name,
71 const string& password,
72 const string& captcha_token,
73 const string& captcha_value) {
74 AuthParams params;
75 params.request_id = ++request_count_;
76 params.email = user_name;
77 params.password = password;
78 params.captcha_token = captcha_token;
79 params.captcha_value = captcha_value;
80 params.authenticator = this;
81 return params;
82 }
83
84 bool GaiaAuthenticator::Authenticate(const string& user_name,
85 const string& password,
86 const string& captcha_token,
87 const string& captcha_value) {
88 DCHECK_EQ(MessageLoop::current(), message_loop_);
89
90 AuthParams const params =
91 MakeParams(user_name, password, captcha_token, captcha_value);
92 return AuthenticateImpl(params);
93 }
94
95 bool GaiaAuthenticator::AuthenticateWithLsid(const string& lsid) {
96 auth_results_.lsid = lsid;
97 // We need to lookup the email associated with this LSID cookie in order to
98 // update |auth_results_| with the correct values.
99 if (LookupEmail(&auth_results_)) {
100 auth_results_.email = auth_results_.primary_email;
101 return IssueAuthToken(&auth_results_, service_id_);
102 }
103 return false;
104 }
105
106 bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params) {
107 DCHECK_EQ(MessageLoop::current(), message_loop_);
108 AuthResults results;
109 const bool succeeded = AuthenticateImpl(params, &results);
110 return succeeded;
111 }
112
113 // This method makes an HTTP request to the Gaia server, and calls other
114 // methods to help parse the response. If authentication succeeded, then
115 // Gaia-issued cookies are available in the respective variables; if
116 // authentication failed, then the exact error is available as an enum. If the
117 // client wishes to save the credentials, the last parameter must be true.
118 // If a subsequent request is made with fresh credentials, the saved credentials
119 // are wiped out; any subsequent request to the zero-parameter overload of this
120 // method preserves the saved credentials.
121 bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params,
122 AuthResults* results) {
123 DCHECK_EQ(MessageLoop::current(), message_loop_);
124 results->auth_error = ConnectionUnavailable;
125 results->email = params.email.data();
126 results->password = params.password;
127
128 // The aim of this code is to start failing requests if due to a logic error
129 // in the program we're hammering GAIA.
130 #if defined(OS_WIN)
131 __time32_t now = _time32(0);
132 #else // defined(OS_WIN)
133 time_t now = time(0);
134 #endif // defined(OS_WIN)
135
136 if (now > next_allowed_auth_attempt_time_) {
137 next_allowed_auth_attempt_time_ = now + 1;
138 // If we're more than 2 minutes past the allowed time we reset the early
139 // attempt count.
140 if (now - next_allowed_auth_attempt_time_ > 2 * 60) {
141 delay_ = 1;
142 early_auth_attempt_count_ = 0;
143 }
144 } else {
145 ++early_auth_attempt_count_;
146 // Allow 3 attempts, but then limit.
147 if (early_auth_attempt_count_ > 3) {
148 delay_ = GetBackoffDelaySeconds(delay_);
149 next_allowed_auth_attempt_time_ = now + delay_;
150 return false;
151 }
152 }
153
154 return PerformGaiaRequest(params, results);
155 }
156
157 bool GaiaAuthenticator::PerformGaiaRequest(const AuthParams& params,
158 AuthResults* results) {
159 DCHECK_EQ(MessageLoop::current(), message_loop_);
160 GURL gaia_auth_url(gaia_url_);
161
162 string post_body;
163 post_body += "Email=" + net::EscapeUrlEncodedData(params.email, true);
164 post_body += "&Passwd=" + net::EscapeUrlEncodedData(params.password, true);
165 post_body += "&source=" + net::EscapeUrlEncodedData(user_agent_, true);
166 post_body += "&service=" + service_id_;
167 if (!params.captcha_token.empty() && !params.captcha_value.empty()) {
168 post_body += "&logintoken=" +
169 net::EscapeUrlEncodedData(params.captcha_token, true);
170 post_body += "&logincaptcha=" +
171 net::EscapeUrlEncodedData(params.captcha_value, true);
172 }
173 post_body += "&PersistentCookie=true";
174 // We set it to GOOGLE (and not HOSTED or HOSTED_OR_GOOGLE) because we only
175 // allow consumer logins.
176 post_body += "&accountType=GOOGLE";
177
178 string message_text;
179 unsigned long server_response_code;
180 if (!Post(gaia_auth_url, post_body, &server_response_code, &message_text)) {
181 results->auth_error = ConnectionUnavailable;
182 return false;
183 }
184
185 // Parse reply in two different ways, depending on if request failed or
186 // succeeded.
187 if (net::HTTP_FORBIDDEN == server_response_code) {
188 ExtractAuthErrorFrom(message_text, results);
189 return false;
190 } else if (net::HTTP_OK == server_response_code) {
191 ExtractTokensFrom(message_text, results);
192 if (!IssueAuthToken(results, service_id_)) {
193 return false;
194 }
195
196 return LookupEmail(results);
197 } else {
198 results->auth_error = Unknown;
199 return false;
200 }
201 }
202
203 bool GaiaAuthenticator::Post(const GURL& url,
204 const std::string& post_body,
205 unsigned long* response_code,
206 std::string* response_body) {
207 return false;
208 }
209
210 bool GaiaAuthenticator::LookupEmail(AuthResults* results) {
211 DCHECK_EQ(MessageLoop::current(), message_loop_);
212 // Use the provided Gaia server, but change the path to what V1 expects.
213 GURL url(gaia_url_); // Gaia server.
214 GURL::Replacements repl;
215 // Needs to stay in scope till GURL is out of scope.
216 string path(kGetUserInfoPath);
217 repl.SetPathStr(path);
218 url = url.ReplaceComponents(repl);
219
220 string post_body;
221 post_body += "LSID=";
222 post_body += net::EscapeUrlEncodedData(results->lsid, true);
223
224 unsigned long server_response_code;
225 string message_text;
226 if (!Post(url, post_body, &server_response_code, &message_text)) {
227 return false;
228 }
229
230 // Check if we received a valid AuthToken; if not, ignore it.
231 if (net::HTTP_FORBIDDEN == server_response_code) {
232 // Server says we're not authenticated.
233 ExtractAuthErrorFrom(message_text, results);
234 return false;
235 } else if (net::HTTP_OK == server_response_code) {
236 typedef vector<pair<string, string> > Tokens;
237 Tokens tokens;
238 base::SplitStringIntoKeyValuePairs(message_text, '=', '\n', &tokens);
239 for (Tokens::iterator i = tokens.begin(); i != tokens.end(); ++i) {
240 if ("accountType" == i->first) {
241 // We never authenticate an email as a hosted account.
242 DCHECK_EQ("GOOGLE", i->second);
243 } else if ("email" == i->first) {
244 results->primary_email = i->second;
245 }
246 }
247 return true;
248 }
249 return false;
250 }
251
252 int GaiaAuthenticator::GetBackoffDelaySeconds(int current_backoff_delay) {
253 NOTREACHED();
254 return current_backoff_delay;
255 }
256
257 // We need to call this explicitly when we need to obtain a long-lived session
258 // token.
259 bool GaiaAuthenticator::IssueAuthToken(AuthResults* results,
260 const string& service_id) {
261 DCHECK_EQ(MessageLoop::current(), message_loop_);
262 // Use the provided Gaia server, but change the path to what V1 expects.
263 GURL url(gaia_url_); // Gaia server.
264 GURL::Replacements repl;
265 // Needs to stay in scope till GURL is out of scope.
266 string path(kGaiaV1IssueAuthTokenPath);
267 repl.SetPathStr(path);
268 url = url.ReplaceComponents(repl);
269
270 string post_body;
271 post_body += "LSID=";
272 post_body += net::EscapeUrlEncodedData(results->lsid, true);
273 post_body += "&service=" + service_id;
274 post_body += "&Session=true";
275
276 unsigned long server_response_code;
277 string message_text;
278 if (!Post(url, post_body, &server_response_code, &message_text)) {
279 return false;
280 }
281
282 // Check if we received a valid AuthToken; if not, ignore it.
283 if (net::HTTP_FORBIDDEN == server_response_code) {
284 // Server says we're not authenticated.
285 ExtractAuthErrorFrom(message_text, results);
286 return false;
287 } else if (net::HTTP_OK == server_response_code) {
288 // Note that the format of message_text is different from what is returned
289 // in the first request, or to the sole request that is made to Gaia V2.
290 // Specifically, the entire string is the AuthToken, and looks like:
291 // "<token>" rather than "AuthToken=<token>". Thus, we need not use
292 // ExtractTokensFrom(...), but simply assign the token.
293 int last_index = message_text.length() - 1;
294 if ('\n' == message_text[last_index])
295 message_text.erase(last_index);
296 results->auth_token = message_text;
297 return true;
298 }
299 return false;
300 }
301
302 // Helper method that extracts tokens from a successful reply, and saves them
303 // in the right fields.
304 void GaiaAuthenticator::ExtractTokensFrom(const string& response,
305 AuthResults* results) {
306 vector<pair<string, string> > tokens;
307 base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens);
308 for (vector<pair<string, string> >::iterator i = tokens.begin();
309 i != tokens.end(); ++i) {
310 if (i->first == "SID") {
311 results->sid = i->second;
312 } else if (i->first == "LSID") {
313 results->lsid = i->second;
314 } else if (i->first == "Auth") {
315 results->auth_token = i->second;
316 }
317 }
318 }
319
320 // Helper method that extracts tokens from a failure response, and saves them
321 // in the right fields.
322 void GaiaAuthenticator::ExtractAuthErrorFrom(const string& response,
323 AuthResults* results) {
324 vector<pair<string, string> > tokens;
325 base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens);
326 for (vector<pair<string, string> >::iterator i = tokens.begin();
327 i != tokens.end(); ++i) {
328 if (i->first == "Error") {
329 results->error_msg = i->second;
330 } else if (i->first == "Url") {
331 results->auth_error_url = i->second;
332 } else if (i->first == "CaptchaToken") {
333 results->captcha_token = i->second;
334 } else if (i->first == "CaptchaUrl") {
335 results->captcha_url = i->second;
336 }
337 }
338
339 // Convert string error messages to enum values. Each case has two different
340 // strings; the first one is the most current and the second one is
341 // deprecated, but available.
342 const string& error_msg = results->error_msg;
343 if (error_msg == "BadAuthentication" || error_msg == "badauth") {
344 results->auth_error = BadAuthentication;
345 } else if (error_msg == "NotVerified" || error_msg == "nv") {
346 results->auth_error = NotVerified;
347 } else if (error_msg == "TermsNotAgreed" || error_msg == "tna") {
348 results->auth_error = TermsNotAgreed;
349 } else if (error_msg == "Unknown" || error_msg == "unknown") {
350 results->auth_error = Unknown;
351 } else if (error_msg == "AccountDeleted" || error_msg == "adel") {
352 results->auth_error = AccountDeleted;
353 } else if (error_msg == "AccountDisabled" || error_msg == "adis") {
354 results->auth_error = AccountDisabled;
355 } else if (error_msg == "CaptchaRequired" || error_msg == "cr") {
356 results->auth_error = CaptchaRequired;
357 } else if (error_msg == "ServiceUnavailable" || error_msg == "ire") {
358 results->auth_error = ServiceUnavailable;
359 }
360 }
361
362 // Reset all stored credentials, perhaps in preparation for letting a different
363 // user sign in.
364 void GaiaAuthenticator::ResetCredentials() {
365 DCHECK_EQ(MessageLoop::current(), message_loop_);
366 AuthResults blank;
367 auth_results_ = blank;
368 }
369
370 void GaiaAuthenticator::SetUsernamePassword(const string& username,
371 const string& password) {
372 DCHECK_EQ(MessageLoop::current(), message_loop_);
373 auth_results_.password = password;
374 auth_results_.email = username;
375 }
376
377 void GaiaAuthenticator::SetUsername(const string& username) {
378 DCHECK_EQ(MessageLoop::current(), message_loop_);
379 auth_results_.email = username;
380 }
381
382 void GaiaAuthenticator::RenewAuthToken(const string& auth_token) {
383 DCHECK_EQ(MessageLoop::current(), message_loop_);
384 DCHECK(!this->auth_token().empty());
385 auth_results_.auth_token = auth_token;
386 }
387 void GaiaAuthenticator::SetAuthToken(const string& auth_token) {
388 DCHECK_EQ(MessageLoop::current(), message_loop_);
389 auth_results_.auth_token = auth_token;
390 }
391
392 bool GaiaAuthenticator::Authenticate(const string& user_name,
393 const string& password) {
394 DCHECK_EQ(MessageLoop::current(), message_loop_);
395 const string empty;
396 return Authenticate(user_name, password, empty,
397 empty);
398 }
399
400 } // namespace gaia
OLDNEW
« no previous file with comments | « chrome/common/net/gaia/gaia_authenticator.h ('k') | chrome/common/net/gaia/gaia_authenticator_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698