summaryrefslogtreecommitdiffstats
path: root/chrome/common
diff options
context:
space:
mode:
authorsanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 02:53:55 +0000
committersanjeevr@chromium.org <sanjeevr@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-05-07 02:53:55 +0000
commitf3d0174b68971b96dd2e8f518d81abee1fc87645 (patch)
treedc8b5e279042af86343784c90c2b85553a100ab9 /chrome/common
parent92e83e01a65d77e69978360250979ddea41987ca (diff)
downloadchromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.zip
chromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.tar.gz
chromium_src-f3d0174b68971b96dd2e8f518d81abee1fc87645.tar.bz2
Moved GaiaAuthenticator from chrome/browser/sync/engine/net to chrome/common/net/gaia.
BUG=None TEST=None, no functional change. Review URL: http://codereview.chromium.org/1998004 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@46654 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/common')
-rw-r--r--chrome/common/net/gaia/gaia_authenticator.cc404
-rw-r--r--chrome/common/net/gaia/gaia_authenticator.h331
-rw-r--r--chrome/common/net/gaia/gaia_authenticator_unittest.cc49
-rw-r--r--chrome/common/net/gaia/signin.h18
-rw-r--r--chrome/common/net/http_return.h17
5 files changed, 819 insertions, 0 deletions
diff --git a/chrome/common/net/gaia/gaia_authenticator.cc b/chrome/common/net/gaia/gaia_authenticator.cc
new file mode 100644
index 0000000..38fc19b
--- /dev/null
+++ b/chrome/common/net/gaia/gaia_authenticator.cc
@@ -0,0 +1,404 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/net/gaia/gaia_authenticator.h"
+
+#include <string>
+#include <utility>
+#include <vector>
+
+#include "base/basictypes.h"
+#include "base/port.h"
+#include "base/string_split.h"
+#include "chrome/common/deprecated/event_sys-inl.h"
+#include "chrome/common/net/http_return.h"
+#include "googleurl/src/gurl.h"
+#include "net/base/escape.h"
+
+using std::pair;
+using std::string;
+using std::vector;
+
+namespace gaia {
+
+static const char kGaiaV1IssueAuthTokenPath[] = "/accounts/IssueAuthToken";
+
+static const char kGetUserInfoPath[] = "/accounts/GetUserInfo";
+
+// Sole constructor with initializers for all fields.
+GaiaAuthenticator::GaiaAuthenticator(const string& user_agent,
+ const string& service_id,
+ const string& gaia_url)
+ : user_agent_(user_agent),
+ service_id_(service_id),
+ gaia_url_(gaia_url),
+ request_count_(0),
+ delay_(0),
+ next_allowed_auth_attempt_time_(0),
+ early_auth_attempt_count_(0),
+ message_loop_(NULL) {
+ GaiaAuthEvent done = { GaiaAuthEvent::GAIA_AUTHENTICATOR_DESTROYED, None,
+ this };
+ channel_ = new Channel(done);
+}
+
+GaiaAuthenticator::~GaiaAuthenticator() {
+ delete channel_;
+}
+
+// mutex_ must be entered before calling this function.
+GaiaAuthenticator::AuthParams GaiaAuthenticator::MakeParams(
+ const string& user_name,
+ const string& password,
+ SaveCredentials should_save_credentials,
+ const string& captcha_token,
+ const string& captcha_value,
+ SignIn try_first) {
+ AuthParams params;
+ params.request_id = ++request_count_;
+ params.email = user_name;
+ params.password = password;
+ params.should_save_credentials = should_save_credentials;
+ params.captcha_token = captcha_token;
+ params.captcha_value = captcha_value;
+ params.authenticator = this;
+ params.try_first = try_first;
+ return params;
+}
+
+bool GaiaAuthenticator::Authenticate(const string& user_name,
+ const string& password,
+ SaveCredentials should_save_credentials,
+ const string& captcha_token,
+ const string& captcha_value,
+ SignIn try_first) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+
+ AuthParams const params =
+ MakeParams(user_name, password, should_save_credentials, captcha_token,
+ captcha_value, try_first);
+ return AuthenticateImpl(params);
+}
+
+bool GaiaAuthenticator::AuthenticateWithLsid(const string& lsid,
+ bool long_lived) {
+ auth_results_.lsid = lsid;
+ // We need to lookup the email associated with this LSID cookie in order to
+ // update |auth_results_| with the correct values.
+ if (LookupEmail(&auth_results_)) {
+ auth_results_.email = auth_results_.primary_email;
+ return IssueAuthToken(&auth_results_, service_id_, long_lived);
+ }
+ return false;
+}
+
+
+bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ AuthResults results;
+ const bool succeeded = AuthenticateImpl(params, &results);
+ if (params.request_id == request_count_) {
+ auth_results_ = results;
+ GaiaAuthEvent event = { succeeded ? GaiaAuthEvent::GAIA_AUTH_SUCCEEDED
+ : GaiaAuthEvent::GAIA_AUTH_FAILED,
+ results.auth_error, this };
+ channel_->NotifyListeners(event);
+ }
+ return succeeded;
+}
+
+// This method makes an HTTP request to the Gaia server, and calls other
+// methods to help parse the response. If authentication succeeded, then
+// Gaia-issued cookies are available in the respective variables; if
+// authentication failed, then the exact error is available as an enum. If the
+// client wishes to save the credentials, the last parameter must be true.
+// If a subsequent request is made with fresh credentials, the saved credentials
+// are wiped out; any subsequent request to the zero-parameter overload of this
+// method preserves the saved credentials.
+bool GaiaAuthenticator::AuthenticateImpl(const AuthParams& params,
+ AuthResults* results) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ results->credentials_saved = params.should_save_credentials;
+ results->auth_error = ConnectionUnavailable;
+ // Save credentials if so requested.
+ if (params.should_save_credentials != DONT_SAVE_CREDENTIALS) {
+ results->email = params.email.data();
+ results->password = params.password;
+ } else { // Explicitly clear previously-saved credentials.
+ results->email = "";
+ results->password = "";
+ }
+
+ // The aim of this code is to start failing requests if due to a logic error
+ // in the program we're hammering GAIA.
+#if defined(OS_WIN)
+ __time32_t now = _time32(0);
+#else // defined(OS_WIN)
+ time_t now = time(0);
+#endif // defined(OS_WIN)
+
+ if (now > next_allowed_auth_attempt_time_) {
+ next_allowed_auth_attempt_time_ = now + 1;
+ // If we're more than 2 minutes past the allowed time we reset the early
+ // attempt count.
+ if (now - next_allowed_auth_attempt_time_ > 2 * 60) {
+ delay_ = 1;
+ early_auth_attempt_count_ = 0;
+ }
+ } else {
+ ++early_auth_attempt_count_;
+ // Allow 3 attempts, but then limit.
+ if (early_auth_attempt_count_ > 3) {
+ delay_ = GetBackoffDelaySeconds(delay_);
+ next_allowed_auth_attempt_time_ = now + delay_;
+ return false;
+ }
+ }
+
+ return PerformGaiaRequest(params, results);
+}
+
+bool GaiaAuthenticator::PerformGaiaRequest(const AuthParams& params,
+ AuthResults* results) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ GURL gaia_auth_url(gaia_url_);
+
+ string post_body;
+ post_body += "Email=" + EscapeUrlEncodedData(params.email);
+ post_body += "&Passwd=" + EscapeUrlEncodedData(params.password);
+ post_body += "&source=" + EscapeUrlEncodedData(user_agent_);
+ post_body += "&service=" + service_id_;
+ if (!params.captcha_token.empty() && !params.captcha_value.empty()) {
+ post_body += "&logintoken=" + EscapeUrlEncodedData(params.captcha_token);
+ post_body += "&logincaptcha=" + EscapeUrlEncodedData(params.captcha_value);
+ }
+ post_body += "&PersistentCookie=true";
+ // We set it to GOOGLE (and not HOSTED or HOSTED_OR_GOOGLE) because we only
+ // allow consumer logins.
+ post_body += "&accountType=GOOGLE";
+
+ string message_text;
+ unsigned long server_response_code;
+ if (!Post(gaia_auth_url, post_body, &server_response_code, &message_text)) {
+ results->auth_error = ConnectionUnavailable;
+ return false;
+ }
+
+ // Parse reply in two different ways, depending on if request failed or
+ // succeeded.
+ if (RC_FORBIDDEN == server_response_code) {
+ ExtractAuthErrorFrom(message_text, results);
+ return false;
+ } else if (RC_REQUEST_OK == server_response_code) {
+ ExtractTokensFrom(message_text, results);
+ const bool old_gaia =
+ results->auth_token.empty() && !results->lsid.empty();
+ const bool long_lived_token =
+ params.should_save_credentials == PERSIST_TO_DISK;
+ if ((old_gaia || long_lived_token) &&
+ !IssueAuthToken(results, service_id_, long_lived_token))
+ return false;
+
+ return LookupEmail(results);
+ } else {
+ results->auth_error = Unknown;
+ return false;
+ }
+}
+
+bool GaiaAuthenticator::LookupEmail(AuthResults* results) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ // Use the provided Gaia server, but change the path to what V1 expects.
+ GURL url(gaia_url_); // Gaia server.
+ GURL::Replacements repl;
+ // Needs to stay in scope till GURL is out of scope.
+ string path(kGetUserInfoPath);
+ repl.SetPathStr(path);
+ url = url.ReplaceComponents(repl);
+
+ string post_body;
+ post_body += "LSID=";
+ post_body += EscapeUrlEncodedData(results->lsid);
+
+ unsigned long server_response_code;
+ string message_text;
+ if (!Post(url, post_body, &server_response_code, &message_text)) {
+ return false;
+ }
+
+ // Check if we received a valid AuthToken; if not, ignore it.
+ if (RC_FORBIDDEN == server_response_code) {
+ // Server says we're not authenticated.
+ ExtractAuthErrorFrom(message_text, results);
+ return false;
+ } else if (RC_REQUEST_OK == server_response_code) {
+ typedef vector<pair<string, string> > Tokens;
+ Tokens tokens;
+ base::SplitStringIntoKeyValuePairs(message_text, '=', '\n', &tokens);
+ for (Tokens::iterator i = tokens.begin(); i != tokens.end(); ++i) {
+ if ("accountType" == i->first) {
+ // We never authenticate an email as a hosted account.
+ DCHECK_EQ("GOOGLE", i->second);
+ results->signin = GMAIL_SIGNIN;
+ } else if ("email" == i->first) {
+ results->primary_email = i->second;
+ }
+ }
+ return true;
+ }
+ return false;
+}
+
+// We need to call this explicitly when we need to obtain a long-lived session
+// token.
+bool GaiaAuthenticator::IssueAuthToken(AuthResults* results,
+ const string& service_id,
+ bool long_lived) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ // Use the provided Gaia server, but change the path to what V1 expects.
+ GURL url(gaia_url_); // Gaia server.
+ GURL::Replacements repl;
+ // Needs to stay in scope till GURL is out of scope.
+ string path(kGaiaV1IssueAuthTokenPath);
+ repl.SetPathStr(path);
+ url = url.ReplaceComponents(repl);
+
+ string post_body;
+ post_body += "LSID=";
+ post_body += EscapeUrlEncodedData(results->lsid);
+ post_body += "&service=" + service_id;
+ if (long_lived) {
+ post_body += "&Session=true";
+ }
+
+ unsigned long server_response_code;
+ string message_text;
+ if (!Post(url, post_body, &server_response_code, &message_text)) {
+ return false;
+ }
+
+ // Check if we received a valid AuthToken; if not, ignore it.
+ if (RC_FORBIDDEN == server_response_code) {
+ // Server says we're not authenticated.
+ ExtractAuthErrorFrom(message_text, results);
+ return false;
+ } else if (RC_REQUEST_OK == server_response_code) {
+ // Note that the format of message_text is different from what is returned
+ // in the first request, or to the sole request that is made to Gaia V2.
+ // Specifically, the entire string is the AuthToken, and looks like:
+ // "<token>" rather than "AuthToken=<token>". Thus, we need not use
+ // ExtractTokensFrom(...), but simply assign the token.
+ int last_index = message_text.length() - 1;
+ if ('\n' == message_text[last_index])
+ message_text.erase(last_index);
+ results->auth_token = message_text;
+ return true;
+ }
+ return false;
+}
+
+// Helper method that extracts tokens from a successful reply, and saves them
+// in the right fields.
+void GaiaAuthenticator::ExtractTokensFrom(const string& response,
+ AuthResults* results) {
+ vector<pair<string, string> > tokens;
+ base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens);
+ for (vector<pair<string, string> >::iterator i = tokens.begin();
+ i != tokens.end(); ++i) {
+ if (i->first == "SID") {
+ results->sid = i->second;
+ } else if (i->first == "LSID") {
+ results->lsid = i->second;
+ } else if (i->first == "Auth") {
+ results->auth_token = i->second;
+ }
+ }
+}
+
+// Helper method that extracts tokens from a failure response, and saves them
+// in the right fields.
+void GaiaAuthenticator::ExtractAuthErrorFrom(const string& response,
+ AuthResults* results) {
+ vector<pair<string, string> > tokens;
+ base::SplitStringIntoKeyValuePairs(response, '=', '\n', &tokens);
+ for (vector<pair<string, string> >::iterator i = tokens.begin();
+ i != tokens.end(); ++i) {
+ if (i->first == "Error") {
+ results->error_msg = i->second;
+ } else if (i->first == "Url") {
+ results->auth_error_url = i->second;
+ } else if (i->first == "CaptchaToken") {
+ results->captcha_token = i->second;
+ } else if (i->first == "CaptchaUrl") {
+ results->captcha_url = i->second;
+ }
+ }
+
+ // Convert string error messages to enum values. Each case has two different
+ // strings; the first one is the most current and the second one is
+ // deprecated, but available.
+ const string& error_msg = results->error_msg;
+ if (error_msg == "BadAuthentication" || error_msg == "badauth") {
+ results->auth_error = BadAuthentication;
+ } else if (error_msg == "NotVerified" || error_msg == "nv") {
+ results->auth_error = NotVerified;
+ } else if (error_msg == "TermsNotAgreed" || error_msg == "tna") {
+ results->auth_error = TermsNotAgreed;
+ } else if (error_msg == "Unknown" || error_msg == "unknown") {
+ results->auth_error = Unknown;
+ } else if (error_msg == "AccountDeleted" || error_msg == "adel") {
+ results->auth_error = AccountDeleted;
+ } else if (error_msg == "AccountDisabled" || error_msg == "adis") {
+ results->auth_error = AccountDisabled;
+ } else if (error_msg == "CaptchaRequired" || error_msg == "cr") {
+ results->auth_error = CaptchaRequired;
+ } else if (error_msg == "ServiceUnavailable" || error_msg == "ire") {
+ results->auth_error = ServiceUnavailable;
+ }
+}
+
+// Reset all stored credentials, perhaps in preparation for letting a different
+// user sign in.
+void GaiaAuthenticator::ResetCredentials() {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ AuthResults blank;
+ auth_results_ = blank;
+}
+
+void GaiaAuthenticator::SetUsernamePassword(const string& username,
+ const string& password) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ auth_results_.password = password;
+ auth_results_.email = username;
+}
+
+void GaiaAuthenticator::SetUsername(const string& username) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ auth_results_.email = username;
+}
+
+void GaiaAuthenticator::RenewAuthToken(const string& auth_token) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ DCHECK(!this->auth_token().empty());
+ auth_results_.auth_token = auth_token;
+}
+void GaiaAuthenticator::SetAuthToken(const string& auth_token,
+ SaveCredentials save) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ auth_results_.auth_token = auth_token;
+ auth_results_.credentials_saved = save;
+}
+
+bool GaiaAuthenticator::Authenticate(const string& user_name,
+ const string& password,
+ SaveCredentials should_save_credentials,
+ SignIn try_first) {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ const string empty;
+ return Authenticate(user_name, password, should_save_credentials, empty,
+ empty, try_first);
+}
+
+} // namepace gaia
+
diff --git a/chrome/common/net/gaia/gaia_authenticator.h b/chrome/common/net/gaia/gaia_authenticator.h
new file mode 100644
index 0000000..b165cf6
--- /dev/null
+++ b/chrome/common/net/gaia/gaia_authenticator.h
@@ -0,0 +1,331 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+//
+// Use this class to authenticate users with Gaia and access cookies sent
+// by the Gaia servers. This class cannot be used on its own becaue it relies
+// on a subclass to provide the virtual Post and GetBackoffDelaySeconds methods.
+//
+// Sample usage:
+// class ActualGaiaAuthenticator : public gaia::GaiaAuthenticator {
+// Provides actual implementation of Post and GetBackoffDelaySeconds.
+// };
+// ActualGaiaAuthenticator gaia_auth("User-Agent", SERVICE_NAME, kGaiaUrl);
+// if (gaia_auth.Authenticate("email", "passwd", SAVE_IN_MEMORY_ONLY,
+// true)) { // Synchronous
+// // Do something with: gaia_auth.auth_token(), or gaia_auth.sid(),
+// // or gaia_auth.lsid()
+// }
+//
+// Credentials can also be preserved for subsequent requests, though these are
+// saved in plain-text in memory, and not very secure on client systems. The
+// email address associated with the Gaia account can be read; the password is
+// write-only.
+
+// TODO(sanjeevr): This class has been moved here from the bookmarks sync code.
+// While it is a generic class that handles GAIA authentication, there are some
+// artifacts of the sync code such as the SaveCredentials enum which needs to
+// be cleaned up.
+#ifndef CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_
+#define CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/message_loop.h"
+#include "chrome/common/net/gaia/signin.h"
+#include "chrome/common/deprecated/event_sys.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest_prod.h" // For FRIEND_TEST
+
+namespace gaia {
+
+static const char kGaiaUrl[] =
+ "https://www.google.com:443/accounts/ClientLogin";
+
+// Use of the following enum is odd. GaiaAuthenticator only looks at
+// and DONT_SAVE_CREDENTIALS and SAVE_IN_MEMORY_ONLY (PERSIST_TO_DISK is == to
+// SAVE_IN_MEMORY_ONLY for GaiaAuthenticator).
+
+enum SaveCredentials {
+ DONT_SAVE_CREDENTIALS,
+ SAVE_IN_MEMORY_ONLY,
+ PERSIST_TO_DISK // Saved in both memory and disk
+};
+
+// Error codes from Gaia. These will be set correctly for both Gaia V1
+// (/ClientAuth) and V2 (/ClientLogin)
+enum AuthenticationError {
+ None = 0,
+ BadAuthentication = 1,
+ NotVerified = 2,
+ TermsNotAgreed = 3,
+ Unknown = 4,
+ AccountDeleted = 5,
+ AccountDisabled = 6,
+ CaptchaRequired = 7,
+ ServiceUnavailable = 8,
+ // Errors generated by this class not Gaia.
+ CredentialsNotSet = 9,
+ ConnectionUnavailable = 10
+};
+
+class GaiaAuthenticator;
+
+struct GaiaAuthEvent {
+ enum {
+ GAIA_AUTH_FAILED,
+ GAIA_AUTH_SUCCEEDED,
+ GAIA_AUTHENTICATOR_DESTROYED
+ }
+ what_happened;
+ AuthenticationError error;
+ const GaiaAuthenticator* authenticator;
+
+ // Lets us use GaiaAuthEvent as its own traits type in hookups.
+ typedef GaiaAuthEvent EventType;
+ static inline bool IsChannelShutdownEvent(const GaiaAuthEvent& event) {
+ return event.what_happened == GAIA_AUTHENTICATOR_DESTROYED;
+ }
+};
+
+// GaiaAuthenticator can be used to pass user credentials to Gaia and obtain
+// cookies set by the Gaia servers.
+class GaiaAuthenticator {
+ FRIEND_TEST(GaiaAuthenticatorTest, TestNewlineAtEndOfAuthTokenRemoved);
+ public:
+
+ // Since GaiaAuthenticator can be used for any service, or by any client, you
+ // must include a user-agent and a service-id when creating one. The
+ // user_agent is a short string used for simple log analysis. gaia_url is used
+ // to choose the server to authenticate with (e.g.
+ // http://www.google.com/accounts/ClientLogin).
+ GaiaAuthenticator(const std::string& user_agent,
+ const std::string& service_id,
+ const std::string& gaia_url);
+
+ virtual ~GaiaAuthenticator();
+
+ // This object should only be invoked from the AuthWatcherThread message
+ // loop, which is injected here.
+ void set_message_loop(const MessageLoop* loop) {
+ message_loop_ = loop;
+ }
+
+ // Pass credentials to authenticate with, or use saved credentials via an
+ // overload. If authentication succeeds, you can retrieve the authentication
+ // token via the respective accessors. Returns a boolean indicating whether
+ // authentication succeeded or not.
+ bool Authenticate(const std::string& user_name, const std::string& password,
+ SaveCredentials should_save_credentials,
+ const std::string& captcha_token,
+ const std::string& captcha_value,
+ SignIn try_first);
+
+ bool Authenticate(const std::string& user_name, const std::string& password,
+ SaveCredentials should_save_credentials,
+ SignIn try_first);
+
+ // Pass the LSID to authenticate with. If the authentication succeeds, you can
+ // retrieve the authetication token via the respective accessors. Returns a
+ // boolean indicating whether authentication succeeded or not.
+ bool AuthenticateWithLsid(const std::string& lsid, bool long_lived_token);
+
+ // Resets all stored cookies to their default values.
+ void ResetCredentials();
+
+ void SetUsernamePassword(const std::string& username,
+ const std::string& password);
+
+ void SetUsername(const std::string& username);
+
+ // Virtual for testing
+ virtual void RenewAuthToken(const std::string& auth_token);
+ void SetAuthToken(const std::string& auth_token, SaveCredentials);
+
+ struct AuthResults {
+ SaveCredentials credentials_saved;
+ std::string email;
+ std::string password;
+
+ // Fields that store various cookies.
+ std::string sid;
+ std::string lsid;
+ std::string auth_token;
+
+ std::string primary_email;
+
+ // Fields for items returned when authentication fails.
+ std::string error_msg;
+ enum AuthenticationError auth_error;
+ std::string auth_error_url;
+ std::string captcha_token;
+ std::string captcha_url;
+ SignIn signin;
+
+ // TODO(skrul): When auth fails, the "signin" field of the results
+ // struct never gets set, which causes valgrind to complain. Give
+ // this field a value here so the error is suppressed. It turns
+ // out that the signin field has only one possible value, so the
+ // correct fix here would be to to remove it entirely.
+ AuthResults() : credentials_saved(DONT_SAVE_CREDENTIALS),
+ auth_error(None),
+ signin(GMAIL_SIGNIN) { }
+ };
+
+ protected:
+
+ struct AuthParams {
+ GaiaAuthenticator* authenticator;
+ uint32 request_id;
+ SaveCredentials should_save_credentials;
+ std::string email;
+ std::string password;
+ std::string captcha_token;
+ std::string captcha_value;
+ SignIn try_first;
+ };
+
+ // mutex_ must be entered before calling this function.
+ AuthParams MakeParams(const std::string& user_name,
+ const std::string& password,
+ SaveCredentials should_save_credentials,
+ const std::string& captcha_token,
+ const std::string& captcha_value,
+ SignIn try_first);
+
+ // The real Authenticate implementations.
+ bool AuthenticateImpl(const AuthParams& params);
+ bool AuthenticateImpl(const AuthParams& params, AuthResults* results);
+
+ // virtual for testing purposes.
+ virtual bool PerformGaiaRequest(const AuthParams& params,
+ AuthResults* results);
+ virtual bool Post(const GURL& url, const std::string& post_body,
+ unsigned long* response_code, std::string* response_body) {
+ return false;
+ }
+
+ // Caller should fill in results->LSID before calling. Result in
+ // results->primary_email.
+ virtual bool LookupEmail(AuthResults* results);
+
+ // Subclasses must override to provide a backoff delay. It is virtual instead
+ // of pure virtual for testing purposes.
+ // TODO(sanjeevr): This should be made pure virtual. But this class is
+ // currently directly being used in sync/engine/authenticator.cc, which is
+ // wrong.
+ virtual int GetBackoffDelaySeconds(int current_backoff_delay) {
+ NOTREACHED();
+ return current_backoff_delay;
+ }
+
+ public:
+ // Retrieve email.
+ inline std::string email() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.email;
+ }
+
+ // Retrieve password.
+ inline std::string password() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.password;
+ }
+
+ // Retrieve AuthToken, if previously authenticated; otherwise returns "".
+ inline std::string auth_token() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.auth_token;
+ }
+
+ // Retrieve SID cookie. For details, see the Google Accounts documentation.
+ inline std::string sid() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.sid;
+ }
+
+ // Retrieve LSID cookie. For details, see the Google Accounts documentation.
+ inline std::string lsid() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.lsid;
+ }
+
+ // Get last authentication error.
+ inline enum AuthenticationError auth_error() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.auth_error;
+ }
+
+ inline std::string auth_error_url() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.auth_error_url;
+ }
+
+ inline std::string captcha_token() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.captcha_token;
+ }
+
+ inline std::string captcha_url() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_.captcha_url;
+ }
+
+ inline AuthResults results() const {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return auth_results_;
+ }
+
+ typedef EventChannel<GaiaAuthEvent, Lock> Channel;
+
+ inline Channel* channel() const {
+ return channel_;
+ }
+
+ private:
+ bool IssueAuthToken(AuthResults* results, const std::string& service_id,
+ bool long_lived_token);
+
+ // Helper method to parse response when authentication succeeds.
+ void ExtractTokensFrom(const std::string& response, AuthResults* results);
+ // Helper method to parse response when authentication fails.
+ void ExtractAuthErrorFrom(const std::string& response, AuthResults* results);
+
+ // Fields for the obvious data items.
+ const std::string user_agent_;
+ const std::string service_id_;
+ const std::string gaia_url_;
+
+ AuthResults auth_results_;
+
+ // When multiple async requests are running, only the one that started most
+ // recently updates the values.
+ //
+ // Note that even though this code was written to handle multiple requests
+ // simultaneously, the sync code issues auth requests one at a time.
+ uint32 request_count_;
+
+ Channel* channel_;
+
+ // Used to compute backoff time for next allowed authentication.
+ int delay_; // In seconds.
+ // On Windows, time_t is 64-bit by default. Even though we have defined the
+ // _USE_32BIT_TIME_T preprocessor flag, other libraries including this header
+ // may not have that preprocessor flag defined resulting in mismatched class
+ // sizes. So we explicitly define it as 32-bit on Windows.
+ // TODO(sanjeevr): Change this to to use base::Time
+#if defined(OS_WIN)
+ __time32_t next_allowed_auth_attempt_time_;
+#else // defined(OS_WIN)
+ time_t next_allowed_auth_attempt_time_;
+#endif // defined(OS_WIN)
+ int early_auth_attempt_count_;
+
+ // The message loop all our methods are invoked on.
+ const MessageLoop* message_loop_;
+};
+
+} // namespace gaia
+#endif // CHROME_COMMON_NET_GAIA_GAIA_AUTHENTICATOR_H_
+
diff --git a/chrome/common/net/gaia/gaia_authenticator_unittest.cc b/chrome/common/net/gaia/gaia_authenticator_unittest.cc
new file mode 100644
index 0000000..1f9813d
--- /dev/null
+++ b/chrome/common/net/gaia/gaia_authenticator_unittest.cc
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "chrome/common/net/gaia/gaia_authenticator.h"
+
+#include <string>
+
+#include "chrome/common/net/http_return.h"
+#include "googleurl/src/gurl.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using std::string;
+
+namespace gaia {
+
+class GaiaAuthenticatorTest : public testing::Test { };
+
+class GaiaAuthMockForGaiaAuthenticator : public GaiaAuthenticator {
+ public:
+ GaiaAuthMockForGaiaAuthenticator()
+ : GaiaAuthenticator("useragent", "serviceid", "http://gaia_url") {}
+ ~GaiaAuthMockForGaiaAuthenticator() {}
+ protected:
+ bool Post(const GURL& url, const string& post_body,
+ unsigned long* response_code, string* response_body) {
+ *response_code = RC_REQUEST_OK;
+ response_body->assign("body\n");
+ return true;
+ }
+
+ int GetBackoffDelaySeconds(
+ int current_backoff_delay) {
+ // Dummy delay value.
+ return 5;
+ }
+};
+
+TEST(GaiaAuthenticatorTest, TestNewlineAtEndOfAuthTokenRemoved) {
+ GaiaAuthMockForGaiaAuthenticator mock_auth;
+ MessageLoop message_loop;
+ mock_auth.set_message_loop(&message_loop);
+ GaiaAuthenticator::AuthResults results;
+ EXPECT_TRUE(mock_auth.IssueAuthToken(&results, "sid", true));
+ EXPECT_EQ(0, results.auth_token.compare("body"));
+}
+
+} // namespace gaia
+
diff --git a/chrome/common/net/gaia/signin.h b/chrome/common/net/gaia/signin.h
new file mode 100644
index 0000000..48f3654
--- /dev/null
+++ b/chrome/common/net/gaia/signin.h
@@ -0,0 +1,18 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_NET_GAIA_SIGNIN_H_
+#define CHROME_COMMON_NET_GAIA_SIGNIN_H_
+
+namespace gaia {
+// This enumeration is here since we used to support hosted and non-hosted
+// accounts, but now only the latter is supported.
+enum SignIn {
+ // The account foo@domain is authenticated as a consumer account.
+ GMAIL_SIGNIN
+};
+
+} // namespace gaia
+#endif // CHROME_COMMON_NET_GAIA_SIGNIN_H_
+
diff --git a/chrome/common/net/http_return.h b/chrome/common/net/http_return.h
new file mode 100644
index 0000000..f47d1bb
--- /dev/null
+++ b/chrome/common/net/http_return.h
@@ -0,0 +1,17 @@
+// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef CHROME_COMMON_NET_HTTP_RETURN_H_
+#define CHROME_COMMON_NET_HTTP_RETURN_H_
+
+// TODO(sanjeevr): This has been moved from the sync library so it only
+// contains a few HTTP return codes. Add more HTTP return codes.
+enum HTTPReturnCode {
+ RC_REQUEST_OK = 200,
+ RC_UNAUTHORIZED = 401,
+ RC_FORBIDDEN = 403,
+};
+
+#endif // CHROME_COMMON_NET_HTTP_RETURN_H_
+