Re-land 43382, which was reverted in 43478 due to valgrind problems.

Valgrind issues failures addressed, trying to land this again.

Move fetching of fullfledged auth cookies to a time when we have the user's real profile available. Also, enable the use of a localaccount on Chrome OS.

Initial CL: http://codereview.chromium.org/1515003
Revert CL: http://codereview.chromium.org/1517015


Review URL: http://codereview.chromium.org/1567020

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@43629 0039d316-1c4b-4281-b951-d872f2087c98
diff --git a/chrome/browser/chromeos/external_cookie_handler.cc b/chrome/browser/chromeos/external_cookie_handler.cc
index 5881532a1..525ed80 100644
--- a/chrome/browser/chromeos/external_cookie_handler.cc
+++ b/chrome/browser/chromeos/external_cookie_handler.cc
@@ -68,7 +68,6 @@
     // it in to the cookie jar.
     std::string cookie_line = ReadLine(kChunkSize);
     while (!cookie_line.empty()) {
-      LOG(WARNING) << cookie_line;
       if (!cookie_store->SetCookieWithOptions(url, cookie_line, options))
         return false;
       cookie_line = ReadLine(kChunkSize);
diff --git a/chrome/browser/chromeos/login/auth_response_handler.cc b/chrome/browser/chromeos/login/auth_response_handler.cc
index fea29af..d1d37081 100644
--- a/chrome/browser/chromeos/login/auth_response_handler.cc
+++ b/chrome/browser/chromeos/login/auth_response_handler.cc
@@ -4,6 +4,9 @@
 
 #include "chrome/browser/chromeos/login/auth_response_handler.h"
 
+namespace chromeos {
+
+const int kHttpSuccess = 200;
 const char AuthResponseHandler::kClientLoginUrl[] =
     "https://www.google.com/accounts/ClientLogin";
 const char AuthResponseHandler::kIssueAuthTokenUrl[] =
@@ -13,3 +16,5 @@
 const char AuthResponseHandler::kTokenAuthUrl[] =
     "https://www.google.com/accounts/TokenAuth?"
     "continue=http://www.google.com/webhp&source=chromeos&auth=";
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/auth_response_handler.h b/chrome/browser/chromeos/login/auth_response_handler.h
index 0c672c7..04e9d1d1 100644
--- a/chrome/browser/chromeos/login/auth_response_handler.h
+++ b/chrome/browser/chromeos/login/auth_response_handler.h
@@ -11,6 +11,11 @@
 
 class GURL;
 
+namespace chromeos {
+
+// The success code specified by the HTTP spec.
+extern const int kHttpSuccess;
+
 class AuthResponseHandler {
  public:
   AuthResponseHandler() {}
@@ -33,4 +38,6 @@
   static const char kTokenAuthUrl[];
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_AUTH_RESPONSE_HANDLER_H_
diff --git a/chrome/browser/chromeos/login/authenticator.h b/chrome/browser/chromeos/login/authenticator.h
index d335127..cf6531e5 100644
--- a/chrome/browser/chromeos/login/authenticator.h
+++ b/chrome/browser/chromeos/login/authenticator.h
@@ -8,14 +8,19 @@
 #include <vector>
 
 #include "base/logging.h"
+#include "base/ref_counted.h"
+#include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/chromeos/login/login_status_consumer.h"
 
+class Profile;
+
+namespace chromeos {
+
 // An interface for objects that will authenticate a Chromium OS user.
 // When authentication successfully completes, will call
-// consumer_->OnLoginSuccess(|username|).
-// On failure, will call consumer_->OnLoginFailure().
-
-class Authenticator {
+// consumer_->OnLoginSuccess(|username|) on the UI thread.
+// On failure, will call consumer_->OnLoginFailure() on the UI thread.
+class Authenticator : public base::RefCountedThreadSafe<Authenticator> {
  public:
   explicit Authenticator(LoginStatusConsumer* consumer)
       : consumer_(consumer) {
@@ -24,9 +29,16 @@
 
   // Given a |username| and |password|, this method attempts to authenticate
   // Returns true if we kick off the attempt successfully and false if we can't.
-  virtual bool Authenticate(const std::string& username,
+  // Must be called on the FILE thread.
+  virtual bool Authenticate(Profile* profile,
+                            const std::string& username,
                             const std::string& password) = 0;
 
+  // These methods must be called on the UI thread, as they make DBus calls
+  // and also call back to the login UI.
+  virtual void OnLoginSuccess(const std::string& credentials) = 0;
+  virtual void OnLoginFailure(const std::string& data) = 0;
+
  protected:
   LoginStatusConsumer* consumer_;
 
@@ -42,14 +54,29 @@
   virtual ~StubAuthenticator() {}
 
   // Returns true after calling OnLoginSuccess().
-  virtual bool Authenticate(const std::string& username,
+  virtual bool Authenticate(Profile* profile,
+                            const std::string& username,
                             const std::string& password) {
-    consumer_->OnLoginSuccess(username, std::vector<std::string>());
+    username_ = username;
+    ChromeThread::PostTask(
+        ChromeThread::UI, FROM_HERE,
+        NewRunnableMethod(this,
+                          &StubAuthenticator::OnLoginSuccess,
+                          std::string()));
     return true;
   }
 
+  void OnLoginSuccess(const std::string& credentials) {
+    consumer_->OnLoginSuccess(username_, credentials);
+  }
+
+  void OnLoginFailure(const std::string& data) {}
+
  private:
+  std::string username_;
   DISALLOW_COPY_AND_ASSIGN(StubAuthenticator);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_AUTHENTICATOR_H_
diff --git a/chrome/browser/chromeos/login/client_login_response_handler.cc b/chrome/browser/chromeos/login/client_login_response_handler.cc
index 97a2edc..611662f 100644
--- a/chrome/browser/chromeos/login/client_login_response_handler.cc
+++ b/chrome/browser/chromeos/login/client_login_response_handler.cc
@@ -12,6 +12,8 @@
 #include "chrome/browser/net/url_fetcher.h"
 #include "net/base/load_flags.h"
 
+namespace chromeos {
+
 // By setting "service=gaia", we get an uber-auth-token back.
 const char ClientLoginResponseHandler::kService[] = "service=gaia";
 
@@ -37,9 +39,11 @@
   fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
   fetcher->set_upload_data("application/x-www-form-urlencoded", payload_);
   if (getter_) {
-    LOG(INFO) << "Fetching " << fetcher->url().spec();
+    LOG(INFO) << "Fetching";
     fetcher->set_request_context(getter_);
     fetcher->Start();
   }
   return fetcher;
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/client_login_response_handler.h b/chrome/browser/chromeos/login/client_login_response_handler.h
index 2650261..c0f1227 100644
--- a/chrome/browser/chromeos/login/client_login_response_handler.h
+++ b/chrome/browser/chromeos/login/client_login_response_handler.h
@@ -12,6 +12,12 @@
 
 class URLRequestContextGetter;
 
+namespace chromeos {
+
+// Handles responses to a fetch executed upon the Google Accounts ClientLogin
+// endpoint.  The cookies that are sent back in the response body are
+// reformatted into a request for an time-limited authorization token, which
+// is then sent to the IssueAuthToken endpoint.
 class ClientLoginResponseHandler : public AuthResponseHandler {
  public:
   explicit ClientLoginResponseHandler(URLRequestContextGetter* getter)
@@ -37,6 +43,10 @@
  private:
   std::string payload_;
   URLRequestContextGetter* getter_;
+
+  DISALLOW_COPY_AND_ASSIGN(ClientLoginResponseHandler);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_CLIENT_LOGIN_RESPONSE_HANDLER_H_
diff --git a/chrome/browser/chromeos/login/cookie_fetcher.cc b/chrome/browser/chromeos/login/cookie_fetcher.cc
new file mode 100644
index 0000000..d27dfed
--- /dev/null
+++ b/chrome/browser/chromeos/login/cookie_fetcher.cc
@@ -0,0 +1,67 @@
+// Copyright (c) 2010 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/browser/chromeos/login/cookie_fetcher.h"
+
+#include "base/command_line.h"
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/client_login_response_handler.h"
+#include "chrome/browser/chromeos/login/issue_response_handler.h"
+#include "chrome/browser/chromeos/login/login_utils.h"
+#include "chrome/browser/net/chrome_url_request_context.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/common/chrome_paths.h"
+#include "net/url_request/url_request_status.h"
+
+namespace chromeos {
+
+CookieFetcher::CookieFetcher(Profile* profile) : profile_(profile) {
+  CHECK(profile_);
+  client_login_handler_.reset(
+      new ClientLoginResponseHandler(profile_->GetRequestContext()));
+  issue_handler_.reset(
+      new IssueResponseHandler(profile_->GetRequestContext()));
+  launcher_.reset(new DelegateImpl);
+}
+
+void CookieFetcher::AttemptFetch(const std::string& credentials) {
+  LOG(INFO) << "getting auth token...";
+  fetcher_.reset(client_login_handler_->Handle(credentials, this));
+}
+
+void CookieFetcher::OnURLFetchComplete(const URLFetcher* source,
+                                       const GURL& url,
+                                       const URLRequestStatus& status,
+                                       int response_code,
+                                       const ResponseCookies& cookies,
+                                       const std::string& data) {
+  if (status.is_success() && response_code == kHttpSuccess) {
+    if (issue_handler_->CanHandle(url)) {
+      LOG(INFO) << "Handling auth token";
+      fetcher_.reset(issue_handler_->Handle(data, this));
+      return;
+    }
+  }
+  LOG(INFO) << "Calling DoLaunch";
+  launcher_->DoLaunch(profile_);
+  delete this;
+}
+
+void CookieFetcher::DelegateImpl::DoLaunch(Profile* profile) {
+  FilePath user_data_dir;
+  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
+  ProfileManager* profile_manager = g_browser_process->profile_manager();
+  if (profile == profile_manager->GetDefaultProfile(user_data_dir)) {
+    LoginUtils::DoBrowserLaunch(profile);
+  } else {
+    LOG(ERROR) <<
+        "Profile has changed since we started populating it with cookies";
+  }
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/cookie_fetcher.h b/chrome/browser/chromeos/login/cookie_fetcher.h
new file mode 100644
index 0000000..434da44c
--- /dev/null
+++ b/chrome/browser/chromeos/login/cookie_fetcher.h
@@ -0,0 +1,89 @@
+// Copyright (c) 2010 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_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_
+#define CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_
+
+#include <string>
+#include "base/scoped_ptr.h"
+#include "chrome/browser/chromeos/login/auth_response_handler.h"
+#include "chrome/browser/chromeos/login/client_login_response_handler.h"
+#include "chrome/browser/chromeos/login/issue_response_handler.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "chrome/browser/profile.h"
+
+namespace chromeos {
+
+// Given a SID/LSID pair, this class will attempt to turn them into a
+// full-fledged set of Google AuthN cookies.
+//
+// A CookieFetcher manages its own lifecycle.  It deletes itself once it's
+// done attempting to fetch URLs.
+class CookieFetcher : public URLFetcher::Delegate {
+ public:
+  // This class is a very thin wrapper around posting a task to the UI thread
+  // to call LoginUtils::DoBrowserLaunch().  It's here to allow mocking.
+  //
+  // In normal usage, instances of this class are owned by a CookieFetcher.
+  class Delegate {
+   public:
+    Delegate() {}
+    virtual ~Delegate() {}
+    virtual void DoLaunch(Profile* profile) = 0;
+  };
+
+  // |profile| is the Profile whose cookie jar you want the cookies in.
+  explicit CookieFetcher(Profile* profile);
+
+  // |profile| is the Profile whose cookie jar you want the cookies in.
+  // Takes ownership of |cl_handler|, |i_handler|, and |launcher|.
+  CookieFetcher(Profile* profile,
+                AuthResponseHandler* cl_handler,
+                AuthResponseHandler* i_handler,
+                Delegate* launcher)
+      : profile_(profile),
+        client_login_handler_(cl_handler),
+        issue_handler_(i_handler),
+        launcher_(launcher) {
+  }
+
+  // Given a newline-delineated SID/LSID pair of Google cookies (like
+  // those that come back from ClientLogin), try to use them to fetch
+  // a full-fledged set of Google AuthN cookies.  These cookies will wind up
+  // stored in the cookie jar associated with |profile_|, if we get them.
+  // Either way, we end up by calling launcher_->DoLaunch()
+  void AttemptFetch(const std::string& credentials);
+
+  // Overloaded from URLFetcher::Delegate.
+  virtual void OnURLFetchComplete(const URLFetcher* source,
+                                  const GURL& url,
+                                  const URLRequestStatus& status,
+                                  int response_code,
+                                  const ResponseCookies& cookies,
+                                  const std::string& data);
+
+ private:
+  class DelegateImpl : public Delegate {
+   public:
+    DelegateImpl() {}
+    ~DelegateImpl() {}
+    void DoLaunch(Profile* profile);
+   private:
+    DISALLOW_COPY_AND_ASSIGN(DelegateImpl);
+  };
+
+  virtual ~CookieFetcher() {}
+
+  scoped_ptr<URLFetcher> fetcher_;
+  Profile* profile_;
+  scoped_ptr<AuthResponseHandler> client_login_handler_;
+  scoped_ptr<AuthResponseHandler> issue_handler_;
+  scoped_ptr<Delegate> launcher_;
+
+  DISALLOW_COPY_AND_ASSIGN(CookieFetcher);
+};
+
+}  // namespace chromeos
+
+#endif  // CHROME_BROWSER_CHROMEOS_LOGIN_COOKIE_FETCHER_H_
diff --git a/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc
new file mode 100644
index 0000000..f07b395
--- /dev/null
+++ b/chrome/browser/chromeos/login/cookie_fetcher_unittest.cc
@@ -0,0 +1,219 @@
+// Copyright (c) 2010 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 <errno.h>
+#include <string>
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chromeos/login/cookie_fetcher.h"
+#include "chrome/browser/chromeos/login/client_login_response_handler.h"
+#include "chrome/browser/chromeos/login/issue_response_handler.h"
+#include "chrome/browser/chromeos/login/mock_auth_response_handler.h"
+#include "chrome/test/testing_profile.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromeos {
+using ::testing::Return;
+using ::testing::Invoke;
+using ::testing::Unused;
+using ::testing::_;
+
+class MockDelegate : public CookieFetcher::Delegate {
+ public:
+  MockDelegate() {}
+  virtual ~MockDelegate() {}
+  MOCK_METHOD1(DoLaunch, void(Profile* profile));
+};
+
+class CookieFetcherTest : public ::testing::Test {
+ public:
+  CookieFetcherTest()
+      : iat_url_(AuthResponseHandler::kIssueAuthTokenUrl),
+        ta_url_(AuthResponseHandler::kTokenAuthUrl),
+        client_login_data_("SID n' LSID"),
+        token_("auth token"),
+        ui_thread_(ChromeThread::UI, &message_loop_) {
+  }
+
+  const GURL iat_url_;
+  const GURL ta_url_;
+  const std::string client_login_data_;
+  const std::string token_;
+  MessageLoopForUI message_loop_;
+  ChromeThread ui_thread_;
+  TestingProfile profile_;
+};
+
+// Check that successful HTTP responses from both end points results in
+// the browser window getting put up.
+TEST_F(CookieFetcherTest, SuccessfulFetchTest) {
+  URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
+
+  MockAuthResponseHandler* cl_handler =
+      new MockAuthResponseHandler(iat_url_, status, kHttpSuccess, token_);
+  MockAuthResponseHandler* i_handler =
+      new MockAuthResponseHandler(ta_url_, status, kHttpSuccess, std::string());
+  MockDelegate* delegate = new MockDelegate;
+
+  CookieFetcher* cf = new CookieFetcher(NULL, cl_handler, i_handler, delegate);
+
+  EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*i_handler, CanHandle(iat_url_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*i_handler, CanHandle(ta_url_))
+      .WillOnce(Return(false));
+  EXPECT_CALL(*i_handler, Handle(token_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*delegate, DoLaunch(_))
+      .Times(1);
+
+  cf->AttemptFetch(client_login_data_);
+  message_loop_.RunAllPending();
+}
+
+// Check that a network failure when trying IssueAuthToken results in us bailing
+// and putting up the browser window.
+TEST_F(CookieFetcherTest, IssueAuthTokenNetworkFailureTest) {
+  URLRequestStatus failed(URLRequestStatus::FAILED, ECONNRESET);
+
+  MockAuthResponseHandler* cl_handler =
+      new MockAuthResponseHandler(iat_url_, failed, kHttpSuccess, token_);
+  MockDelegate* delegate = new MockDelegate;
+  // I expect nothing in i_handler to get called anyway
+  MockAuthResponseHandler* i_handler =
+      new MockAuthResponseHandler(ta_url_, failed, kHttpSuccess, std::string());
+
+  CookieFetcher* cf = new CookieFetcher(&profile_,
+                                        cl_handler,
+                                        i_handler,
+                                        delegate);
+
+  EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf))
+      .Times(1);
+  EXPECT_CALL(*delegate, DoLaunch(_))
+      .Times(1);
+
+  cf->AttemptFetch(client_login_data_);
+  message_loop_.RunAllPending();
+}
+
+// Check that a network failure when trying TokenAuth results in us bailing
+// and putting up the browser window.
+TEST_F(CookieFetcherTest, TokenAuthNetworkFailureTest) {
+  URLRequestStatus success;
+  URLRequestStatus failed(URLRequestStatus::FAILED, ECONNRESET);
+
+  MockAuthResponseHandler* cl_handler =
+      new MockAuthResponseHandler(iat_url_, success, kHttpSuccess, token_);
+  MockAuthResponseHandler* i_handler =
+      new MockAuthResponseHandler(ta_url_, failed, 0, std::string());
+  MockDelegate* delegate = new MockDelegate;
+
+  CookieFetcher* cf = new CookieFetcher(&profile_,
+                                        cl_handler,
+                                        i_handler,
+                                        delegate);
+
+  EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*i_handler, CanHandle(iat_url_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*i_handler, Handle(token_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*delegate, DoLaunch(_))
+      .Times(1);
+
+  cf->AttemptFetch(client_login_data_);
+  message_loop_.RunAllPending();
+}
+
+// Check that an unsuccessful HTTP response when trying IssueAuthToken results
+// in us bailing and putting up the browser window.
+TEST_F(CookieFetcherTest, IssueAuthTokenDeniedTest) {
+  URLRequestStatus success;
+
+  MockAuthResponseHandler* cl_handler =
+      new MockAuthResponseHandler(iat_url_, success, 403, std::string());
+  MockDelegate* delegate = new MockDelegate;
+  // I expect nothing in i_handler to get called anyway.
+  MockAuthResponseHandler* i_handler =
+      new MockAuthResponseHandler(ta_url_, success, 0, std::string());
+
+  CookieFetcher* cf = new CookieFetcher(&profile_,
+                                        cl_handler,
+                                        i_handler,
+                                        delegate);
+
+  EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf))
+      .Times(1);
+  EXPECT_CALL(*delegate, DoLaunch(_))
+      .Times(1);
+
+  cf->AttemptFetch(client_login_data_);
+  message_loop_.RunAllPending();
+}
+
+// Check that an unsuccessful HTTP response when trying TokenAuth results
+// in us bailing and putting up the browser window.
+TEST_F(CookieFetcherTest, TokenAuthDeniedTest) {
+  URLRequestStatus success;
+
+  MockAuthResponseHandler* cl_handler =
+      new MockAuthResponseHandler(iat_url_,
+                                  success,
+                                  kHttpSuccess,
+                                  token_);
+  MockAuthResponseHandler* i_handler =
+      new MockAuthResponseHandler(ta_url_, success, 403, std::string());
+  MockDelegate* delegate = new MockDelegate;
+
+  CookieFetcher* cf = new CookieFetcher(&profile_,
+                                        cl_handler,
+                                        i_handler,
+                                        delegate);
+
+  EXPECT_CALL(*cl_handler, Handle(client_login_data_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*i_handler, CanHandle(iat_url_))
+      .WillOnce(Return(true));
+  EXPECT_CALL(*i_handler, Handle(token_, cf))
+      .Times(1);
+
+  EXPECT_CALL(*delegate, DoLaunch(_))
+      .Times(1);
+
+  cf->AttemptFetch(client_login_data_);
+  message_loop_.RunAllPending();
+}
+
+TEST_F(CookieFetcherTest, ClientLoginResponseHandlerTest) {
+  ClientLoginResponseHandler handler(NULL);
+  std::string input("a\nb\n");
+  std::string expected("a&b&");
+  expected.append(ClientLoginResponseHandler::kService);
+
+  scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL));
+  EXPECT_EQ(expected, handler.payload());
+}
+
+TEST_F(CookieFetcherTest, IssueResponseHandlerTest) {
+  IssueResponseHandler handler(NULL);
+  std::string input("a\n");
+  std::string expected(IssueResponseHandler::kTokenAuthUrl);
+  expected.append(input);
+
+  scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL));
+  EXPECT_EQ(expected, handler.token_url());
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/existing_user_controller.cc b/chrome/browser/chromeos/login/existing_user_controller.cc
index 66a7978..ee29b0f 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.cc
+++ b/chrome/browser/chromeos/login/existing_user_controller.cc
@@ -10,6 +10,8 @@
 #include "base/message_loop.h"
 #include "base/stl_util-inl.h"
 #include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/chromeos/cros/cros_library.h"
 #include "chrome/browser/chromeos/cros/login_library.h"
 #include "chrome/browser/chromeos/login/authenticator.h"
@@ -17,6 +19,8 @@
 #include "chrome/browser/chromeos/login/login_utils.h"
 #include "chrome/browser/chromeos/login/wizard_controller.h"
 #include "chrome/browser/chromeos/wm_ipc.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
 #include "views/screen.h"
 #include "views/widget/widget.h"
 
@@ -118,10 +122,15 @@
   DCHECK(i != controllers_.end());
   index_of_view_logging_in_ = i - controllers_.begin();
 
-  authenticator_.reset(LoginUtils::Get()->CreateAuthenticator(this));
-  authenticator_->Authenticate(
-      controllers_[index_of_view_logging_in_]->user().email(),
-      UTF16ToUTF8(password));
+  authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
+  Profile* profile = g_browser_process->profile_manager()->GetWizardProfile();
+  ChromeThread::PostTask(
+      ChromeThread::FILE, FROM_HERE,
+      NewRunnableMethod(authenticator_.get(),
+                        &Authenticator::Authenticate,
+                        profile,
+                        controllers_[index_of_view_logging_in_]->user().email(),
+                        UTF16ToUTF8(password)));
 
   // Disable clicking on other windows.
   chromeos::WmIpc::Message message(
@@ -130,7 +139,7 @@
   chromeos::WmIpc::instance()->SendMessage(message);
 }
 
-void ExistingUserController::OnLoginFailure(const std::string error) {
+void ExistingUserController::OnLoginFailure(const std::string& error) {
   LOG(INFO) << "OnLoginFailure";
 
   // TODO(sky): alert the user in some way to the failure.
@@ -144,14 +153,14 @@
   chromeos::WmIpc::instance()->SendMessage(message);
 }
 
-void ExistingUserController::OnLoginSuccess(const std::string username,
-                                            std::vector<std::string> cookies) {
+void ExistingUserController::OnLoginSuccess(const std::string& username,
+                                            const std::string& credentials) {
   // Hide the login windows now.
   STLDeleteElements(&controllers_);
 
   background_window_->Close();
 
-  chromeos::LoginUtils::Get()->CompleteLogin(username, cookies);
+  chromeos::LoginUtils::Get()->CompleteLogin(username, credentials);
 
   // Delay deletion as we're on the stack.
   MessageLoop::current()->DeleteSoon(FROM_HERE, this);
diff --git a/chrome/browser/chromeos/login/existing_user_controller.h b/chrome/browser/chromeos/login/existing_user_controller.h
index 9e587fd..1f4c903 100644
--- a/chrome/browser/chromeos/login/existing_user_controller.h
+++ b/chrome/browser/chromeos/login/existing_user_controller.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/ref_counted.h"
 #include "base/task.h"
 #include "base/timer.h"
 #include "chrome/browser/chromeos/login/login_status_consumer.h"
@@ -16,14 +17,13 @@
 #include "chrome/browser/chromeos/wm_message_listener.h"
 #include "gfx/size.h"
 
-class Authenticator;
-
 namespace views {
 class Wiget;
 }
 
 namespace chromeos {
 
+class Authenticator;
 class BackgroundView;
 
 // ExistingUserController is used to handle login when someone has already
@@ -64,9 +64,9 @@
   virtual void Login(UserController* source, const string16& password);
 
   // LoginStatusConsumer:
-  virtual void OnLoginFailure(const std::string error);
-  virtual void OnLoginSuccess(const std::string username,
-                              std::vector<std::string> cookies);
+  virtual void OnLoginFailure(const std::string& error);
+  virtual void OnLoginSuccess(const std::string& username,
+                              const std::string& credentials);
 
   // Bounds of the background window.
   const gfx::Rect background_bounds_;
@@ -79,7 +79,7 @@
   std::vector<UserController*> controllers_;
 
   // Used for logging in.
-  scoped_ptr<Authenticator> authenticator_;
+  scoped_refptr<Authenticator> authenticator_;
 
   // Index of view loggin in.
   size_t index_of_view_logging_in_;
diff --git a/chrome/browser/chromeos/login/google_authenticator.cc b/chrome/browser/chromeos/login/google_authenticator.cc
index 409d382..0d8ba14 100644
--- a/chrome/browser/chromeos/login/google_authenticator.cc
+++ b/chrome/browser/chromeos/login/google_authenticator.cc
@@ -2,10 +2,11 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
+#include "chrome/browser/chromeos/login/google_authenticator.h"
+
 #include <errno.h>
 #include <string>
 #include <vector>
-#include <time.h>
 
 #include "base/logging.h"
 #include "base/file_path.h"
@@ -15,13 +16,10 @@
 #include "base/string_util.h"
 #include "base/third_party/nss/blapi.h"
 #include "base/third_party/nss/sha256.h"
-#include "base/time.h"
 #include "chrome/browser/browser_process.h"
 #include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/chromeos/cros/cryptohome_library.h"
-#include "chrome/browser/chromeos/login/client_login_response_handler.h"
-#include "chrome/browser/chromeos/login/issue_response_handler.h"
-#include "chrome/browser/chromeos/login/google_authenticator.h"
+#include "chrome/browser/chromeos/login/auth_response_handler.h"
 #include "chrome/browser/chromeos/login/login_status_consumer.h"
 #include "chrome/browser/net/chrome_url_request_context.h"
 #include "chrome/browser/net/url_fetcher.h"
@@ -34,37 +32,56 @@
 
 using base::Time;
 using base::TimeDelta;
-using namespace chromeos;
 using namespace file_util;
 
+namespace chromeos {
+
+// static
+const char GoogleAuthenticator::kCookiePersistence[] = "true";
+// static
+const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE";
+// static
+const char GoogleAuthenticator::kSource[] = "chromeos";
+// static
 const char GoogleAuthenticator::kFormat[] =
     "Email=%s&"
     "Passwd=%s&"
     "PersistentCookie=%s&"
     "accountType=%s&"
     "source=%s&";
-const char GoogleAuthenticator::kCookiePersistence[] = "true";
-const char GoogleAuthenticator::kAccountType[] = "HOSTED_OR_GOOGLE";
-const char GoogleAuthenticator::kSource[] = "chromeos";
-const int GoogleAuthenticator::kHttpSuccess = 200;
+
+// static
+const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt";
+// static
+const char GoogleAuthenticator::kOpenSSLMagic[] = "Salted__";
+// static
+const char GoogleAuthenticator::kLocalaccountFile[] = "localaccount";
+// static
+const char GoogleAuthenticator::kTmpfsTrigger[] = "incognito";
+
 const int kPassHashLen = 32;
 
-// Chromium OS system salt stored here
-const char GoogleAuthenticator::kSystemSalt[] = "/home/.shadow/salt";
+GoogleAuthenticator::GoogleAuthenticator(LoginStatusConsumer* consumer)
+    : Authenticator(consumer),
+      fetcher_(NULL),
+      getter_(NULL),
+      checked_for_localaccount_(false) {
+  CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
+}
 
-// String that appears at the start of OpenSSL cipher text with embedded salt
-const char GoogleAuthenticator::kOpenSSLMagic[] = "Salted__";
+GoogleAuthenticator::~GoogleAuthenticator() {
+  ChromeThread::DeleteSoon(ChromeThread::FILE, FROM_HERE, fetcher_);
+}
 
-bool GoogleAuthenticator::Authenticate(const std::string& username,
+bool GoogleAuthenticator::Authenticate(Profile* profile,
+                                       const std::string& username,
                                        const std::string& password) {
-  FilePath user_data_dir;
-  PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
-  ProfileManager* profile_manager = g_browser_process->profile_manager();
-  Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
+  DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
   getter_ = profile->GetRequestContext();
-  fetcher_.reset(new URLFetcher(GURL(AuthResponseHandler::kClientLoginUrl),
+  fetcher_ = URLFetcher::Create(0,
+                                GURL(AuthResponseHandler::kClientLoginUrl),
                                 URLFetcher::POST,
-                                this));
+                                this);
   fetcher_->set_request_context(getter_);
   fetcher_->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
   // TODO(cmasone): be more careful about zeroing memory that stores
@@ -76,13 +93,9 @@
                                   kAccountType,
                                   kSource);
   fetcher_->set_upload_data("application/x-www-form-urlencoded", body);
-  fetcher_->Start();
-  if (!client_login_handler_.get())
-    client_login_handler_.reset(new ClientLoginResponseHandler(getter_));
-  if (!issue_handler_.get())
-    issue_handler_.reset(new IssueResponseHandler(getter_));
   username_.assign(username);
   StoreHashedPassword(password);
+  fetcher_->Start();
   return true;
 }
 
@@ -92,93 +105,70 @@
                                              int response_code,
                                              const ResponseCookies& cookies,
                                              const std::string& data) {
-  if (status.is_success() && response_code == 200) {
-    if (client_login_handler_->CanHandle(url)) {
-      fetcher_.reset(client_login_handler_->Handle(data, this));
-    } else if (issue_handler_->CanHandle(url)) {
-      fetcher_.reset(issue_handler_->Handle(data, this));
-    } else {
-      LOG(INFO) << "Online login successful!";
-      ChromeThread::PostTask(
-          ChromeThread::UI, FROM_HERE,
-          NewRunnableFunction(GoogleAuthenticator::OnLoginSuccess,
-                              consumer_,
-                              username_,
-                              ascii_hash_,
-                              cookies));
-    }
+  if (status.is_success() && response_code == kHttpSuccess) {
+    LOG(INFO) << "Online login successful!";
+    ChromeThread::PostTask(
+        ChromeThread::UI, FROM_HERE,
+        NewRunnableMethod(this, &GoogleAuthenticator::OnLoginSuccess, data));
   } else if (!status.is_success()) {
     LOG(INFO) << "Network fail";
     // The fetch failed for network reasons, try offline login.
+    LoadLocalaccount(kLocalaccountFile);
     ChromeThread::PostTask(
         ChromeThread::UI, FROM_HERE,
-        NewRunnableFunction(GoogleAuthenticator::CheckOffline,
-                            consumer_,
-                            username_,
-                            ascii_hash_,
-                            status));
+        NewRunnableMethod(this, &GoogleAuthenticator::CheckOffline, status));
   } else {
-    std::string error;
-    if (status.is_success()) {
-      // The fetch succeeded, but ClientLogin said no.
-      error.assign(data);
-    } else {
-      // We couldn't hit the network, and offline login failed.
-      error.assign(strerror(status.os_error()));
-    }
+    // The fetch succeeded, but ClientLogin said no.
+    LoadLocalaccount(kLocalaccountFile);
     ChromeThread::PostTask(
         ChromeThread::UI, FROM_HERE,
-        NewRunnableFunction(
-            GoogleAuthenticator::OnLoginFailure, consumer_, error));
+        NewRunnableMethod(this, &GoogleAuthenticator::CheckLocalaccount, data));
   }
 }
 
-// static
-void GoogleAuthenticator::OnLoginSuccess(LoginStatusConsumer* consumer,
-                                         const std::string& username,
-                                         const std::string& passhash,
-                                         const ResponseCookies& cookies) {
-  if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username.c_str(),
-                                                        passhash.c_str()))
-    consumer->OnLoginSuccess(username, cookies);
-  else
-    GoogleAuthenticator::OnLoginFailure(consumer, "Could not mount cryptohome");
+void GoogleAuthenticator::OnLoginSuccess(const std::string& data) {
+  if (CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(username_.c_str(),
+                                                        ascii_hash_.c_str())) {
+    consumer_->OnLoginSuccess(username_, data);
+  } else {
+    OnLoginFailure("Could not mount cryptohome");
+  }
 }
 
-// static
-void GoogleAuthenticator::CheckOffline(LoginStatusConsumer* consumer,
-                                       const std::string& username,
-                                       const std::string& passhash,
-                                       const URLRequestStatus& status) {
-  if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey(username.c_str(),
-                                                           passhash.c_str())) {
+void GoogleAuthenticator::CheckOffline(const URLRequestStatus& status) {
+  if (CrosLibrary::Get()->GetCryptohomeLibrary()->CheckKey(
+          username_.c_str(),
+          ascii_hash_.c_str())) {
     // The fetch didn't succeed, but offline login did.
     LOG(INFO) << "Offline login successful!";
-    ResponseCookies cookies;
-    GoogleAuthenticator::OnLoginSuccess(consumer,
-                                        username,
-                                        passhash,
-                                        cookies);
+    OnLoginSuccess(std::string());
   } else {
     // We couldn't hit the network, and offline login failed.
-    GoogleAuthenticator::OnLoginFailure(consumer, strerror(status.os_error()));
+    GoogleAuthenticator::CheckLocalaccount(strerror(status.os_error()));
   }
 }
 
-// static
-void GoogleAuthenticator::OnLoginFailure(LoginStatusConsumer* consumer,
-                                         const std::string& data) {
-  // TODO(cmasone): what can we do to expose these OS/server-side error strings
-  // in an internationalizable way?
-  consumer->OnLoginFailure(data);
+void GoogleAuthenticator::CheckLocalaccount(const std::string& error) {
+  if (!localaccount_.empty() && localaccount_ == username_ &&
+      CrosLibrary::Get()->GetCryptohomeLibrary()->Mount(kTmpfsTrigger, "")) {
+    LOG(WARNING) << "Logging in with localaccount: " << localaccount_;
+    consumer_->OnLoginSuccess(username_, std::string());
+  } else {
+    OnLoginFailure(error);
+  }
 }
 
-void GoogleAuthenticator::LoadSystemSalt(FilePath& path) {
+void GoogleAuthenticator::OnLoginFailure(const std::string& data) {
+  LOG(WARNING) << "Login failed: " << data;
+  // TODO(cmasone): what can we do to expose these OS/server-side error strings
+  // in an internationalizable way?
+  consumer_->OnLoginFailure(data);
+}
+
+void GoogleAuthenticator::LoadSystemSalt(const FilePath& path) {
   if (!system_salt_.empty())
     return;
-
   CHECK(PathExists(path)) << path.value() << " does not exist!";
-
   int64 file_size;
   CHECK(GetFileSize(path, &file_size)) << "Could not get size of "
                                        << path.value();
@@ -190,19 +180,22 @@
   system_salt_.assign(salt, salt + data_read);
 }
 
-std::string GoogleAuthenticator::SaltAsAscii() {
-  FilePath salt_path(kSystemSalt);
-  LoadSystemSalt(salt_path);
-  unsigned int salt_len = system_salt_.size();
-  char ascii_salt[2 * salt_len + 1];
-  if (GoogleAuthenticator::BinaryToHex(system_salt_,
-                                       salt_len,
-                                       ascii_salt,
-                                       sizeof(ascii_salt))) {
-    return std::string(ascii_salt, sizeof(ascii_salt) - 1);
+void GoogleAuthenticator::LoadLocalaccount(const std::string& filename) {
+  if (checked_for_localaccount_)
+    return;
+  FilePath localaccount_file;
+  std::string localaccount;
+  if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
+    localaccount_file = localaccount_file.Append(filename);
+    LOG(INFO) << "looking for localaccount in " << localaccount_file.value();
+
+    ReadFileToString(localaccount_file, &localaccount);
+    TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
+    LOG(INFO) << "Loading localaccount: " << localaccount;
   } else {
-    return std::string();
+    LOG(INFO) << "Assuming no localaccount";
   }
+  set_localaccount(localaccount);
 }
 
 void GoogleAuthenticator::StoreHashedPassword(const std::string& password) {
@@ -235,6 +228,20 @@
   ascii_hash_.assign(ascii_buf, sizeof(ascii_buf) - 1);
 }
 
+std::string GoogleAuthenticator::SaltAsAscii() {
+  LoadSystemSalt(FilePath(kSystemSalt));  // no-op if it's already loaded.
+  unsigned int salt_len = system_salt_.size();
+  char ascii_salt[2 * salt_len + 1];
+  if (GoogleAuthenticator::BinaryToHex(system_salt_,
+                                       salt_len,
+                                       ascii_salt,
+                                       sizeof(ascii_salt))) {
+    return std::string(ascii_salt, sizeof(ascii_salt) - 1);
+  } else {
+    return std::string();
+  }
+}
+
 // static
 bool GoogleAuthenticator::BinaryToHex(const std::vector<unsigned char>& binary,
                                       const unsigned int binary_len,
@@ -247,3 +254,5 @@
     snprintf(hex_string + j, len - j, "%02x", binary[i]);
   return true;
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/google_authenticator.h b/chrome/browser/chromeos/login/google_authenticator.h
index f00ae8d..c89b7f0 100644
--- a/chrome/browser/chromeos/login/google_authenticator.h
+++ b/chrome/browser/chromeos/login/google_authenticator.h
@@ -10,53 +10,38 @@
 #include "base/basictypes.h"
 #include "base/file_path.h"
 #include "base/ref_counted.h"
-#include "base/scoped_ptr.h"
 #include "base/sha2.h"
 #include "chrome/browser/chromeos/cros/cros_library.h"
 #include "chrome/browser/chromeos/cros/cryptohome_library.h"
 #include "chrome/browser/chromeos/login/authenticator.h"
-#include "chrome/browser/chromeos/login/auth_response_handler.h"
 #include "chrome/browser/net/url_fetcher.h"
-
-class LoginStatusConsumer;
+#include "testing/gtest/include/gtest/gtest_prod.h"  // For FRIEND_TEST
 
 // Authenticates a Chromium OS user against the Google Accounts ClientLogin API.
 
+class Profile;
+
+namespace chromeos {
+
+class GoogleAuthenticatorTest;
+class LoginStatusConsumer;
+
 class GoogleAuthenticator : public Authenticator,
                             public URLFetcher::Delegate {
  public:
-  GoogleAuthenticator(LoginStatusConsumer* consumer,
-                      AuthResponseHandler* cl_handler,
-                      AuthResponseHandler* i_handler)
-      : Authenticator(consumer),
-        fetcher_(NULL),
-        getter_(NULL),
-        client_login_handler_(cl_handler),
-        issue_handler_(i_handler) {
-    CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
-    library_ = chromeos::CrosLibrary::Get()->GetCryptohomeLibrary();
-  }
-
-  explicit GoogleAuthenticator(LoginStatusConsumer* consumer)
-      : Authenticator(consumer),
-        fetcher_(NULL),
-        getter_(NULL),
-        client_login_handler_(NULL),
-        issue_handler_(NULL) {
-    CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
-    library_ = chromeos::CrosLibrary::Get()->GetCryptohomeLibrary();
-  }
-
-  virtual ~GoogleAuthenticator() {}
+  explicit GoogleAuthenticator(LoginStatusConsumer* consumer);
+  virtual ~GoogleAuthenticator();
 
   // Given a |username| and |password|, this method attempts to authenticate to
   // the Google accounts servers.  The ultimate result is either a callback to
   // consumer_->OnLoginSuccess() with the |username| and a vector of
   // authentication cookies or a callback to consumer_->OnLoginFailure() with
-  // an error message.
+  // an error message.  Uses |profile| when doing URL fetches.
+  // Should be called on the FILE thread!
   //
   // Returns true if the attempt gets sent successfully and false if not.
-  bool Authenticate(const std::string& username,
+  bool Authenticate(Profile* profile,
+                    const std::string& username,
                     const std::string& password);
 
   // Overridden from URLFetcher::Delegate
@@ -70,58 +55,51 @@
                                   const ResponseCookies& cookies,
                                   const std::string& data);
 
-  // Returns the ascii encoding of the system salt.
-  std::string SaltAsAscii();
 
-  // I need these static methods so I can PostTasks that use methods
-  // of sublcasses of LoginStatusConsumer.  I can't seem to make
-  // RunnableMethods out of methods belonging to subclasses without referring
-  // to the subclasses specifically, and I want to allow mocked out
-  // LoginStatusConsumers to be used here as well.
-  static void OnLoginSuccess(LoginStatusConsumer* consumer,
-                             const std::string& username,
-                             const std::string& passhash,
-                             const ResponseCookies& cookies);
-  static void CheckOffline(LoginStatusConsumer* consumer,
-                           const std::string& username,
-                           const std::string& passhash,
-                           const URLRequestStatus& status);
-  static void OnLoginFailure(LoginStatusConsumer* consumer,
-                             const std::string& data);
 
-  // Meant for testing.
-  void set_system_salt(const std::vector<unsigned char>& fake_salt) {
-    system_salt_ = fake_salt;
+  // Public for testing.
+  void set_system_salt(const std::vector<unsigned char>& new_salt) {
+    system_salt_ = new_salt;
+  }
+  void set_localaccount(const std::string& new_name) {
+    localaccount_ = new_name;
+    checked_for_localaccount_ = true;
   }
   void set_username(const std::string& fake_user) { username_ = fake_user; }
   void set_password_hash(const std::string& fake_hash) {
     ascii_hash_ = fake_hash;
   }
 
+  // These methods must be called on the UI thread, as they make DBus calls
+  // and also call back to the login UI.
+  void OnLoginSuccess(const std::string& credentials);
+  void CheckOffline(const URLRequestStatus& status);
+  void CheckLocalaccount(const std::string& error);
+  void OnLoginFailure(const std::string& data);
+
+  // The signal to cryptohomed that we want a tmpfs.
+  // TODO(cmasone): revisit this after cryptohome re-impl
+  static const char kTmpfsTrigger[];
+
  private:
-  static const char kCookiePersistence[];
-  static const char kAccountType[];
-  static const char kSource[];
-  static const char kFormat[];
-  static const int kHttpSuccess;
-  static const char kSystemSalt[];
-  static const char kOpenSSLMagic[];
-
-  chromeos::CryptohomeLibrary* library_;
-  scoped_ptr<URLFetcher> fetcher_;
-  URLRequestContextGetter* getter_;
-  scoped_ptr<AuthResponseHandler> client_login_handler_;
-  scoped_ptr<AuthResponseHandler> issue_handler_;
-  std::vector<unsigned char> system_salt_;
-  std::string username_;
-  std::string ascii_hash_;
-
   // If we don't have the system salt yet, loads it from |path|.
-  void LoadSystemSalt(FilePath& path);
+  // Should only be called on the FILE thread.
+  void LoadSystemSalt(const FilePath& path);
+
+  // If we haven't already, looks in a file called |filename| next to
+  // the browser executable for a "localaccount" name, and retrieves it
+  // if one is present.  If someone attempts to authenticate with this
+  // username, we will mount a tmpfs for them and let them use the
+  // browser.
+  // Should only be called on the FILE thread.
+  void LoadLocalaccount(const std::string& filename);
 
   // Stores a hash of |password|, salted with the ascii of |system_salt_|.
   void StoreHashedPassword(const std::string& password);
 
+  // Returns the ascii encoding of the system salt.
+  std::string SaltAsAscii();
+
   // Converts the binary data |binary| into an ascii hex string and stores
   // it in |hex_string|.  Not guaranteed to be NULL-terminated.
   // Returns false if |hex_string| is too small, true otherwise.
@@ -130,7 +108,42 @@
                           char* hex_string,
                           const unsigned int len);
 
+  // Constants to use in the ClientLogin request POST body.
+  static const char kCookiePersistence[];
+  static const char kAccountType[];
+  static const char kSource[];
+
+  // The format of said POST body.
+  static const char kFormat[];
+
+  // Chromium OS system salt stored here.
+  static const char kSystemSalt[];
+  // String that appears at the start of OpenSSL cipher text with embedded salt.
+  static const char kOpenSSLMagic[];
+
+  // Name of a file, next to chrome, that contains a local account username.
+  static const char kLocalaccountFile[];
+
+  URLFetcher* fetcher_;
+  URLRequestContextGetter* getter_;
+  std::string username_;
+  std::string ascii_hash_;
+  std::vector<unsigned char> system_salt_;
+  std::string localaccount_;
+  bool checked_for_localaccount_;  // needed becasuse empty localaccount_ is ok.
+
+  friend class GoogleAuthenticatorTest;
+  FRIEND_TEST(GoogleAuthenticatorTest, SaltToAsciiTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, ReadSaltTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, ReadLocalaccountTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, ReadLocalaccountTrailingWSTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, ReadNoLocalaccountTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, LoginNetFailureTest);
+  FRIEND_TEST(GoogleAuthenticatorTest, LoginDeniedTest);
+
   DISALLOW_COPY_AND_ASSIGN(GoogleAuthenticator);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_GOOGLE_AUTHENTICATOR_H_
diff --git a/chrome/browser/chromeos/login/google_authenticator_unittest.cc b/chrome/browser/chromeos/login/google_authenticator_unittest.cc
index 8139abd..a41ebe60 100644
--- a/chrome/browser/chromeos/login/google_authenticator_unittest.cc
+++ b/chrome/browser/chromeos/login/google_authenticator_unittest.cc
@@ -2,50 +2,62 @@
 // Use of this source code is governed by a BSD-style license that can be
 // found in the LICENSE file.
 
-#include "chrome/browser/chromeos/login/google_authenticator.h"
-#include "chrome/browser/chromeos/login/client_login_response_handler.h"
-#include "chrome/browser/chromeos/login/issue_response_handler.h"
-
 #include <errno.h>
 #include <string>
 #include <vector>
 
+#include "base/file_path.h"
+#include "base/file_util.h"
 #include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
+#include "base/string_util.h"
 #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h"
 #include "chrome/browser/chromeos/cros/mock_library_loader.h"
+#include "chrome/browser/chromeos/login/client_login_response_handler.h"
+#include "chrome/browser/chromeos/login/google_authenticator.h"
+#include "chrome/browser/chromeos/login/issue_response_handler.h"
 #include "chrome/browser/chromeos/login/mock_auth_response_handler.h"
 #include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/net/url_fetcher.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/testing_profile.h"
 #include "googleurl/src/gurl.h"
 #include "net/url_request/url_request_status.h"
 #include "testing/gtest/include/gtest/gtest.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-namespace chromeos {
+using namespace file_util;
 using ::testing::AnyNumber;
 using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
 using ::testing::_;
 
+namespace chromeos {
+
 class MockConsumer : public LoginStatusConsumer {
  public:
   MockConsumer() {}
   ~MockConsumer() {}
-  MOCK_METHOD1(OnLoginFailure, void(const std::string error));
-  MOCK_METHOD2(OnLoginSuccess, void(const std::string username,
-                                    const std::vector<std::string> cookies));
+  MOCK_METHOD1(OnLoginFailure, void(const std::string& error));
+  MOCK_METHOD2(OnLoginSuccess, void(const std::string& username,
+                                    const std::string& data));
 };
 
 class GoogleAuthenticatorTest : public ::testing::Test {
  public:
-  GoogleAuthenticatorTest() : username_("[email protected]") {
+  GoogleAuthenticatorTest()
+      : username_("[email protected]"),
+        bytes_as_ascii_("ffff") {
     memset(fake_hash_, 0, sizeof(fake_hash_));
     fake_hash_[0] = 10;
     fake_hash_[1] = 1;
     fake_hash_[7] = 10 << 4;
     hash_ascii_.assign("0a010000000000a0");
-    hash_ascii_.append(std::string(16,'0'));
+    hash_ascii_.append(std::string(16, '0'));
+
+    memset(raw_bytes_, 0xff, sizeof(raw_bytes_));
   }
   ~GoogleAuthenticatorTest() {}
 
@@ -74,13 +86,39 @@
     test_api->SetCryptohomeLibrary(NULL);
   }
 
+  FilePath PopulateTempFile(const char* data, int data_len) {
+    FilePath out;
+    FILE* tmp_file = CreateAndOpenTemporaryFile(&out);
+    EXPECT_NE(tmp_file, reinterpret_cast<FILE*>(NULL));
+    EXPECT_EQ(WriteFile(out, data, data_len), data_len);
+    EXPECT_TRUE(CloseFile(tmp_file));
+    return out;
+  }
+
+  FilePath FakeLocalaccountFile(const std::string& ascii) {
+    FilePath exe_dir;
+    FilePath local_account_file;
+    PathService::Get(base::DIR_EXE, &exe_dir);
+    FILE* tmp_file = CreateAndOpenTemporaryFileInDir(exe_dir,
+                                                     &local_account_file);
+    int ascii_len = ascii.length();
+    EXPECT_NE(tmp_file, reinterpret_cast<FILE*>(NULL));
+    EXPECT_EQ(WriteFile(local_account_file, ascii.c_str(), ascii_len),
+              ascii_len);
+    EXPECT_TRUE(CloseFile(tmp_file));
+    return local_account_file;
+  }
+
   unsigned char fake_hash_[32];
   std::string hash_ascii_;
   std::string username_;
+  std::string data_;
   ResponseCookies cookies_;
   // Mocks, destroyed by CrosLibrary class.
   MockCryptohomeLibrary* mock_library_;
   MockLibraryLoader* loader_;
+  char raw_bytes_[2];
+  std::string bytes_as_ascii_;
 };
 
 TEST_F(GoogleAuthenticatorTest, SaltToAsciiTest) {
@@ -90,30 +128,47 @@
   fake_salt[7] = 10 << 4;
   std::vector<unsigned char> salt_v(fake_salt, fake_salt + sizeof(fake_salt));
 
-  GoogleAuthenticator auth(NULL, NULL, NULL);
-  auth.set_system_salt(salt_v);
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL));
+  auth->set_system_salt(salt_v);
 
-  EXPECT_EQ("0a010000000000a0", auth.SaltAsAscii());
+  EXPECT_EQ("0a010000000000a0", auth->SaltAsAscii());
 }
 
-TEST_F(GoogleAuthenticatorTest, ClientLoginResponseHandlerTest) {
-  ClientLoginResponseHandler handler(NULL);
-  std::string input("a\nb\n");
-  std::string expected("a&b&");
-  expected.append(ClientLoginResponseHandler::kService);
+TEST_F(GoogleAuthenticatorTest, ReadSaltTest) {
+  FilePath tmp_file_path = PopulateTempFile(raw_bytes_, sizeof(raw_bytes_));
 
-  scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL));
-  EXPECT_EQ(expected, handler.payload());
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL));
+  auth->LoadSystemSalt(tmp_file_path);
+  EXPECT_EQ(auth->SaltAsAscii(), bytes_as_ascii_);
+  Delete(tmp_file_path, false);
 }
 
-TEST_F(GoogleAuthenticatorTest, IssueResponseHandlerTest) {
-  IssueResponseHandler handler(NULL);
-  std::string input("a\n");
-  std::string expected(IssueResponseHandler::kTokenAuthUrl);
-  expected.append(input);
+TEST_F(GoogleAuthenticatorTest, ReadLocalaccountTest) {
+  FilePath tmp_file_path = FakeLocalaccountFile(bytes_as_ascii_);
 
-  scoped_ptr<URLFetcher> fetcher(handler.Handle(input, NULL));
-  EXPECT_EQ(expected, handler.token_url());
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL));
+  auth->LoadLocalaccount(tmp_file_path.BaseName().value());
+  EXPECT_EQ(auth->localaccount_, bytes_as_ascii_);
+  Delete(tmp_file_path, false);
+}
+
+TEST_F(GoogleAuthenticatorTest, ReadLocalaccountTrailingWSTest) {
+  FilePath tmp_file_path =
+      FakeLocalaccountFile(StringPrintf("%s\n", bytes_as_ascii_.c_str()));
+
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL));
+  auth->LoadLocalaccount(tmp_file_path.BaseName().value());
+  EXPECT_EQ(auth->localaccount_, bytes_as_ascii_);
+  Delete(tmp_file_path, false);
+}
+
+TEST_F(GoogleAuthenticatorTest, ReadNoLocalaccountTest) {
+  FilePath tmp_file_path = FakeLocalaccountFile(bytes_as_ascii_);
+  EXPECT_TRUE(Delete(tmp_file_path, false));  // Ensure non-existent file.
+
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(NULL));
+  auth->LoadLocalaccount(tmp_file_path.BaseName().value());
+  EXPECT_EQ(auth->localaccount_, std::string());
 }
 
 TEST_F(GoogleAuthenticatorTest, OnLoginSuccessTest) {
@@ -123,9 +178,10 @@
   EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_))
       .WillOnce(Return(true));
 
-  GoogleAuthenticator auth(&consumer, NULL, NULL);
-  auth.OnLoginSuccess(&consumer, username_, hash_ascii_,
-                      cookies_);
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnLoginSuccess(data_);
 }
 
 TEST_F(GoogleAuthenticatorTest, MountFailureTest) {
@@ -135,12 +191,12 @@
   EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_))
       .WillOnce(Return(false));
 
-  GoogleAuthenticator auth(&consumer, NULL, NULL);
-  auth.OnLoginSuccess(&consumer, username_, hash_ascii_,
-                      cookies_);
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnLoginSuccess(data_);
 }
 
-static void Quit() { MessageLoop::current()->Quit(); }
 TEST_F(GoogleAuthenticatorTest, LoginNetFailureTest) {
   MessageLoopForUI message_loop;
   ChromeThread ui_thread(ChromeThread::UI, &message_loop);
@@ -153,15 +209,15 @@
 
   MockConsumer consumer;
   EXPECT_CALL(consumer, OnLoginFailure(data))
-      .WillOnce(InvokeWithoutArgs(Quit));
+      .Times(1);
   EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_))
       .WillOnce(Return(false));
 
-  GoogleAuthenticator auth(&consumer, NULL, NULL);
-  auth.set_password_hash(hash_ascii_);
-  auth.set_username(username_);
-  auth.OnURLFetchComplete(NULL, source, status, 0, cookies_, data);
-  MessageLoop::current()->Run();  // So tasks can be posted.
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data);
+  message_loop.RunAllPending();
 }
 
 TEST_F(GoogleAuthenticatorTest, LoginDeniedTest) {
@@ -175,11 +231,13 @@
 
   MockConsumer consumer;
   EXPECT_CALL(consumer, OnLoginFailure(data))
-      .WillOnce(InvokeWithoutArgs(Quit));
+      .Times(1);
 
-  GoogleAuthenticator auth(&consumer, NULL, NULL);
-  auth.OnURLFetchComplete(NULL, source, status, 403, cookies_, data);
-  MessageLoop::current()->Run();  // So tasks can be posted.
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnURLFetchComplete(NULL, source, status, 403, cookies_, data);
+  message_loop.RunAllPending();
 }
 
 TEST_F(GoogleAuthenticatorTest, OfflineLoginTest) {
@@ -193,55 +251,18 @@
   URLRequestStatus status(URLRequestStatus::FAILED, error_no);
 
   MockConsumer consumer;
-  EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_))
-      .WillOnce(InvokeWithoutArgs(Quit));
+  EXPECT_CALL(consumer, OnLoginSuccess(username_, data_))
+      .Times(1);
   EXPECT_CALL(*mock_library_, CheckKey(username_, hash_ascii_))
       .WillOnce(Return(true));
   EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_))
       .WillOnce(Return(true));
 
-  GoogleAuthenticator auth(&consumer, NULL, NULL);
-  auth.set_password_hash(hash_ascii_);
-  auth.set_username(username_);
-  auth.OnURLFetchComplete(NULL, source, status, 0, cookies_, data);
-  MessageLoop::current()->Run();  // So tasks can be posted.
-}
-
-TEST_F(GoogleAuthenticatorTest, ClientLoginPassIssueAuthTokenFailTest) {
-  MessageLoopForUI message_loop;
-  ChromeThread ui_thread(ChromeThread::UI, &message_loop);
-
-  std::string data("Error: NO!");
-  GURL cl_source(AuthResponseHandler::kClientLoginUrl);
-  GURL iat_source(AuthResponseHandler::kIssueAuthTokenUrl);
-  URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
-
-  MockConsumer consumer;
-  EXPECT_CALL(consumer, OnLoginFailure(data))
-      .WillOnce(InvokeWithoutArgs(Quit));
-  MockAuthResponseHandler* cl_handler = new MockAuthResponseHandler;
-  EXPECT_CALL(*cl_handler, CanHandle(cl_source))
-      .WillOnce(Return(true));
-
-  GoogleAuthenticator auth(&consumer,
-                           cl_handler,  // takes ownership.
-                           new IssueResponseHandler(NULL));
-  auth.set_password_hash(hash_ascii_);
-  auth.set_username(username_);
-
-  EXPECT_CALL(*cl_handler, Handle(_, &auth))
-      .WillOnce(Return(new URLFetcher(GURL(""),
-                                      URLFetcher::POST,
-                                      &auth)));
-
-  auth.OnURLFetchComplete(NULL,
-                          cl_source,
-                          status,
-                          200,
-                          cookies_,
-                          std::string());
-  auth.OnURLFetchComplete(NULL, iat_source, status, 403, cookies_, data);
-  MessageLoop::current()->Run();  // So tasks can be posted.
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnURLFetchComplete(NULL, source, status, 0, cookies_, data);
+  message_loop.RunAllPending();
 }
 
 TEST_F(GoogleAuthenticatorTest, OnlineLoginTest) {
@@ -252,18 +273,113 @@
   URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
 
   MockConsumer consumer;
-  EXPECT_CALL(consumer, OnLoginSuccess(username_, cookies_))
-      .WillOnce(InvokeWithoutArgs(Quit));
+  EXPECT_CALL(consumer, OnLoginSuccess(username_, data_))
+      .Times(1);
   EXPECT_CALL(*mock_library_, Mount(username_, hash_ascii_))
       .WillOnce(Return(true));
 
-  GoogleAuthenticator auth(&consumer,
-                           new ClientLoginResponseHandler(NULL),
-                           new IssueResponseHandler(NULL));
-  auth.set_password_hash(hash_ascii_);
-  auth.set_username(username_);
-  auth.OnURLFetchComplete(NULL, source, status, 200, cookies_, std::string());
-  MessageLoop::current()->Run();  // So tasks can be posted.
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->OnURLFetchComplete(NULL,
+                          source,
+                          status,
+                          kHttpSuccess,
+                          cookies_,
+                          std::string());
+  message_loop.RunAllPending();
+}
+
+TEST_F(GoogleAuthenticatorTest, LocalaccountLoginTest) {
+  GURL source(AuthResponseHandler::kTokenAuthUrl);
+  URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
+
+  std::string trigger(GoogleAuthenticator::kTmpfsTrigger);
+
+  MockConsumer consumer;
+  EXPECT_CALL(consumer, OnLoginSuccess(username_, _))
+      .Times(1);
+  EXPECT_CALL(*mock_library_, Mount(trigger, _))
+      .WillOnce(Return(true));
+
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_password_hash(hash_ascii_);
+  auth->set_username(username_);
+  auth->set_localaccount(username_);
+
+  auth->CheckLocalaccount(std::string());
+}
+
+// Responds as though ClientLogin was successful.
+class MockFetcher : public URLFetcher {
+ public:
+  MockFetcher(const GURL& url,
+              URLFetcher::RequestType request_type,
+              URLFetcher::Delegate* d)
+      : URLFetcher(url, request_type, d) {
+  }
+  ~MockFetcher() {}
+  void Start() {
+    GURL source(AuthResponseHandler::kClientLoginUrl);
+    URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
+    delegate()->OnURLFetchComplete(NULL,
+                                   source,
+                                   status,
+                                   kHttpSuccess,
+                                   ResponseCookies(),
+                                   std::string());
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockFetcher);
+};
+
+class MockFactory : public URLFetcher::Factory {
+ public:
+  MockFactory() {}
+  ~MockFactory() {}
+  URLFetcher* CreateURLFetcher(int id,
+                               const GURL& url,
+                               URLFetcher::RequestType request_type,
+                               URLFetcher::Delegate* d) {
+    return new MockFetcher(url, request_type, d);
+  }
+ private:
+  DISALLOW_COPY_AND_ASSIGN(MockFactory);
+};
+
+TEST_F(GoogleAuthenticatorTest, FullLoginTest) {
+  MessageLoopForUI message_loop;
+  ChromeThread ui_thread(ChromeThread::UI, &message_loop);
+  ChromeThread file_thread(ChromeThread::FILE);
+  file_thread.Start();
+
+  GURL source(AuthResponseHandler::kTokenAuthUrl);
+  URLRequestStatus status(URLRequestStatus::SUCCESS, 0);
+
+  MockConsumer consumer;
+  EXPECT_CALL(consumer, OnLoginSuccess(username_, data_))
+      .Times(1);
+  EXPECT_CALL(*mock_library_, Mount(username_, _))
+      .WillOnce(Return(true));
+
+  TestingProfile profile;
+
+  MockFactory factory;
+  URLFetcher::set_factory(&factory);
+  std::vector<unsigned char> salt_v(fake_hash_,
+                                    fake_hash_ + sizeof(fake_hash_));
+
+  scoped_refptr<GoogleAuthenticator> auth(new GoogleAuthenticator(&consumer));
+  auth->set_system_salt(salt_v);
+
+  ChromeThread::PostTask(
+      ChromeThread::FILE, FROM_HERE,
+      NewRunnableMethod(auth.get(),
+                        &Authenticator::Authenticate,
+                        &profile, username_, hash_ascii_));
+  file_thread.Stop();
+  message_loop.RunAllPending();
+  URLFetcher::set_factory(NULL);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/issue_response_handler.cc b/chrome/browser/chromeos/login/issue_response_handler.cc
index 7c0850ae..20fa6196 100644
--- a/chrome/browser/chromeos/login/issue_response_handler.cc
+++ b/chrome/browser/chromeos/login/issue_response_handler.cc
@@ -10,8 +10,7 @@
 #include "chrome/browser/net/url_fetcher.h"
 #include "net/base/load_flags.h"
 
-const int kMaxRedirs = 2;
-const int kTimeout = 2;
+namespace chromeos {
 
 // Overridden from AuthResponseHandler.
 bool IssueResponseHandler::CanHandle(const GURL& url) {
@@ -31,8 +30,11 @@
       new URLFetcher(GURL(token_url_), URLFetcher::GET, catcher);
   fetcher->set_load_flags(net::LOAD_DO_NOT_SEND_COOKIES);
   if (getter_) {
+    LOG(INFO) << "Fetching";
     fetcher->set_request_context(getter_);
     fetcher->Start();
   }
   return fetcher;
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/issue_response_handler.h b/chrome/browser/chromeos/login/issue_response_handler.h
index 3153b97e..4741c80 100644
--- a/chrome/browser/chromeos/login/issue_response_handler.h
+++ b/chrome/browser/chromeos/login/issue_response_handler.h
@@ -12,6 +12,13 @@
 
 class URLRequestContextGetter;
 
+namespace chromeos {
+
+// Handles responses to a fetch executed upon the Google Accounts IssueAuthToken
+// endpoint.  The token that's sent back in the response body is used as an
+// URL query parameter in a request that, ultimately, results in a full set
+// of authorization cookies for Google services being left in the cookie jar
+// associated with |getter_|.
 class IssueResponseHandler : public AuthResponseHandler {
  public:
   explicit IssueResponseHandler(URLRequestContextGetter* getter)
@@ -24,15 +31,20 @@
   // Overridden from AuthResponseHandler.
   // Takes in a response from IssueAuthToken, formats into an appropriate query
   // to sent to TokenAuth, and issues said query.  |catcher| will receive
-  // the response to the fetch.
+  // the response to the fetch.  This fetch will follow redirects, which is
+  // necesary to support GAFYD and corp accounts.
   virtual URLFetcher* Handle(const std::string& to_process,
                              URLFetcher::Delegate* catcher);
 
   // exposed for testing
   std::string token_url() { return token_url_; }
+
  private:
   std::string token_url_;
   URLRequestContextGetter* getter_;
+  DISALLOW_COPY_AND_ASSIGN(IssueResponseHandler);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_ISSUE_RESPONSE_HANDLER_H_
diff --git a/chrome/browser/chromeos/login/login_manager_view.cc b/chrome/browser/chromeos/login/login_manager_view.cc
index 183d642..20d7a8fa 100644
--- a/chrome/browser/chromeos/login/login_manager_view.cc
+++ b/chrome/browser/chromeos/login/login_manager_view.cc
@@ -18,6 +18,8 @@
 #include "base/logging.h"
 #include "base/process_util.h"
 #include "base/utf_string_conversions.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/chromeos/cros/cros_library.h"
 #include "chrome/browser/chromeos/cros/network_library.h"
 #include "chrome/browser/chromeos/login/authentication_notification_details.h"
@@ -25,6 +27,8 @@
 #include "chrome/browser/chromeos/login/rounded_rect_painter.h"
 #include "chrome/browser/chromeos/login/screen_observer.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/profile_manager.h"
 #include "chrome/common/notification_service.h"
 #include "grit/generated_resources.h"
 #include "grit/theme_resources.h"
@@ -76,9 +80,9 @@
       ALLOW_THIS_IN_INITIALIZER_LIST(focus_grabber_factory_(this)),
       focus_delayed_(false) {
   if (kStubOutLogin)
-    authenticator_.reset(new StubAuthenticator(this));
+    authenticator_ = new StubAuthenticator(this);
   else
-    authenticator_.reset(LoginUtils::Get()->CreateAuthenticator(this));
+    authenticator_ = LoginUtils::Get()->CreateAuthenticator(this);
 }
 
 LoginManagerView::~LoginManagerView() {
@@ -306,7 +310,12 @@
     username_field_->SetText(UTF8ToUTF16(username));
   }
 
-  authenticator_->Authenticate(username, password);
+  Profile* profile = g_browser_process->profile_manager()->GetWizardProfile();
+  ChromeThread::PostTask(
+      ChromeThread::FILE, FROM_HERE,
+      NewRunnableMethod(authenticator_.get(),
+                        &Authenticator::Authenticate,
+                        profile, username, password));
 }
 
 // Sign in button causes a login attempt.
@@ -321,7 +330,7 @@
   observer_->OnExit(ScreenObserver::LOGIN_CREATE_ACCOUNT);
 }
 
-void LoginManagerView::OnLoginFailure(const std::string error) {
+void LoginManagerView::OnLoginFailure(const std::string& error) {
   LOG(INFO) << "LoginManagerView: OnLoginFailure() " << error;
   NetworkLibrary* network = CrosLibrary::Get()->GetNetworkLibrary();
 
@@ -345,13 +354,14 @@
   password_field_->RequestFocus();
 }
 
-void LoginManagerView::OnLoginSuccess(const std::string username,
-                                      std::vector<std::string> cookies) {
+void LoginManagerView::OnLoginSuccess(const std::string& username,
+                                      const std::string& credentials) {
   // TODO(cmasone): something sensible if errors occur.
   if (observer_) {
     observer_->OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED);
   }
-  LoginUtils::Get()->CompleteLogin(username, cookies);
+
+  LoginUtils::Get()->CompleteLogin(username, credentials);
 }
 
 void LoginManagerView::ShowError(int error_id) {
diff --git a/chrome/browser/chromeos/login/login_manager_view.h b/chrome/browser/chromeos/login/login_manager_view.h
index de4108670..686524c3 100644
--- a/chrome/browser/chromeos/login/login_manager_view.h
+++ b/chrome/browser/chromeos/login/login_manager_view.h
@@ -8,6 +8,7 @@
 #include <string>
 #include <vector>
 
+#include "base/ref_counted.h"
 #include "base/scoped_ptr.h"
 #include "chrome/browser/chromeos/login/authenticator.h"
 #include "chrome/browser/chromeos/login/login_status_consumer.h"
@@ -83,9 +84,9 @@
   virtual bool AcceleratorPressed(const views::Accelerator& accelerator);
 
   // Overriden from LoginStatusConsumer.
-  virtual void OnLoginFailure(const std::string error);
-  virtual void OnLoginSuccess(const std::string username,
-                              std::vector<std::string> cookies);
+  virtual void OnLoginFailure(const std::string& error);
+  virtual void OnLoginSuccess(const std::string& username,
+                              const std::string& credentials);
 
  protected:
   // views::View overrides:
@@ -143,7 +144,7 @@
   // (on the hidden tab, for example).
   bool focus_delayed_;
 
-  scoped_ptr<Authenticator> authenticator_;
+  scoped_refptr<Authenticator> authenticator_;
 
   DISALLOW_COPY_AND_ASSIGN(LoginManagerView);
 };
diff --git a/chrome/browser/chromeos/login/login_manager_view_browsertest.cc b/chrome/browser/chromeos/login/login_manager_view_browsertest.cc
index dba2949..cf49a7c 100644
--- a/chrome/browser/chromeos/login/login_manager_view_browsertest.cc
+++ b/chrome/browser/chromeos/login/login_manager_view_browsertest.cc
@@ -3,6 +3,7 @@
 // found in the LICENSE file.
 
 #include "base/message_loop.h"
+#include "chrome/browser/chrome_thread.h"
 #include "chrome/browser/chromeos/cros/mock_cryptohome_library.h"
 #include "chrome/browser/chromeos/cros/mock_login_library.h"
 #include "chrome/browser/chromeos/login/login_manager_view.h"
@@ -15,9 +16,12 @@
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
 
+class Profile;
+
 namespace chromeos {
 
 using ::testing::AnyNumber;
+using ::testing::InvokeWithoutArgs;
 using ::testing::Return;
 
 const char kUsername[] = "[email protected]";
@@ -35,19 +39,41 @@
         authenticate_result_(true) {
   }
 
-  // Returns true after calling OnLoginSuccess().
-  virtual bool Authenticate(const std::string& username,
+  // Returns true after posting task to UI thread to call OnLoginSuccess().
+  // This is called on the FILE thread now, so we need to do this.
+  virtual bool Authenticate(Profile* profile,
+                            const std::string& username,
                             const std::string& password) {
     EXPECT_EQ(expected_username_, username);
     EXPECT_EQ(expected_password_, password);
+
     if (authenticate_result_) {
-      consumer_->OnLoginSuccess(username, std::vector<std::string>());
+      ChromeThread::PostTask(
+          ChromeThread::UI, FROM_HERE,
+          NewRunnableMethod(this,
+                            &MockAuthenticator::OnLoginSuccess,
+                            username));
     } else {
-      consumer_->OnLoginFailure(kLoginError);
+      ChromeThread::PostTask(
+          ChromeThread::UI, FROM_HERE,
+          NewRunnableMethod(this,
+                            &MockAuthenticator::OnLoginFailure,
+                            std::string(kLoginError)));
     }
     return authenticate_result_;
   }
 
+  void OnLoginSuccess(const std::string& username) {
+    consumer_->OnLoginSuccess(username, std::string());
+  }
+
+  void OnLoginFailure(const std::string& data) {
+      consumer_->OnLoginFailure(data);
+      LOG(INFO) << "Posting a QuitTask to UI thread";
+      ChromeThread::PostTask(
+          ChromeThread::UI, FROM_HERE, new MessageLoop::QuitTask);
+  }
+
   void set_authenticate_result(bool b) {
     authenticate_result_ = b;
   }
@@ -69,7 +95,7 @@
   }
 
   virtual void CompleteLogin(const std::string& username,
-                             std::vector<std::string> cookies) {
+                             const std::string& cookies) {
     EXPECT_EQ(expected_username_, username);
   }
 
@@ -123,6 +149,11 @@
   DISALLOW_COPY_AND_ASSIGN(LoginManagerViewTest);
 };
 
+static void Quit() {
+  LOG(INFO) << "Posting a QuitTask to UI thread";
+    ChromeThread::PostTask(
+        ChromeThread::UI, FROM_HERE, new MessageLoop::QuitTask);
+}
 IN_PROC_BROWSER_TEST_F(LoginManagerViewTest, TestBasic) {
   ASSERT_TRUE(controller() != NULL);
   ASSERT_EQ(controller()->current_screen(), controller()->GetLoginScreen());
@@ -131,13 +162,19 @@
       new MockScreenObserver());
   EXPECT_CALL(*mock_screen_observer,
               OnExit(ScreenObserver::LOGIN_SIGN_IN_SELECTED))
-      .Times(1);
+      .WillOnce(InvokeWithoutArgs(Quit));
 
   LoginManagerView* login = controller()->GetLoginScreen()->view();
   login->set_observer(mock_screen_observer.get());
   login->SetUsername(kUsername);
   login->SetPassword(kPassword);
+
+  bool old_state = MessageLoop::current()->NestableTasksAllowed();
+  MessageLoop::current()->SetNestableTasksAllowed(true);
   login->Login();
+  MessageLoop::current()->Run();
+  MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
   login->set_observer(NULL);
 }
 
@@ -159,7 +196,13 @@
   MockAuthenticator* authenticator = static_cast<MockAuthenticator*>(
       login->authenticator());
   authenticator->set_authenticate_result(false);
+
+  bool old_state = MessageLoop::current()->NestableTasksAllowed();
+  MessageLoop::current()->SetNestableTasksAllowed(true);
   login->Login();
+  MessageLoop::current()->Run();
+  MessageLoop::current()->SetNestableTasksAllowed(old_state);
+
   ASSERT_EQ(controller()->current_screen(), controller()->GetLoginScreen());
   ASSERT_EQ(login->error_id(), IDS_LOGIN_ERROR_AUTHENTICATING);
   login->set_observer(NULL);
diff --git a/chrome/browser/chromeos/login/login_status_consumer.h b/chrome/browser/chromeos/login/login_status_consumer.h
index bf8f7e3..5c28c62 100644
--- a/chrome/browser/chromeos/login/login_status_consumer.h
+++ b/chrome/browser/chromeos/login/login_status_consumer.h
@@ -7,18 +7,19 @@
 
 #include <string>
 
+namespace chromeos {
+
 // An interface that defines the callbacks for objects that the
 // Authenticator class will call to report the success/failure of
 // authentication for Chromium OS.
 class LoginStatusConsumer {
  public:
   virtual ~LoginStatusConsumer() {}
-  // These copy data in, so as to avoid potential object lifetime problems.
-  virtual void OnLoginFailure(const std::string error) = 0;
-  virtual void OnLoginSuccess(const std::string username,
-                              std::vector<std::string> cookies) = 0;
+  virtual void OnLoginFailure(const std::string& error) = 0;
+  virtual void OnLoginSuccess(const std::string& username,
+                              const std::string& credentials) = 0;
 };
 
-
+}  // namespace chromeos
 
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_LOGIN_STATUS_CONSUMER_H_
diff --git a/chrome/browser/chromeos/login/login_utils.cc b/chrome/browser/chromeos/login/login_utils.cc
index bc970f63..006f2bb 100644
--- a/chrome/browser/chromeos/login/login_utils.cc
+++ b/chrome/browser/chromeos/login/login_utils.cc
@@ -14,6 +14,7 @@
 #include "chrome/browser/chromeos/cros/login_library.h"
 #include "chrome/browser/chromeos/external_cookie_handler.h"
 #include "chrome/browser/chromeos/login/authentication_notification_details.h"
+#include "chrome/browser/chromeos/login/cookie_fetcher.h"
 #include "chrome/browser/chromeos/login/google_authenticator.h"
 #include "chrome/browser/chromeos/login/pam_google_authenticator.h"
 #include "chrome/browser/chromeos/login/user_manager.h"
@@ -37,7 +38,7 @@
   // Invoked after the user has successfully logged in. This launches a browser
   // and does other bookkeeping after logging in.
   virtual void CompleteLogin(const std::string& username,
-                             std::vector<std::string> cookies);
+                             const std::string& credentials);
 
   // Creates and returns the authenticator to use. The caller owns the returned
   // Authenticator and must delete it when done.
@@ -47,9 +48,9 @@
   DISALLOW_COPY_AND_ASSIGN(LoginUtilsImpl);
 };
 
-class LoginUtilsWraper {
+class LoginUtilsWrapper {
  public:
-  LoginUtilsWraper() : ptr_(new LoginUtilsImpl) {
+  LoginUtilsWrapper() : ptr_(new LoginUtilsImpl) {
   }
 
   LoginUtils* get() {
@@ -63,12 +64,12 @@
  private:
   scoped_ptr<LoginUtils> ptr_;
 
-  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWraper);
+  DISALLOW_COPY_AND_ASSIGN(LoginUtilsWrapper);
 };
 
 void LoginUtilsImpl::CompleteLogin(const std::string& username,
-                                   std::vector<std::string> cookies) {
-  LOG(INFO) << "LoginManagerView: OnLoginSuccess()";
+                                   const std::string& credentials) {
+  LOG(INFO) << "Completing login for " << username;
 
   if (CrosLibrary::Get()->EnsureLoaded())
     CrosLibrary::Get()->GetLoginLibrary()->StartSession(username, "");
@@ -83,7 +84,6 @@
       Details<AuthenticationNotificationDetails>(&details));
 
   // Now launch the initial browser window.
-  BrowserInit browser_init;
   const CommandLine& command_line = *CommandLine::ForCurrentProcess();
   FilePath user_data_dir;
   PathService::Get(chrome::DIR_USER_DATA, &user_data_dir);
@@ -91,19 +91,20 @@
   // The default profile will have been changed because the ProfileManager
   // will process the notification that the UserManager sends out.
   Profile* profile = profile_manager->GetDefaultProfile(user_data_dir);
-  int return_code;
 
   if (!CommandLine::ForCurrentProcess()->HasSwitch(switches::kInChromeAuth)) {
     ExternalCookieHandler::GetCookies(command_line, profile);
+    DoBrowserLaunch(profile);
   } else {
-    GURL url(ExternalCookieHandler::kGoogleAccountsUrl);
-    net::CookieOptions options;
-    options.set_include_httponly();
-    profile->GetRequestContext()->GetCookieStore()->SetCookiesWithOptions(
-        url, cookies, options);
+    // Take the credentials passed in and try to exchange them for
+    // full-fledged Google authentication cookies.  This is
+    // best-effort; it's possible that we'll fail due to network
+    // troubles or some such.  Either way, |cf| will call
+    // DoBrowserLaunch on the UI thread when it's done, and then
+    // delete itself.
+    CookieFetcher* cf = new CookieFetcher(profile);
+    cf->AttemptFetch(credentials);
   }
-  browser_init.LaunchBrowser(command_line, profile, std::wstring(), true,
-                             &return_code);
 }
 
 Authenticator* LoginUtilsImpl::CreateAuthenticator(
@@ -114,11 +115,22 @@
 }
 
 LoginUtils* LoginUtils::Get() {
-  return Singleton<LoginUtilsWraper>::get()->get();
+  return Singleton<LoginUtilsWrapper>::get()->get();
 }
 
 void LoginUtils::Set(LoginUtils* mock) {
-  Singleton<LoginUtilsWraper>::get()->reset(mock);
+  Singleton<LoginUtilsWrapper>::get()->reset(mock);
+}
+
+void LoginUtils::DoBrowserLaunch(Profile* profile) {
+  LOG(INFO) << "Launching browser...";
+  BrowserInit browser_init;
+  int return_code;
+  browser_init.LaunchBrowser(*CommandLine::ForCurrentProcess(),
+                             profile,
+                             std::wstring(),
+                             true,
+                             &return_code);
 }
 
 }  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/login_utils.h b/chrome/browser/chromeos/login/login_utils.h
index bb1b1d36..6d7791c 100644
--- a/chrome/browser/chromeos/login/login_utils.h
+++ b/chrome/browser/chromeos/login/login_utils.h
@@ -8,15 +8,13 @@
 #include <string>
 #include <vector>
 
-class Authenticator;
-class LoginStatusConsumer;
-
-namespace views {
-class Widget;
-}
+class Profile;
 
 namespace chromeos {
 
+class Authenticator;
+class LoginStatusConsumer;
+
 class LoginUtils {
  public:
   // Get LoginUtils singleton object. If it was not set before, new default
@@ -26,12 +24,16 @@
   // Set LoginUtils singleton object for test purpose only!
   static void Set(LoginUtils* ptr);
 
+  // Thin wrapper around BrowserInit::LaunchBrowser().  Meant to be used in a
+  // Task posted to the UI thread.
+  static void DoBrowserLaunch(Profile* profile);
+
   virtual ~LoginUtils() {}
 
   // Invoked after the user has successfully logged in. This launches a browser
   // and does other bookkeeping after logging in.
   virtual void CompleteLogin(const std::string& username,
-                             std::vector<std::string> cookies) = 0;
+                             const std::string& credentials) = 0;
 
   // Creates and returns the authenticator to use. The caller owns the returned
   // Authenticator and must delete it when done.
diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.cc b/chrome/browser/chromeos/login/mock_auth_response_handler.cc
new file mode 100644
index 0000000..1d04331
--- /dev/null
+++ b/chrome/browser/chromeos/login/mock_auth_response_handler.cc
@@ -0,0 +1,62 @@
+// Copyright (c) 2010 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/browser/chromeos/login/mock_auth_response_handler.h"
+
+#include <string>
+
+#include "base/message_loop.h"
+#include "chrome/browser/net/url_fetcher.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_status.h"
+#include "testing/gmock/include/gmock/gmock.h"
+
+namespace chromeos {
+
+using ::testing::_;
+using ::testing::Invoke;
+
+MockAuthResponseHandler::MockAuthResponseHandler(const GURL& url,
+                                                 const URLRequestStatus& status,
+                                                 const int code,
+                                                 const std::string& data)
+    : remote_(url),
+      status_(status),
+      http_response_code_(code),
+      data_(data) {
+  // Take the args sent to Handle() and pass them to MockNetwork(), which will
+  // use the data passed to the constructor here to fill out the call to
+  // OnURLFetchComplete().
+  ON_CALL(*this, Handle(_,_))
+      .WillByDefault(Invoke(this, &MockAuthResponseHandler::MockNetwork));
+}
+
+void MockAuthResponseHandler::CompleteFetch(URLFetcher::Delegate* delegate,
+                                            const GURL remote,
+                                            const URLRequestStatus status,
+                                            const int http_response_code,
+                                            const std::string data) {
+  delegate->OnURLFetchComplete(NULL,
+                               remote,
+                               status,
+                               http_response_code,
+                               ResponseCookies(),
+                               data);
+}
+
+URLFetcher* MockAuthResponseHandler::MockNetwork(
+    std::string data,
+    URLFetcher::Delegate* delegate) {
+  MessageLoop::current()->PostTask(
+      FROM_HERE,
+      NewRunnableFunction(MockAuthResponseHandler::CompleteFetch,
+                          delegate,
+                          remote_,
+                          status_,
+                          http_response_code_,
+                          data_));
+  return new URLFetcher(GURL(), URLFetcher::GET, delegate);
+}
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/mock_auth_response_handler.h b/chrome/browser/chromeos/login/mock_auth_response_handler.h
index 394dc7c..81c33b2 100644
--- a/chrome/browser/chromeos/login/mock_auth_response_handler.h
+++ b/chrome/browser/chromeos/login/mock_auth_response_handler.h
@@ -9,20 +9,50 @@
 
 #include <string>
 
-#include "base/logging.h"
+#include "googleurl/src/gurl.h"
+#include "net/url_request/url_request_status.h"
 #include "testing/gmock/include/gmock/gmock.h"
 
-class GURL;
 class URLFetcher;
 
+namespace chromeos {
+
+// In real AuthResponseHandler subclasses, Handle(data, delegate)
+// initiates an HTTP fetch.  To mock this, we would like to set up
+// appropriate state and then call the OnURLFetchComplete method of
+// |delegate| directly.  Rather than using some kind of global state, we
+// allow a MockAuthResponseHandler to be instantiated with the state we
+// want to send to OnURLFetchComplete, and then have Handle() Invoke a helper
+// method that will do this work.
 class MockAuthResponseHandler : public AuthResponseHandler {
  public:
-  MockAuthResponseHandler() {}
+  MockAuthResponseHandler(const GURL& url,
+                          const URLRequestStatus& status,
+                          const int code,
+                          const std::string& data);
   virtual ~MockAuthResponseHandler() {}
 
   MOCK_METHOD1(CanHandle, bool(const GURL& url));
   MOCK_METHOD2(Handle, URLFetcher*(const std::string& to_process,
                                    URLFetcher::Delegate* catcher));
+
+  URLFetcher* MockNetwork(std::string data, URLFetcher::Delegate* delegate);
+
+ private:
+  const GURL remote_;
+  const URLRequestStatus status_;
+  const int http_response_code_;
+  const std::string data_;
+
+  static void CompleteFetch(URLFetcher::Delegate* delegate,
+                            const GURL remote,
+                            const URLRequestStatus status,
+                            const int http_response_code,
+                            const std::string data);
+
+  DISALLOW_COPY_AND_ASSIGN(MockAuthResponseHandler);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_MOCK_AUTH_RESPONSE_HANDLER_H_
diff --git a/chrome/browser/chromeos/login/pam_google_authenticator.cc b/chrome/browser/chromeos/login/pam_google_authenticator.cc
index 689fde3..ef344cbe 100644
--- a/chrome/browser/chromeos/login/pam_google_authenticator.cc
+++ b/chrome/browser/chromeos/login/pam_google_authenticator.cc
@@ -10,8 +10,12 @@
 #include "base/process_util.h"
 #include "chrome/browser/chromeos/login/pam_google_authenticator.h"
 #include "chrome/browser/chromeos/login/login_status_consumer.h"
+#include "chrome/browser/profile.h"
 
-bool PamGoogleAuthenticator::Authenticate(const std::string& username,
+namespace chromeos {
+
+bool PamGoogleAuthenticator::Authenticate(Profile* profile,
+                                          const std::string& username,
                                           const std::string& password) {
   base::ProcessHandle handle;
   std::vector<std::string> argv;
@@ -27,9 +31,21 @@
   bool ret = (base::WaitForExitCode(handle, &child_exit_code) &&
               child_exit_code == 0);
 
-  if (ret)
-    consumer_->OnLoginSuccess(username, std::vector<std::string>());
-  else
-    consumer_->OnLoginFailure("");
+  if (ret) {
+    username_ = username;
+    ChromeThread::PostTask(
+        ChromeThread::UI, FROM_HERE,
+        NewRunnableMethod(this,
+                          &PamGoogleAuthenticator::OnLoginSuccess,
+                          std::string()));
+  } else {
+    ChromeThread::PostTask(
+        ChromeThread::UI, FROM_HERE,
+        NewRunnableMethod(this,
+                          &PamGoogleAuthenticator::OnLoginFailure,
+                          std::string()));
+  }
   return ret;
 }
+
+}  // namespace chromeos
diff --git a/chrome/browser/chromeos/login/pam_google_authenticator.h b/chrome/browser/chromeos/login/pam_google_authenticator.h
index 688f0c3..bb5bd791 100644
--- a/chrome/browser/chromeos/login/pam_google_authenticator.h
+++ b/chrome/browser/chromeos/login/pam_google_authenticator.h
@@ -8,9 +8,14 @@
 #include <string>
 #include "chrome/browser/chromeos/login/authenticator.h"
 
+class Profile;
+
+namespace chromeos {
+
 class LoginStatusConsumer;
 
-// Authenticates a Chromium OS user against the Google Accounts ClientLogin API.
+// Authenticates a Chromium OS user against the Google Accounts ClientLogin API
+// using a setuid helper binary and a pre-installed pam module.
 
 class PamGoogleAuthenticator : public Authenticator {
  public:
@@ -22,11 +27,23 @@
   // Given a |username| and |password|, this method attempts to authenticate to
   // the Google accounts servers.
   // Returns true if the attempt gets sent successfully and false if not.
-  bool Authenticate(const std::string& username,
+  bool Authenticate(Profile* profile,
+                    const std::string& username,
                     const std::string& password);
 
+  void OnLoginSuccess(const std::string& credentials) {
+    consumer_->OnLoginSuccess(username_, credentials);
+  }
+
+  void OnLoginFailure(const std::string& data) {
+    consumer_->OnLoginFailure(data);
+  }
+
  private:
+  std::string username_;
   DISALLOW_COPY_AND_ASSIGN(PamGoogleAuthenticator);
 };
 
+}  // namespace chromeos
+
 #endif  // CHROME_BROWSER_CHROMEOS_LOGIN_PAM_GOOGLE_AUTHENTICATOR_H_
diff --git a/chrome/chrome_browser.gypi b/chrome/chrome_browser.gypi
index 05c0718..cedfbbe9 100755
--- a/chrome/chrome_browser.gypi
+++ b/chrome/chrome_browser.gypi
@@ -369,12 +369,14 @@
         'browser/chromeos/login/account_screen.cc',
         'browser/chromeos/login/account_screen.h',
         'browser/chromeos/login/authenticator.h',
-        'browser/chromeos/login/background_view.cc',
-        'browser/chromeos/login/background_view.h',
         'browser/chromeos/login/auth_response_handler.cc',
         'browser/chromeos/login/auth_response_handler.h',
+        'browser/chromeos/login/background_view.cc',
+        'browser/chromeos/login/background_view.h',
         'browser/chromeos/login/client_login_response_handler.cc',
         'browser/chromeos/login/client_login_response_handler.h',
+        'browser/chromeos/login/cookie_fetcher.cc',
+        'browser/chromeos/login/cookie_fetcher.h',
         'browser/chromeos/login/existing_user_controller.cc',
         'browser/chromeos/login/existing_user_controller.h',
         'browser/chromeos/login/issue_response_handler.cc',
diff --git a/chrome/chrome_tests.gypi b/chrome/chrome_tests.gypi
index 63870bb..bee3e7e1 100644
--- a/chrome/chrome_tests.gypi
+++ b/chrome/chrome_tests.gypi
@@ -588,7 +588,9 @@
         'browser/chromeos/external_cookie_handler_unittest.cc',
         'browser/chromeos/external_metrics_unittest.cc',
         'browser/chromeos/gview_request_interceptor_unittest.cc',
+        'browser/chromeos/login/cookie_fetcher_unittest.cc',
         'browser/chromeos/login/google_authenticator_unittest.cc',
+        'browser/chromeos/login/mock_auth_response_handler.cc',
         'browser/chromeos/options/language_config_view_test.cc',
         'browser/chromeos/pipe_reader_unittest.cc',
         'browser/chromeos/status/language_menu_l10n_util_unittest.cc',