1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/signin/signin_manager.h"
8 #include "base/bind_helpers.h"
9 #include "base/compiler_specific.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/prefs/testing_pref_service.h"
12 #include "base/run_loop.h"
13 #include "base/strings/stringprintf.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/chrome_notification_types.h"
16 #include "chrome/browser/prefs/browser_prefs.h"
17 #include "chrome/browser/signin/chrome_signin_manager_delegate.h"
18 #include "chrome/browser/signin/signin_manager_factory.h"
19 #include "chrome/browser/signin/token_service.h"
20 #include "chrome/browser/signin/token_service_unittest.h"
21 #include "chrome/common/pref_names.h"
22 #include "chrome/common/url_constants.h"
23 #include "chrome/test/base/testing_browser_process.h"
24 #include "chrome/test/base/testing_profile.h"
25 #include "components/webdata/encryptor/encryptor.h"
26 #include "content/public/browser/child_process_security_policy.h"
27 #include "content/public/test/test_browser_thread_bundle.h"
28 #include "google_apis/gaia/gaia_constants.h"
29 #include "google_apis/gaia/gaia_urls.h"
30 #include "net/cookies/cookie_monster.h"
31 #include "net/url_request/test_url_fetcher_factory.h"
32 #include "net/url_request/url_request.h"
33 #include "net/url_request/url_request_context_getter.h"
34 #include "net/url_request/url_request_status.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "testing/gtest/include/gtest/gtest.h"
41 const char kGetTokenPairValidResponse[] =
43 " \"refresh_token\": \"rt1\","
44 " \"access_token\": \"at1\","
45 " \"expires_in\": 3600,"
46 " \"token_type\": \"Bearer\""
49 const char kUberAuthTokenURLFormat[] = "?source=%s&issueuberauth=1";
51 BrowserContextKeyedService* SigninManagerBuild(
52 content::BrowserContext* context) {
53 SigninManager* service = NULL;
54 Profile* profile = static_cast<Profile*>(context);
55 service = new SigninManager(
56 scoped_ptr<SigninManagerDelegate>(
57 new ChromeSigninManagerDelegate(profile)));
64 class SigninManagerTest : public TokenServiceTestHarness {
66 SigninManagerTest() : manager_(NULL) {}
67 virtual ~SigninManagerTest() {}
69 virtual void SetUp() OVERRIDE {
71 prefs_.reset(new TestingPrefServiceSimple);
72 chrome::RegisterLocalState(prefs_->registry());
73 TestingBrowserProcess::GetGlobal()->SetLocalState(
75 TokenServiceTestHarness::SetUp();
76 google_login_success_.ListenFor(
77 chrome::NOTIFICATION_GOOGLE_SIGNIN_SUCCESSFUL,
78 content::Source<Profile>(profile()));
79 google_login_failure_.ListenFor(chrome::NOTIFICATION_GOOGLE_SIGNIN_FAILED,
80 content::Source<Profile>(profile()));
83 virtual void TearDown() OVERRIDE {
84 // Destroy the SigninManager here, because it relies on profile() which is
85 // freed in the base class.
87 naked_manager_->Shutdown();
88 naked_manager_.reset(NULL);
90 TestingBrowserProcess::GetGlobal()->SetLocalState(NULL);
92 TokenServiceTestHarness::TearDown();
95 // Create a signin manager as a service if other code will try to get it as
97 void CreateSigninManagerAsService() {
99 DCHECK(!naked_manager_);
100 manager_ = static_cast<SigninManager*>(
101 SigninManagerFactory::GetInstance()->SetTestingFactoryAndUse(
102 profile(), SigninManagerBuild));
105 // Create a naked signin manager if integration with PKSs is not needed.
106 void CreateNakedSigninManager() {
108 naked_manager_.reset(new SigninManager(
109 scoped_ptr<SigninManagerDelegate>(
110 new ChromeSigninManagerDelegate(profile()))));
112 manager_ = naked_manager_.get();
115 void SetupFetcherAndComplete(const GURL& url,
117 const net::ResponseCookies& cookies,
118 const std::string& response_string) {
119 net::TestURLFetcher* fetcher = factory_.GetFetcherByID(0);
121 DCHECK(fetcher->delegate());
123 cookies_.insert(cookies_.end(), cookies.begin(), cookies.end());
124 fetcher->set_url(url);
125 fetcher->set_status(net::URLRequestStatus());
126 fetcher->set_response_code(response_code);
127 fetcher->SetResponseString(response_string);
128 fetcher->set_cookies(cookies);
129 fetcher->delegate()->OnURLFetchComplete(fetcher);
132 void SimulateValidResponseSignInWithCredentials() {
133 // Simulate the correct StartOAuthLoginTokenFetch response. This involves
134 // two separate fetches.
135 SetupFetcherAndComplete(
136 GaiaUrls::GetInstance()->client_login_to_oauth2_url(), 200,
137 net::ResponseCookies(), kGetTokenPairValidResponse);
139 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
140 net::ResponseCookies(), kGetTokenPairValidResponse);
142 // Simulate the correct StartOAuthLogin response.
143 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth1_login_url(), 200,
144 net::ResponseCookies(),
145 "SID=sid\nLSID=lsid\nAuth=auth_token");
147 SimulateValidResponseGetClientInfo(false);
150 void SimulateValidResponseClientLogin(bool isGPlusUser) {
151 SetupFetcherAndComplete(GaiaUrls::GetInstance()->client_login_url(), 200,
152 net::ResponseCookies(),
153 "SID=sid\nLSID=lsid\nAuth=auth");
154 SimulateValidResponseGetClientInfo(isGPlusUser);
157 void SimulateValidResponseGetClientInfo(bool isGPlusUser) {
158 // Simulate the correct ClientLogin response.
159 std::string response_string = isGPlusUser ?
160 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
161 "allServices=googleme" :
162 "email=user@gmail.com\ndisplayEmail=USER@gmail.com\n"
164 SetupFetcherAndComplete(GaiaUrls::GetInstance()->get_user_info_url(), 200,
165 net::ResponseCookies(), response_string);
168 void SimulateValidUberToken() {
169 SetupFetcherAndComplete(GaiaUrls::GetInstance()->oauth2_token_url(), 200,
170 net::ResponseCookies(), kGetTokenPairValidResponse);
171 const GURL uberauth_token_gurl =
172 GaiaUrls::GetInstance()->oauth1_login_url().Resolve(
173 base::StringPrintf(kUberAuthTokenURLFormat, "source"));
174 SetupFetcherAndComplete(uberauth_token_gurl, 200,
175 net::ResponseCookies(), "ut1");
177 net::ResponseCookies cookies;
178 cookies.push_back("checkCookie = true");
179 SetupFetcherAndComplete(GaiaUrls::GetInstance()->merge_session_url(), 200,
180 cookies, "<html></html>");
183 void ExpectSignInWithCredentialsSuccess() {
184 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
186 SimulateValidResponseSignInWithCredentials();
188 EXPECT_FALSE(manager_->GetAuthenticatedUsername().empty());
190 // This is flow, the oauth2 credentials should already be available in
191 // the token service.
192 EXPECT_TRUE(service()->HasOAuthLoginToken());
194 // Should go into token service and stop.
195 EXPECT_EQ(1U, google_login_success_.size());
196 EXPECT_EQ(0U, google_login_failure_.size());
199 // Helper method that wraps the logic when signin with credentials
200 // should fail. If |requestSent| is true, then simulate valid resopnse.
201 // Otherwise the sign-in is aborted before any request is sent, thus no need
202 // to simulatate response.
203 void ExpectSignInWithCredentialsFail(bool requestSent) {
204 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
207 SimulateValidResponseSignInWithCredentials();
209 // The oauth2 credentials should not be available in the token service
210 // because the email was incorrect.
211 EXPECT_FALSE(service()->HasOAuthLoginToken());
213 // Should go into token service and stop.
214 EXPECT_EQ(0U, google_login_success_.size());
215 EXPECT_EQ(1U, google_login_failure_.size());
218 void CompleteSigninCallback(const std::string& oauth_token) {
219 oauth_tokens_fetched_.push_back(oauth_token);
220 manager_->CompletePendingSignin();
223 void CancelSigninCallback(const std::string& oauth_token) {
224 oauth_tokens_fetched_.push_back(oauth_token);
228 net::TestURLFetcherFactory factory_;
229 scoped_ptr<SigninManager> naked_manager_;
230 SigninManager* manager_;
231 content::TestNotificationTracker google_login_success_;
232 content::TestNotificationTracker google_login_failure_;
233 std::vector<std::string> oauth_tokens_fetched_;
234 scoped_ptr<TestingPrefServiceSimple> prefs_;
235 std::vector<std::string> cookies_;
238 TEST_F(SigninManagerTest, SignInWithCredentials) {
239 CreateSigninManagerAsService();
240 manager_->Initialize(profile(), NULL);
241 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
243 manager_->StartSignInWithCredentials(
247 SigninManager::OAuthTokenFetchedCallback());
249 ExpectSignInWithCredentialsSuccess();
251 // Should persist across resets.
252 manager_->Shutdown();
254 CreateNakedSigninManager();
255 manager_->Initialize(profile(), NULL);
256 EXPECT_EQ("user@gmail.com", manager_->GetAuthenticatedUsername());
259 TEST_F(SigninManagerTest, SignInWithCredentialsNonCanonicalEmail) {
260 CreateSigninManagerAsService();
261 manager_->Initialize(profile(), NULL);
262 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
264 manager_->StartSignInWithCredentials(
268 SigninManager::OAuthTokenFetchedCallback());
270 ExpectSignInWithCredentialsSuccess();
273 TEST_F(SigninManagerTest, SignInWithCredentialsWrongEmail) {
274 CreateSigninManagerAsService();
275 manager_->Initialize(profile(), NULL);
276 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
278 // If the email address used to start the sign in does not match the
279 // email address returned by /GetUserInfo, the sign in should fail.
280 manager_->StartSignInWithCredentials(
284 SigninManager::OAuthTokenFetchedCallback());
286 ExpectSignInWithCredentialsFail(true /* requestSent */);
289 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordValidCookie) {
290 CreateSigninManagerAsService();
291 manager_->Initialize(profile(), NULL);
292 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
294 // Set a valid LSID cookie in the test cookie store.
295 scoped_refptr<net::CookieMonster> cookie_monster =
296 profile()->GetCookieMonster();
297 net::CookieOptions options;
298 options.set_include_httponly();
299 cookie_monster->SetCookieWithOptionsAsync(
300 GURL("https://accounts.google.com"),
301 "LSID=1234; secure; httponly", options,
302 net::CookieMonster::SetCookiesCallback());
304 // Since the password is empty, will verify the gaia cookies first.
305 manager_->StartSignInWithCredentials(
309 SigninManager::OAuthTokenFetchedCallback());
311 base::RunLoop().RunUntilIdle();
313 // Verification should succeed and continue with auto signin.
314 ExpectSignInWithCredentialsSuccess();
317 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordNoValidCookie) {
318 CreateSigninManagerAsService();
319 manager_->Initialize(profile(), NULL);
320 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
322 // Since the password is empty, will verify the gaia cookies first.
323 manager_->StartSignInWithCredentials(
327 SigninManager::OAuthTokenFetchedCallback());
329 base::RunLoop().RunUntilIdle();
331 // Since the test cookie store is empty, verification should fail and throws
333 ExpectSignInWithCredentialsFail(false /* requestSent */);
336 TEST_F(SigninManagerTest, SignInWithCredentialsEmptyPasswordInValidCookie) {
337 CreateSigninManagerAsService();
338 manager_->Initialize(profile(), NULL);
339 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
341 // Set an invalid LSID cookie in the test cookie store.
342 scoped_refptr<net::CookieMonster> cookie_monster =
343 profile()->GetCookieMonster();
344 net::CookieOptions options;
345 options.set_include_httponly();
346 cookie_monster->SetCookieWithOptionsAsync(
347 GURL("https://accounts.google.com"),
348 "LSID=1234; domain=google.com; secure; httponly", options,
349 net::CookieMonster::SetCookiesCallback());
351 // Since the password is empty, must verify the gaia cookies first.
352 manager_->StartSignInWithCredentials(
356 SigninManager::OAuthTokenFetchedCallback());
358 base::RunLoop().RunUntilIdle();
360 // Since the LSID cookie is invalid, verification should fail and throws
362 ExpectSignInWithCredentialsFail(false /* requestSent */);
365 TEST_F(SigninManagerTest, SignInWithCredentialsCallbackComplete) {
366 CreateSigninManagerAsService();
367 manager_->Initialize(profile(), NULL);
368 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
370 // Since the password is empty, must verify the gaia cookies first.
371 SigninManager::OAuthTokenFetchedCallback callback =
372 base::Bind(&SigninManagerTest::CompleteSigninCallback,
373 base::Unretained(this));
374 manager_->StartSignInWithCredentials(
380 ExpectSignInWithCredentialsSuccess();
381 ASSERT_EQ(1U, oauth_tokens_fetched_.size());
382 EXPECT_EQ(oauth_tokens_fetched_[0], "rt1");
385 TEST_F(SigninManagerTest, SignInWithCredentialsCallbackCancel) {
386 CreateSigninManagerAsService();
387 manager_->Initialize(profile(), NULL);
388 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
390 // Since the password is empty, must verify the gaia cookies first.
391 SigninManager::OAuthTokenFetchedCallback callback =
392 base::Bind(&SigninManagerTest::CancelSigninCallback,
393 base::Unretained(this));
394 manager_->StartSignInWithCredentials(
400 // Signin should fail since it would be cancelled by the callback.
401 ExpectSignInWithCredentialsFail(true);
402 ASSERT_EQ(1U, oauth_tokens_fetched_.size());
403 EXPECT_EQ(oauth_tokens_fetched_[0], "rt1");
406 TEST_F(SigninManagerTest, SignOut) {
407 CreateSigninManagerAsService();
408 manager_->Initialize(profile(), NULL);
409 SigninManager::OAuthTokenFetchedCallback dummy;
410 manager_->StartSignInWithCredentials("0", "user@gmail.com", "password",
412 ExpectSignInWithCredentialsSuccess();
415 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
416 // Should not be persisted anymore
417 manager_->Shutdown();
419 CreateNakedSigninManager();
420 manager_->Initialize(profile(), NULL);
421 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
424 TEST_F(SigninManagerTest, SignOutMidConnect) {
425 CreateSigninManagerAsService();
426 manager_->Initialize(profile(), NULL);
427 SigninManager::OAuthTokenFetchedCallback dummy;
428 manager_->StartSignInWithCredentials("0", "user@gmail.com", "password",
432 EXPECT_EQ(0U, google_login_success_.size());
433 EXPECT_EQ(1U, google_login_failure_.size());
435 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
436 EXPECT_TRUE(manager_->GetUsernameForAuthInProgress().empty());
439 TEST_F(SigninManagerTest, SignOutWhileProhibited) {
440 CreateSigninManagerAsService();
441 manager_->Initialize(profile(), NULL);
442 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
444 manager_->SetAuthenticatedUsername("user@gmail.com");
445 manager_->ProhibitSignout(true);
447 EXPECT_FALSE(manager_->GetAuthenticatedUsername().empty());
448 manager_->ProhibitSignout(false);
450 EXPECT_TRUE(manager_->GetAuthenticatedUsername().empty());
453 TEST_F(SigninManagerTest, TestIsWebBasedSigninFlowURL) {
454 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
455 GURL("http://www.google.com")));
456 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
457 GURL("https://accounts.google.com/ServiceLogin?service=chromiumsync")));
458 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
459 GURL("http://accounts.google.com/ServiceLogin?service=chromiumsync")));
460 // http, not https, should not be treated as web based signin.
461 EXPECT_FALSE(SigninManager::IsWebBasedSigninFlowURL(
462 GURL("http://accounts.google.com/ServiceLogin?service=googlemail")));
463 // chromiumsync is double-embedded in a continue query param.
464 EXPECT_TRUE(SigninManager::IsWebBasedSigninFlowURL(
465 GURL("https://accounts.google.com/CheckCookie?"
466 "continue=https%3A%2F%2Fwww.google.com%2Fintl%2Fen-US%2Fchrome"
467 "%2Fblank.html%3Fsource%3D3%26nonadv%3D1&service=chromiumsync")));
470 TEST_F(SigninManagerTest, Prohibited) {
471 g_browser_process->local_state()->SetString(
472 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
473 CreateNakedSigninManager();
474 manager_->Initialize(profile(), g_browser_process->local_state());
475 EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
476 EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
477 EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
478 EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
479 EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
482 TEST_F(SigninManagerTest, TestAlternateWildcard) {
483 // Test to make sure we accept "*@google.com" as a pattern (treat it as if
484 // the admin entered ".*@google.com").
485 g_browser_process->local_state()->SetString(
486 prefs::kGoogleServicesUsernamePattern, "*@google.com");
487 CreateNakedSigninManager();
488 manager_->Initialize(profile(), g_browser_process->local_state());
489 EXPECT_TRUE(manager_->IsAllowedUsername("test@google.com"));
490 EXPECT_TRUE(manager_->IsAllowedUsername("happy@google.com"));
491 EXPECT_FALSE(manager_->IsAllowedUsername("test@invalid.com"));
492 EXPECT_FALSE(manager_->IsAllowedUsername("test@notgoogle.com"));
493 EXPECT_FALSE(manager_->IsAllowedUsername(std::string()));
496 TEST_F(SigninManagerTest, ProhibitedAtStartup) {
497 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername,
498 "monkey@invalid.com");
499 g_browser_process->local_state()->SetString(
500 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
501 CreateNakedSigninManager();
502 manager_->Initialize(profile(), g_browser_process->local_state());
503 // Currently signed in user is prohibited by policy, so should be signed out.
504 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
507 TEST_F(SigninManagerTest, ProhibitedAfterStartup) {
508 std::string user("monkey@invalid.com");
509 profile()->GetPrefs()->SetString(prefs::kGoogleServicesUsername, user);
510 CreateNakedSigninManager();
511 manager_->Initialize(profile(), g_browser_process->local_state());
512 EXPECT_EQ(user, manager_->GetAuthenticatedUsername());
513 // Update the profile - user should be signed out.
514 g_browser_process->local_state()->SetString(
515 prefs::kGoogleServicesUsernamePattern, ".*@google.com");
516 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
519 TEST_F(SigninManagerTest, ExternalSignIn) {
520 CreateNakedSigninManager();
521 manager_->Initialize(profile(), g_browser_process->local_state());
523 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
524 EXPECT_EQ("", manager_->GetAuthenticatedUsername());
525 EXPECT_EQ(0u, google_login_success_.size());
527 manager_->OnExternalSigninCompleted("external@example.com");
528 EXPECT_EQ(1u, google_login_success_.size());
529 EXPECT_EQ(0u, google_login_failure_.size());
530 EXPECT_EQ("external@example.com",
531 profile()->GetPrefs()->GetString(prefs::kGoogleServicesUsername));
532 EXPECT_EQ("external@example.com", manager_->GetAuthenticatedUsername());