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.
9 #include "base/metrics/field_trial.h"
10 #include "base/strings/utf_string_conversions.h"
11 #include "chrome/browser/chrome_notification_types.h"
12 #include "chrome/browser/prerender/prerender_manager.h"
13 #include "chrome/browser/ui/browser.h"
14 #include "chrome/browser/ui/browser_commands.h"
15 #include "chrome/browser/ui/login/login_prompt.h"
16 #include "chrome/browser/ui/login/login_prompt_test_utils.h"
17 #include "chrome/browser/ui/tabs/tab_strip_model.h"
18 #include "chrome/test/base/in_process_browser_test.h"
19 #include "chrome/test/base/ui_test_utils.h"
20 #include "content/public/browser/interstitial_page.h"
21 #include "content/public/browser/notification_details.h"
22 #include "content/public/browser/notification_source.h"
23 #include "content/public/browser/web_contents.h"
24 #include "content/public/test/browser_test_utils.h"
25 #include "content/public/test/test_navigation_observer.h"
26 #include "net/base/auth.h"
27 #include "net/dns/mock_host_resolver.h"
29 using content::NavigationController;
30 using content::OpenURLParams;
31 using content::Referrer;
35 class LoginPromptBrowserTest : public InProcessBrowserTest {
37 LoginPromptBrowserTest()
38 : bad_password_("incorrect"),
39 bad_username_("nouser"),
41 username_basic_("basicuser"),
42 username_digest_("digestuser") {
43 auth_map_["foo"] = AuthInfo("testuser", "foopassword");
44 auth_map_["bar"] = AuthInfo("testuser", "barpassword");
45 auth_map_["testrealm"] = AuthInfo(username_basic_, password_);
50 std::string username_;
51 std::string password_;
55 AuthInfo(const std::string& username,
56 const std::string& password)
57 : username_(username), password_(password) {}
60 typedef std::map<std::string, AuthInfo> AuthMap;
62 void SetAuthFor(LoginHandler* handler);
65 std::string bad_password_;
66 std::string bad_username_;
67 std::string password_;
68 std::string username_basic_;
69 std::string username_digest_;
72 void LoginPromptBrowserTest::SetAuthFor(LoginHandler* handler) {
73 const net::AuthChallengeInfo* challenge = handler->auth_info();
75 ASSERT_TRUE(challenge);
76 AuthMap::iterator i = auth_map_.find(challenge->realm);
77 EXPECT_TRUE(auth_map_.end() != i);
78 if (i != auth_map_.end()) {
79 const AuthInfo& info = i->second;
80 handler->SetAuth(base::UTF8ToUTF16(info.username_),
81 base::UTF8ToUTF16(info.password_));
85 class InterstitialObserver : public content::WebContentsObserver {
87 InterstitialObserver(content::WebContents* web_contents,
88 const base::Closure& attach_callback,
89 const base::Closure& detach_callback)
90 : WebContentsObserver(web_contents),
91 attach_callback_(attach_callback),
92 detach_callback_(detach_callback) {
95 virtual void DidAttachInterstitialPage() OVERRIDE {
96 attach_callback_.Run();
99 virtual void DidDetachInterstitialPage() OVERRIDE {
100 detach_callback_.Run();
104 base::Closure attach_callback_;
105 base::Closure detach_callback_;
107 DISALLOW_COPY_AND_ASSIGN(InterstitialObserver);
110 void WaitForInterstitialAttach(content::WebContents* web_contents) {
111 scoped_refptr<content::MessageLoopRunner> interstitial_attach_loop_runner(
112 new content::MessageLoopRunner);
113 InterstitialObserver observer(
115 interstitial_attach_loop_runner->QuitClosure(),
117 if (!content::InterstitialPage::GetInterstitialPage(web_contents))
118 interstitial_attach_loop_runner->Run();
121 const char kPrefetchAuthPage[] = "files/login/prefetch.html";
123 const char kMultiRealmTestPage[] = "files/login/multi_realm.html";
124 const int kMultiRealmTestRealmCount = 2;
126 const char kSingleRealmTestPage[] = "files/login/single_realm.html";
128 const char* kAuthBasicPage = "auth-basic";
129 const char* kAuthDigestPage = "auth-digest";
131 base::string16 ExpectedTitleFromAuth(const base::string16& username,
132 const base::string16& password) {
133 // The TestServer sets the title to username/password on successful login.
134 return username + base::UTF8ToUTF16("/") + password;
137 // Confirm that <link rel="prefetch"> targetting an auth required
138 // resource does not provide a login dialog. These types of requests
139 // should instead just cancel the auth.
141 // Unfortunately, this test doesn't assert on anything for its
142 // correctness. Instead, it relies on the auth dialog blocking the
143 // browser, and triggering a timeout to cause failure when the
144 // prefetch resource requires authorization.
145 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, PrefetchAuthCancels) {
146 ASSERT_TRUE(test_server()->Start());
148 GURL test_page = test_server()->GetURL(kPrefetchAuthPage);
150 class SetPrefetchForTest {
152 explicit SetPrefetchForTest(bool prefetch)
153 : old_prerender_mode_(prerender::PrerenderManager::GetMode()) {
154 std::string exp_group = prefetch ? "ExperimentYes" : "ExperimentNo";
155 base::FieldTrialList::CreateFieldTrial("Prefetch", exp_group);
156 // Disable prerender so this is just a prefetch of the top-level page.
157 prerender::PrerenderManager::SetMode(
158 prerender::PrerenderManager::PRERENDER_MODE_DISABLED);
161 ~SetPrefetchForTest() {
162 prerender::PrerenderManager::SetMode(old_prerender_mode_);
166 prerender::PrerenderManager::PrerenderManagerMode old_prerender_mode_;
167 } set_prefetch_for_test(true);
169 content::WebContents* contents =
170 browser()->tab_strip_model()->GetActiveWebContents();
171 NavigationController* controller = &contents->GetController();
172 LoginPromptBrowserTestObserver observer;
174 observer.Register(content::Source<NavigationController>(controller));
176 WindowedLoadStopObserver load_stop_waiter(controller, 1);
177 browser()->OpenURL(OpenURLParams(
178 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
181 load_stop_waiter.Wait();
182 EXPECT_TRUE(observer.handlers().empty());
183 EXPECT_TRUE(test_server()->Stop());
186 // Test that "Basic" HTTP authentication works.
187 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestBasicAuth) {
188 ASSERT_TRUE(test_server()->Start());
189 GURL test_page = test_server()->GetURL(kAuthBasicPage);
191 content::WebContents* contents =
192 browser()->tab_strip_model()->GetActiveWebContents();
193 NavigationController* controller = &contents->GetController();
194 LoginPromptBrowserTestObserver observer;
196 observer.Register(content::Source<NavigationController>(controller));
199 WindowedAuthNeededObserver auth_needed_waiter(controller);
200 browser()->OpenURL(OpenURLParams(
201 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
203 auth_needed_waiter.Wait();
206 ASSERT_FALSE(observer.handlers().empty());
208 WindowedAuthNeededObserver auth_needed_waiter(controller);
209 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
210 LoginHandler* handler = *observer.handlers().begin();
212 ASSERT_TRUE(handler);
213 handler->SetAuth(base::UTF8ToUTF16(bad_username_),
214 base::UTF8ToUTF16(bad_password_));
215 auth_supplied_waiter.Wait();
217 // The request should be retried after the incorrect password is
218 // supplied. This should result in a new AUTH_NEEDED notification
219 // for the same realm.
220 auth_needed_waiter.Wait();
223 ASSERT_EQ(1u, observer.handlers().size());
224 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
225 LoginHandler* handler = *observer.handlers().begin();
227 auth_supplied_waiter.Wait();
229 base::string16 expected_title =
230 ExpectedTitleFromAuth(base::ASCIIToUTF16("basicuser"),
231 base::ASCIIToUTF16("secret"));
232 content::TitleWatcher title_watcher(contents, expected_title);
233 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
236 // Test that "Digest" HTTP authentication works.
237 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestDigestAuth) {
238 ASSERT_TRUE(test_server()->Start());
239 GURL test_page = test_server()->GetURL(kAuthDigestPage);
241 content::WebContents* contents =
242 browser()->tab_strip_model()->GetActiveWebContents();
243 NavigationController* controller = &contents->GetController();
244 LoginPromptBrowserTestObserver observer;
246 observer.Register(content::Source<NavigationController>(controller));
249 WindowedAuthNeededObserver auth_needed_waiter(controller);
250 browser()->OpenURL(OpenURLParams(
251 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
253 auth_needed_waiter.Wait();
256 ASSERT_FALSE(observer.handlers().empty());
258 WindowedAuthNeededObserver auth_needed_waiter(controller);
259 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
260 LoginHandler* handler = *observer.handlers().begin();
262 ASSERT_TRUE(handler);
263 handler->SetAuth(base::UTF8ToUTF16(bad_username_),
264 base::UTF8ToUTF16(bad_password_));
265 auth_supplied_waiter.Wait();
267 // The request should be retried after the incorrect password is
268 // supplied. This should result in a new AUTH_NEEDED notification
269 // for the same realm.
270 auth_needed_waiter.Wait();
273 ASSERT_EQ(1u, observer.handlers().size());
274 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
275 LoginHandler* handler = *observer.handlers().begin();
277 base::string16 username(base::UTF8ToUTF16(username_digest_));
278 base::string16 password(base::UTF8ToUTF16(password_));
279 handler->SetAuth(username, password);
280 auth_supplied_waiter.Wait();
282 base::string16 expected_title = ExpectedTitleFromAuth(username, password);
283 content::TitleWatcher title_watcher(contents, expected_title);
284 EXPECT_EQ(expected_title, title_watcher.WaitAndGetTitle());
287 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestTwoAuths) {
288 ASSERT_TRUE(test_server()->Start());
290 content::WebContents* contents1 =
291 browser()->tab_strip_model()->GetActiveWebContents();
292 NavigationController* controller1 = &contents1->GetController();
293 LoginPromptBrowserTestObserver observer;
295 observer.Register(content::Source<NavigationController>(controller1));
298 ui_test_utils::NavigateToURLWithDisposition(
302 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
304 content::WebContents* contents2 =
305 browser()->tab_strip_model()->GetActiveWebContents();
306 ASSERT_NE(contents1, contents2);
307 NavigationController* controller2 = &contents2->GetController();
308 observer.Register(content::Source<NavigationController>(controller2));
311 WindowedAuthNeededObserver auth_needed_waiter(controller1);
312 contents1->OpenURL(OpenURLParams(
313 test_server()->GetURL(kAuthBasicPage), Referrer(),
314 CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false));
315 auth_needed_waiter.Wait();
319 WindowedAuthNeededObserver auth_needed_waiter(controller2);
320 contents2->OpenURL(OpenURLParams(
321 test_server()->GetURL(kAuthDigestPage), Referrer(),
322 CURRENT_TAB, content::PAGE_TRANSITION_TYPED, false));
323 auth_needed_waiter.Wait();
326 ASSERT_EQ(2u, observer.handlers().size());
328 LoginHandler* handler1 = *observer.handlers().begin();
329 LoginHandler* handler2 = *(++(observer.handlers().begin()));
331 base::string16 expected_title1 = ExpectedTitleFromAuth(
332 base::UTF8ToUTF16(username_basic_), base::UTF8ToUTF16(password_));
333 base::string16 expected_title2 = ExpectedTitleFromAuth(
334 base::UTF8ToUTF16(username_digest_), base::UTF8ToUTF16(password_));
335 content::TitleWatcher title_watcher1(contents1, expected_title1);
336 content::TitleWatcher title_watcher2(contents2, expected_title2);
338 handler1->SetAuth(base::UTF8ToUTF16(username_basic_),
339 base::UTF8ToUTF16(password_));
340 handler2->SetAuth(base::UTF8ToUTF16(username_digest_),
341 base::UTF8ToUTF16(password_));
343 EXPECT_EQ(expected_title1, title_watcher1.WaitAndGetTitle());
344 EXPECT_EQ(expected_title2, title_watcher2.WaitAndGetTitle());
347 // Test login prompt cancellation.
348 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, TestCancelAuth) {
349 ASSERT_TRUE(test_server()->Start());
350 GURL auth_page = test_server()->GetURL(kAuthBasicPage);
351 GURL no_auth_page_1 = test_server()->GetURL("a");
352 GURL no_auth_page_2 = test_server()->GetURL("b");
353 GURL no_auth_page_3 = test_server()->GetURL("c");
355 content::WebContents* contents =
356 browser()->tab_strip_model()->GetActiveWebContents();
357 NavigationController* controller = &contents->GetController();
359 LoginPromptBrowserTestObserver observer;
360 observer.Register(content::Source<NavigationController>(controller));
362 // First navigate to an unauthenticated page so we have something to
364 ui_test_utils::NavigateToURL(browser(), no_auth_page_1);
366 // Navigating while auth is requested is the same as cancelling.
368 // We need to wait for two LOAD_STOP events. One for auth_page and one for
370 WindowedLoadStopObserver load_stop_waiter(controller, 2);
371 WindowedAuthNeededObserver auth_needed_waiter(controller);
372 browser()->OpenURL(OpenURLParams(
373 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
375 auth_needed_waiter.Wait();
376 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
377 browser()->OpenURL(OpenURLParams(
378 no_auth_page_2, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
380 auth_cancelled_waiter.Wait();
381 load_stop_waiter.Wait();
382 EXPECT_TRUE(observer.handlers().empty());
385 // Try navigating backwards.
387 // As above, we wait for two LOAD_STOP events; one for each navigation.
388 WindowedLoadStopObserver load_stop_waiter(controller, 2);
389 WindowedAuthNeededObserver auth_needed_waiter(controller);
390 browser()->OpenURL(OpenURLParams(
391 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
393 auth_needed_waiter.Wait();
394 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
395 ASSERT_TRUE(chrome::CanGoBack(browser()));
396 chrome::GoBack(browser(), CURRENT_TAB);
397 auth_cancelled_waiter.Wait();
398 load_stop_waiter.Wait();
399 EXPECT_TRUE(observer.handlers().empty());
402 // Now add a page and go back, so we have something to go forward to.
403 ui_test_utils::NavigateToURL(browser(), no_auth_page_3);
405 WindowedLoadStopObserver load_stop_waiter(controller, 1);
406 chrome::GoBack(browser(), CURRENT_TAB); // Should take us to page 1
407 load_stop_waiter.Wait();
411 // We wait for two LOAD_STOP events; one for each navigation.
412 WindowedLoadStopObserver load_stop_waiter(controller, 2);
413 WindowedAuthNeededObserver auth_needed_waiter(controller);
414 browser()->OpenURL(OpenURLParams(
415 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
417 auth_needed_waiter.Wait();
418 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
419 ASSERT_TRUE(chrome::CanGoForward(browser()));
420 chrome::GoForward(browser(), CURRENT_TAB); // Should take us to page 3
421 auth_cancelled_waiter.Wait();
422 load_stop_waiter.Wait();
423 EXPECT_TRUE(observer.handlers().empty());
426 // Now test that cancelling works as expected.
428 WindowedLoadStopObserver load_stop_waiter(controller, 1);
429 WindowedAuthNeededObserver auth_needed_waiter(controller);
430 browser()->OpenURL(OpenURLParams(
431 auth_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
433 auth_needed_waiter.Wait();
434 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
435 LoginHandler* handler = *observer.handlers().begin();
436 ASSERT_TRUE(handler);
437 handler->CancelAuth();
438 auth_cancelled_waiter.Wait();
439 load_stop_waiter.Wait();
440 EXPECT_TRUE(observer.handlers().empty());
444 // Test handling of resources that require authentication even though
445 // the page they are included on doesn't. In this case we should only
446 // present the minimal number of prompts necessary for successfully
447 // displaying the page. First we check whether cancelling works as
449 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmCancellation) {
450 ASSERT_TRUE(test_server()->Start());
451 GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
453 content::WebContents* contents =
454 browser()->tab_strip_model()->GetActiveWebContents();
455 NavigationController* controller = &contents->GetController();
456 LoginPromptBrowserTestObserver observer;
458 observer.Register(content::Source<NavigationController>(controller));
460 WindowedLoadStopObserver load_stop_waiter(controller, 1);
463 WindowedAuthNeededObserver auth_needed_waiter(controller);
464 browser()->OpenURL(OpenURLParams(
465 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
467 auth_needed_waiter.Wait();
472 while (n_handlers < kMultiRealmTestRealmCount) {
473 WindowedAuthNeededObserver auth_needed_waiter(controller);
475 while (!observer.handlers().empty()) {
476 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
477 LoginHandler* handler = *observer.handlers().begin();
479 ASSERT_TRUE(handler);
481 handler->CancelAuth();
482 auth_cancelled_waiter.Wait();
485 if (n_handlers < kMultiRealmTestRealmCount)
486 auth_needed_waiter.Wait();
489 load_stop_waiter.Wait();
491 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
492 EXPECT_EQ(0, observer.auth_supplied_count());
493 EXPECT_LT(0, observer.auth_needed_count());
494 EXPECT_LT(0, observer.auth_cancelled_count());
495 EXPECT_TRUE(test_server()->Stop());
498 // Similar to the MultipleRealmCancellation test above, but tests
499 // whether supplying credentials work as exepcted.
500 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, MultipleRealmConfirmation) {
501 ASSERT_TRUE(test_server()->Start());
502 GURL test_page = test_server()->GetURL(kMultiRealmTestPage);
504 content::WebContents* contents =
505 browser()->tab_strip_model()->GetActiveWebContents();
506 NavigationController* controller = &contents->GetController();
507 LoginPromptBrowserTestObserver observer;
509 observer.Register(content::Source<NavigationController>(controller));
511 WindowedLoadStopObserver load_stop_waiter(controller, 1);
515 WindowedAuthNeededObserver auth_needed_waiter(controller);
517 browser()->OpenURL(OpenURLParams(
518 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
520 auth_needed_waiter.Wait();
523 while (n_handlers < kMultiRealmTestRealmCount) {
524 WindowedAuthNeededObserver auth_needed_waiter(controller);
526 while (!observer.handlers().empty()) {
527 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
528 LoginHandler* handler = *observer.handlers().begin();
530 ASSERT_TRUE(handler);
533 auth_supplied_waiter.Wait();
536 if (n_handlers < kMultiRealmTestRealmCount)
537 auth_needed_waiter.Wait();
540 load_stop_waiter.Wait();
542 EXPECT_EQ(kMultiRealmTestRealmCount, n_handlers);
543 EXPECT_LT(0, observer.auth_needed_count());
544 EXPECT_LT(0, observer.auth_supplied_count());
545 EXPECT_EQ(0, observer.auth_cancelled_count());
546 EXPECT_TRUE(test_server()->Stop());
549 // Testing for recovery from an incorrect password for the case where
550 // there are multiple authenticated resources.
551 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, IncorrectConfirmation) {
552 ASSERT_TRUE(test_server()->Start());
553 GURL test_page = test_server()->GetURL(kSingleRealmTestPage);
555 content::WebContents* contents =
556 browser()->tab_strip_model()->GetActiveWebContents();
557 NavigationController* controller = &contents->GetController();
558 LoginPromptBrowserTestObserver observer;
560 observer.Register(content::Source<NavigationController>(controller));
563 WindowedAuthNeededObserver auth_needed_waiter(controller);
564 browser()->OpenURL(OpenURLParams(
565 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
567 auth_needed_waiter.Wait();
570 EXPECT_FALSE(observer.handlers().empty());
572 if (!observer.handlers().empty()) {
573 WindowedAuthNeededObserver auth_needed_waiter(controller);
574 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
575 LoginHandler* handler = *observer.handlers().begin();
577 ASSERT_TRUE(handler);
578 handler->SetAuth(base::UTF8ToUTF16(bad_username_),
579 base::UTF8ToUTF16(bad_password_));
580 auth_supplied_waiter.Wait();
582 // The request should be retried after the incorrect password is
583 // supplied. This should result in a new AUTH_NEEDED notification
584 // for the same realm.
585 auth_needed_waiter.Wait();
590 while (n_handlers < 1) {
591 WindowedAuthNeededObserver auth_needed_waiter(controller);
593 while (!observer.handlers().empty()) {
594 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
595 LoginHandler* handler = *observer.handlers().begin();
597 ASSERT_TRUE(handler);
600 auth_supplied_waiter.Wait();
604 auth_needed_waiter.Wait();
607 // The single realm test has only one realm, and thus only one login
609 EXPECT_EQ(1, n_handlers);
610 EXPECT_LT(0, observer.auth_needed_count());
611 EXPECT_EQ(0, observer.auth_cancelled_count());
612 EXPECT_EQ(observer.auth_needed_count(), observer.auth_supplied_count());
613 EXPECT_TRUE(test_server()->Stop());
616 // If the favicon is an authenticated resource, we shouldn't prompt
617 // for credentials. The same URL, if requested elsewhere should
618 // prompt for credentials.
619 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, NoLoginPromptForFavicon) {
620 const char* kFaviconTestPage = "files/login/has_favicon.html";
621 const char* kFaviconResource = "auth-basic/favicon.gif";
623 ASSERT_TRUE(test_server()->Start());
625 content::WebContents* contents =
626 browser()->tab_strip_model()->GetActiveWebContents();
627 NavigationController* controller = &contents->GetController();
628 LoginPromptBrowserTestObserver observer;
630 observer.Register(content::Source<NavigationController>(controller));
632 // First load a page that has a favicon that requires
633 // authentication. There should be no login prompt.
635 GURL test_page = test_server()->GetURL(kFaviconTestPage);
636 WindowedLoadStopObserver load_stop_waiter(controller, 1);
637 browser()->OpenURL(OpenURLParams(
638 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
640 load_stop_waiter.Wait();
643 // Now request the same favicon, but directly as the document.
644 // There should be one login prompt.
646 GURL test_page = test_server()->GetURL(kFaviconResource);
647 WindowedLoadStopObserver load_stop_waiter(controller, 1);
648 WindowedAuthNeededObserver auth_needed_waiter(controller);
649 browser()->OpenURL(OpenURLParams(
650 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
652 auth_needed_waiter.Wait();
653 ASSERT_EQ(1u, observer.handlers().size());
655 while (!observer.handlers().empty()) {
656 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
657 LoginHandler* handler = *observer.handlers().begin();
659 ASSERT_TRUE(handler);
660 handler->CancelAuth();
661 auth_cancelled_waiter.Wait();
664 load_stop_waiter.Wait();
667 EXPECT_EQ(0, observer.auth_supplied_count());
668 EXPECT_EQ(1, observer.auth_needed_count());
669 EXPECT_EQ(1, observer.auth_cancelled_count());
670 EXPECT_TRUE(test_server()->Stop());
673 // Block crossdomain image login prompting as a phishing defense.
674 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
675 BlockCrossdomainPromptForSubresources) {
676 const char* kTestPage = "files/login/load_img_from_b.html";
678 host_resolver()->AddRule("www.a.com", "127.0.0.1");
679 host_resolver()->AddRule("www.b.com", "127.0.0.1");
680 ASSERT_TRUE(test_server()->Start());
682 content::WebContents* contents =
683 browser()->tab_strip_model()->GetActiveWebContents();
684 NavigationController* controller = &contents->GetController();
685 LoginPromptBrowserTestObserver observer;
686 observer.Register(content::Source<NavigationController>(controller));
688 // Load a page that has a cross-domain sub-resource authentication.
689 // There should be no login prompt.
691 GURL test_page = test_server()->GetURL(kTestPage);
692 ASSERT_EQ("127.0.0.1", test_page.host());
694 // Change the host from 127.0.0.1 to www.a.com so that when the
695 // page tries to load from b, it will be cross-origin.
696 std::string new_host("www.a.com");
697 GURL::Replacements replacements;
698 replacements.SetHostStr(new_host);
699 test_page = test_page.ReplaceComponents(replacements);
701 WindowedLoadStopObserver load_stop_waiter(controller, 1);
702 browser()->OpenURL(OpenURLParams(
703 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
705 load_stop_waiter.Wait();
708 EXPECT_EQ(0, observer.auth_needed_count());
710 // Now request the same page, but from the same origin.
711 // There should be one login prompt.
713 GURL test_page = test_server()->GetURL(kTestPage);
714 ASSERT_EQ("127.0.0.1", test_page.host());
716 // Change the host from 127.0.0.1 to www.b.com so that when the
717 // page tries to load from b, it will be same-origin.
718 std::string new_host("www.b.com");
719 GURL::Replacements replacements;
720 replacements.SetHostStr(new_host);
721 test_page = test_page.ReplaceComponents(replacements);
723 WindowedAuthNeededObserver auth_needed_waiter(controller);
724 browser()->OpenURL(OpenURLParams(
725 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
727 auth_needed_waiter.Wait();
728 ASSERT_EQ(1u, observer.handlers().size());
730 while (!observer.handlers().empty()) {
731 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
732 LoginHandler* handler = *observer.handlers().begin();
734 ASSERT_TRUE(handler);
735 handler->CancelAuth();
736 auth_cancelled_waiter.Wait();
740 EXPECT_EQ(1, observer.auth_needed_count());
741 EXPECT_TRUE(test_server()->Stop());
744 // Allow crossdomain iframe login prompting despite the above.
745 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
746 AllowCrossdomainPromptForSubframes) {
747 const char* kTestPage = "files/login/load_iframe_from_b.html";
749 host_resolver()->AddRule("www.a.com", "127.0.0.1");
750 host_resolver()->AddRule("www.b.com", "127.0.0.1");
751 ASSERT_TRUE(test_server()->Start());
753 content::WebContents* contents =
754 browser()->tab_strip_model()->GetActiveWebContents();
755 NavigationController* controller = &contents->GetController();
756 LoginPromptBrowserTestObserver observer;
757 observer.Register(content::Source<NavigationController>(controller));
759 // Load a page that has a cross-domain iframe authentication.
761 GURL test_page = test_server()->GetURL(kTestPage);
762 ASSERT_EQ("127.0.0.1", test_page.host());
764 // Change the host from 127.0.0.1 to www.a.com so that when the
765 // page tries to load from b, it will be cross-origin.
766 std::string new_host("www.a.com");
767 GURL::Replacements replacements;
768 replacements.SetHostStr(new_host);
769 test_page = test_page.ReplaceComponents(replacements);
771 WindowedAuthNeededObserver auth_needed_waiter(controller);
772 browser()->OpenURL(OpenURLParams(
773 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
775 auth_needed_waiter.Wait();
776 ASSERT_EQ(1u, observer.handlers().size());
778 while (!observer.handlers().empty()) {
779 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
780 LoginHandler* handler = *observer.handlers().begin();
782 ASSERT_TRUE(handler);
783 // When a cross origin iframe displays a login prompt, the blank
784 // interstitial shouldn't be displayed and the omnibox should show the
785 // main frame's url, not the iframe's.
786 EXPECT_EQ(new_host, contents->GetURL().host());
788 handler->CancelAuth();
789 auth_cancelled_waiter.Wait();
793 // Should stay on the main frame's url once the prompt the iframe is closed.
794 EXPECT_EQ("www.a.com", contents->GetURL().host());
796 EXPECT_EQ(1, observer.auth_needed_count());
797 EXPECT_TRUE(test_server()->Stop());
800 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, SupplyRedundantAuths) {
801 ASSERT_TRUE(test_server()->Start());
803 // Get NavigationController for tab 1.
804 content::WebContents* contents_1 =
805 browser()->tab_strip_model()->GetActiveWebContents();
806 NavigationController* controller_1 = &contents_1->GetController();
809 ui_test_utils::NavigateToURLWithDisposition(
813 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
815 // Get NavigationController for tab 2.
816 content::WebContents* contents_2 =
817 browser()->tab_strip_model()->GetActiveWebContents();
818 ASSERT_NE(contents_1, contents_2);
819 NavigationController* controller_2 = &contents_2->GetController();
821 LoginPromptBrowserTestObserver observer;
822 observer.Register(content::Source<NavigationController>(controller_1));
823 observer.Register(content::Source<NavigationController>(controller_2));
826 // Open different auth urls in each tab.
827 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
828 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
829 contents_1->OpenURL(OpenURLParams(
830 test_server()->GetURL("auth-basic/1"),
833 content::PAGE_TRANSITION_TYPED,
835 contents_2->OpenURL(OpenURLParams(
836 test_server()->GetURL("auth-basic/2"),
839 content::PAGE_TRANSITION_TYPED,
841 auth_needed_waiter_1.Wait();
842 auth_needed_waiter_2.Wait();
844 ASSERT_EQ(2U, observer.handlers().size());
846 // Supply auth in one of the tabs.
847 WindowedAuthSuppliedObserver auth_supplied_waiter_1(controller_1);
848 WindowedAuthSuppliedObserver auth_supplied_waiter_2(controller_2);
849 LoginHandler* handler_1 = *observer.handlers().begin();
850 ASSERT_TRUE(handler_1);
851 SetAuthFor(handler_1);
853 // Both tabs should be authenticated.
854 auth_supplied_waiter_1.Wait();
855 auth_supplied_waiter_2.Wait();
858 EXPECT_EQ(2, observer.auth_needed_count());
859 EXPECT_EQ(2, observer.auth_supplied_count());
860 EXPECT_EQ(0, observer.auth_cancelled_count());
861 EXPECT_TRUE(test_server()->Stop());
864 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest, CancelRedundantAuths) {
865 ASSERT_TRUE(test_server()->Start());
867 // Get NavigationController for tab 1.
868 content::WebContents* contents_1 =
869 browser()->tab_strip_model()->GetActiveWebContents();
870 NavigationController* controller_1 = &contents_1->GetController();
873 ui_test_utils::NavigateToURLWithDisposition(
877 ui_test_utils::BROWSER_TEST_WAIT_FOR_TAB);
879 // Get NavigationController for tab 2.
880 content::WebContents* contents_2 =
881 browser()->tab_strip_model()->GetActiveWebContents();
882 ASSERT_NE(contents_1, contents_2);
883 NavigationController* controller_2 = &contents_2->GetController();
885 LoginPromptBrowserTestObserver observer;
886 observer.Register(content::Source<NavigationController>(controller_1));
887 observer.Register(content::Source<NavigationController>(controller_2));
890 // Open different auth urls in each tab.
891 WindowedAuthNeededObserver auth_needed_waiter_1(controller_1);
892 WindowedAuthNeededObserver auth_needed_waiter_2(controller_2);
893 contents_1->OpenURL(OpenURLParams(
894 test_server()->GetURL("auth-basic/1"),
897 content::PAGE_TRANSITION_TYPED,
899 contents_2->OpenURL(OpenURLParams(
900 test_server()->GetURL("auth-basic/2"),
903 content::PAGE_TRANSITION_TYPED,
905 auth_needed_waiter_1.Wait();
906 auth_needed_waiter_2.Wait();
908 ASSERT_EQ(2U, observer.handlers().size());
910 // Cancel auth in one of the tabs.
911 WindowedAuthCancelledObserver auth_cancelled_waiter_1(controller_1);
912 WindowedAuthCancelledObserver auth_cancelled_waiter_2(controller_2);
913 LoginHandler* handler_1 = *observer.handlers().begin();
914 ASSERT_TRUE(handler_1);
915 handler_1->CancelAuth();
917 // Both tabs should cancel auth.
918 auth_cancelled_waiter_1.Wait();
919 auth_cancelled_waiter_2.Wait();
922 EXPECT_EQ(2, observer.auth_needed_count());
923 EXPECT_EQ(0, observer.auth_supplied_count());
924 EXPECT_EQ(2, observer.auth_cancelled_count());
925 EXPECT_TRUE(test_server()->Stop());
928 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
929 SupplyRedundantAuthsMultiProfile) {
930 ASSERT_TRUE(test_server()->Start());
932 // Get NavigationController for regular tab.
933 content::WebContents* contents =
934 browser()->tab_strip_model()->GetActiveWebContents();
935 NavigationController* controller = &contents->GetController();
937 // Open an incognito window.
938 Browser* browser_incognito = CreateIncognitoBrowser();
940 // Get NavigationController for incognito tab.
941 content::WebContents* contents_incognito =
942 browser_incognito->tab_strip_model()->GetActiveWebContents();
943 ASSERT_NE(contents, contents_incognito);
944 NavigationController* controller_incognito =
945 &contents_incognito->GetController();
947 LoginPromptBrowserTestObserver observer;
948 observer.Register(content::Source<NavigationController>(controller));
949 LoginPromptBrowserTestObserver observer_incognito;
950 observer_incognito.Register(
951 content::Source<NavigationController>(controller_incognito));
954 // Open an auth url in each window.
955 WindowedAuthNeededObserver auth_needed_waiter(controller);
956 WindowedAuthNeededObserver auth_needed_waiter_incognito(
957 controller_incognito);
958 contents->OpenURL(OpenURLParams(
959 test_server()->GetURL("auth-basic/1"),
962 content::PAGE_TRANSITION_TYPED,
964 contents_incognito->OpenURL(OpenURLParams(
965 test_server()->GetURL("auth-basic/2"),
968 content::PAGE_TRANSITION_TYPED,
970 auth_needed_waiter.Wait();
971 auth_needed_waiter_incognito.Wait();
973 ASSERT_EQ(1U, observer.handlers().size());
974 ASSERT_EQ(1U, observer_incognito.handlers().size());
976 // Supply auth in regular tab.
977 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
978 LoginHandler* handler = *observer.handlers().begin();
979 ASSERT_TRUE(handler);
982 // Regular tab should be authenticated.
983 auth_supplied_waiter.Wait();
985 // There's not really a way to wait for the incognito window to "do
986 // nothing". Run anything pending in the message loop just to be sure.
987 // (This shouldn't be necessary since notifications are synchronous, but
988 // maybe it will help avoid flake someday in the future..)
989 content::RunAllPendingInMessageLoop();
992 EXPECT_EQ(1, observer.auth_needed_count());
993 EXPECT_EQ(1, observer.auth_supplied_count());
994 EXPECT_EQ(0, observer.auth_cancelled_count());
995 EXPECT_EQ(1, observer_incognito.auth_needed_count());
996 EXPECT_EQ(0, observer_incognito.auth_supplied_count());
997 EXPECT_EQ(0, observer_incognito.auth_cancelled_count());
998 EXPECT_TRUE(test_server()->Stop());
1001 // If an XMLHttpRequest is made with incorrect credentials, there should be no
1002 // login prompt; instead the 401 status should be returned to the script.
1003 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1004 NoLoginPromptForXHRWithBadCredentials) {
1005 const char* kXHRTestPage = "files/login/xhr_with_credentials.html#incorrect";
1007 ASSERT_TRUE(test_server()->Start());
1009 content::WebContents* contents =
1010 browser()->tab_strip_model()->GetActiveWebContents();
1011 NavigationController* controller = &contents->GetController();
1012 LoginPromptBrowserTestObserver observer;
1014 observer.Register(content::Source<NavigationController>(controller));
1016 // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1017 // resource with the wrong credentials. There should be no login prompt.
1019 GURL test_page = test_server()->GetURL(kXHRTestPage);
1020 WindowedLoadStopObserver load_stop_waiter(controller, 1);
1021 browser()->OpenURL(OpenURLParams(
1022 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1024 load_stop_waiter.Wait();
1027 base::string16 expected_title(base::UTF8ToUTF16("status=401"));
1029 EXPECT_EQ(expected_title, contents->GetTitle());
1030 EXPECT_EQ(0, observer.auth_supplied_count());
1031 EXPECT_EQ(0, observer.auth_needed_count());
1032 EXPECT_EQ(0, observer.auth_cancelled_count());
1033 EXPECT_TRUE(test_server()->Stop());
1036 // If an XMLHttpRequest is made with correct credentials, there should be no
1037 // login prompt either.
1038 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1039 NoLoginPromptForXHRWithGoodCredentials) {
1040 const char* kXHRTestPage = "files/login/xhr_with_credentials.html#secret";
1042 ASSERT_TRUE(test_server()->Start());
1044 content::WebContents* contents =
1045 browser()->tab_strip_model()->GetActiveWebContents();
1046 NavigationController* controller = &contents->GetController();
1047 LoginPromptBrowserTestObserver observer;
1049 observer.Register(content::Source<NavigationController>(controller));
1051 // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1052 // resource with the wrong credentials. There should be no login prompt.
1054 GURL test_page = test_server()->GetURL(kXHRTestPage);
1055 WindowedLoadStopObserver load_stop_waiter(controller, 1);
1056 browser()->OpenURL(OpenURLParams(
1057 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1059 load_stop_waiter.Wait();
1062 base::string16 expected_title(base::UTF8ToUTF16("status=200"));
1064 EXPECT_EQ(expected_title, contents->GetTitle());
1065 EXPECT_EQ(0, observer.auth_supplied_count());
1066 EXPECT_EQ(0, observer.auth_needed_count());
1067 EXPECT_EQ(0, observer.auth_cancelled_count());
1068 EXPECT_TRUE(test_server()->Stop());
1071 // If an XMLHttpRequest is made without credentials, there should be a login
1073 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1074 LoginPromptForXHRWithoutCredentials) {
1075 const char* kXHRTestPage = "files/login/xhr_without_credentials.html";
1077 ASSERT_TRUE(test_server()->Start());
1079 content::WebContents* contents =
1080 browser()->tab_strip_model()->GetActiveWebContents();
1081 NavigationController* controller = &contents->GetController();
1082 LoginPromptBrowserTestObserver observer;
1084 observer.Register(content::Source<NavigationController>(controller));
1086 // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1087 // resource with the wrong credentials. There should be no login prompt.
1089 GURL test_page = test_server()->GetURL(kXHRTestPage);
1090 WindowedAuthNeededObserver auth_needed_waiter(controller);
1091 browser()->OpenURL(OpenURLParams(
1092 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1094 auth_needed_waiter.Wait();
1097 ASSERT_FALSE(observer.handlers().empty());
1099 WindowedAuthNeededObserver auth_needed_waiter(controller);
1100 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
1101 LoginHandler* handler = *observer.handlers().begin();
1103 ASSERT_TRUE(handler);
1104 handler->SetAuth(base::UTF8ToUTF16(bad_username_),
1105 base::UTF8ToUTF16(bad_password_));
1106 auth_supplied_waiter.Wait();
1108 // The request should be retried after the incorrect password is
1109 // supplied. This should result in a new AUTH_NEEDED notification
1110 // for the same realm.
1111 auth_needed_waiter.Wait();
1114 ASSERT_EQ(1u, observer.handlers().size());
1115 WindowedAuthSuppliedObserver auth_supplied_waiter(controller);
1116 LoginHandler* handler = *observer.handlers().begin();
1118 base::string16 username(base::UTF8ToUTF16(username_digest_));
1119 base::string16 password(base::UTF8ToUTF16(password_));
1120 handler->SetAuth(username, password);
1121 auth_supplied_waiter.Wait();
1123 WindowedLoadStopObserver load_stop_waiter(controller, 1);
1124 load_stop_waiter.Wait();
1126 base::string16 expected_title(base::UTF8ToUTF16("status=200"));
1128 EXPECT_EQ(expected_title, contents->GetTitle());
1129 EXPECT_EQ(2, observer.auth_supplied_count());
1130 EXPECT_EQ(2, observer.auth_needed_count());
1131 EXPECT_EQ(0, observer.auth_cancelled_count());
1132 EXPECT_TRUE(test_server()->Stop());
1135 // If an XMLHttpRequest is made without credentials, there should be a login
1136 // prompt. If it's cancelled, the script should get a 401 status.
1137 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1138 LoginPromptForXHRWithoutCredentialsCancelled) {
1139 const char* kXHRTestPage = "files/login/xhr_without_credentials.html";
1141 ASSERT_TRUE(test_server()->Start());
1143 content::WebContents* contents =
1144 browser()->tab_strip_model()->GetActiveWebContents();
1145 NavigationController* controller = &contents->GetController();
1146 LoginPromptBrowserTestObserver observer;
1148 observer.Register(content::Source<NavigationController>(controller));
1150 // Load a page which makes a synchronous XMLHttpRequest for an authenticated
1151 // resource with the wrong credentials. There should be no login prompt.
1153 GURL test_page = test_server()->GetURL(kXHRTestPage);
1154 WindowedAuthNeededObserver auth_needed_waiter(controller);
1155 browser()->OpenURL(OpenURLParams(
1156 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1158 auth_needed_waiter.Wait();
1161 ASSERT_EQ(1u, observer.handlers().size());
1162 WindowedAuthCancelledObserver auth_cancelled_waiter(controller);
1163 LoginHandler* handler = *observer.handlers().begin();
1165 handler->CancelAuth();
1166 auth_cancelled_waiter.Wait();
1168 WindowedLoadStopObserver load_stop_waiter(controller, 1);
1169 load_stop_waiter.Wait();
1171 base::string16 expected_title(base::UTF8ToUTF16("status=401"));
1173 EXPECT_EQ(expected_title, contents->GetTitle());
1174 EXPECT_EQ(0, observer.auth_supplied_count());
1175 EXPECT_EQ(1, observer.auth_needed_count());
1176 EXPECT_EQ(1, observer.auth_cancelled_count());
1177 EXPECT_TRUE(test_server()->Stop());
1180 // If a cross origin navigation triggers a login prompt, the destination URL
1181 // should be shown in the omnibox.
1182 IN_PROC_BROWSER_TEST_F(LoginPromptBrowserTest,
1183 ShowCorrectUrlForCrossOriginMainFrameRequests) {
1184 const char* kTestPage = "files/login/cross_origin.html";
1185 host_resolver()->AddRule("www.a.com", "127.0.0.1");
1186 ASSERT_TRUE(test_server()->Start());
1188 content::WebContents* contents =
1189 browser()->tab_strip_model()->GetActiveWebContents();
1190 NavigationController* controller = &contents->GetController();
1191 LoginPromptBrowserTestObserver observer;
1193 observer.Register(content::Source<NavigationController>(controller));
1195 // Load a page which navigates to a cross origin page with a login prompt.
1197 GURL test_page = test_server()->GetURL(kTestPage);
1198 ASSERT_EQ("127.0.0.1", test_page.host());
1200 WindowedAuthNeededObserver auth_needed_waiter(controller);
1201 browser()->OpenURL(OpenURLParams(
1202 test_page, Referrer(), CURRENT_TAB, content::PAGE_TRANSITION_TYPED,
1204 ASSERT_EQ("127.0.0.1", contents->GetURL().host());
1205 auth_needed_waiter.Wait();
1206 ASSERT_EQ(1u, observer.handlers().size());
1207 WaitForInterstitialAttach(contents);
1209 // The omnibox should show the correct origin for the new page when the
1210 // login prompt is shown.
1211 EXPECT_EQ("www.a.com", contents->GetURL().host());
1212 EXPECT_TRUE(contents->ShowingInterstitialPage());
1214 // Cancel and wait for the interstitial to detach.
1215 LoginHandler* handler = *observer.handlers().begin();
1216 scoped_refptr<content::MessageLoopRunner> loop_runner(
1217 new content::MessageLoopRunner);
1218 InterstitialObserver interstitial_observer(contents,
1220 loop_runner->QuitClosure());
1221 handler->CancelAuth();
1222 if (content::InterstitialPage::GetInterstitialPage(contents))
1224 EXPECT_EQ("www.a.com", contents->GetURL().host());
1225 EXPECT_FALSE(contents->ShowingInterstitialPage());