blob: 7f8a2111d1e1f8d4b040bc847d791ea388866c39 [file] [log] [blame]
Adam Langley573d3ac2018-04-28 00:32:131// Copyright (c) 2018 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Sebastien Marchandf1349f52019-01-25 03:16:415#include "base/bind.h"
Guido Urdanetaef4e91942020-11-09 15:06:246#include "base/test/bind.h"
Adam Langley573d3ac2018-04-28 00:32:137#include "build/build_config.h"
8#include "chrome/browser/devtools/devtools_window_testing.h"
Adam Langley573d3ac2018-04-28 00:32:139#include "chrome/browser/ui/browser.h"
10#include "chrome/browser/ui/browser_commands.h"
Evan Stade9ce42602019-07-29 23:56:1411#include "chrome/browser/ui/browser_list.h"
Nina Satragno8db5a0d2019-04-08 14:37:3212#include "chrome/browser/webauthn/authenticator_request_scheduler.h"
13#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
Adam Langley573d3ac2018-04-28 00:32:1314#include "chrome/test/base/in_process_browser_test.h"
15#include "chrome/test/base/interactive_test_utils.h"
Evan Stade4487c452019-07-11 00:58:0616#include "chrome/test/base/ui_test_utils.h"
Adam Langley573d3ac2018-04-28 00:32:1317#include "components/network_session_configurator/common/network_switches.h"
Nina Satragnoacf403f92019-05-23 17:16:5218#include "content/public/browser/authenticator_environment.h"
Peter Kasting919ce652020-05-07 10:22:3619#include "content/public/test/browser_test.h"
Adam Langley573d3ac2018-04-28 00:32:1320#include "content/public/test/browser_test_utils.h"
Nina Satragnoacf403f92019-05-23 17:16:5221#include "device/fido/virtual_fido_device_factory.h"
Adam Langley573d3ac2018-04-28 00:32:1322#include "net/dns/mock_host_resolver.h"
23#include "net/test/embedded_test_server/embedded_test_server.h"
24#include "testing/gmock/include/gmock/gmock.h"
25
26namespace {
27
28class WebAuthFocusTest : public InProcessBrowserTest,
Nina Satragno8db5a0d2019-04-08 14:37:3229 public AuthenticatorRequestDialogModel::Observer {
Adam Langley573d3ac2018-04-28 00:32:1330 protected:
Nina Satragno8db5a0d2019-04-08 14:37:3231 WebAuthFocusTest()
32 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
33 permission_requested_(false) {}
Adam Langley573d3ac2018-04-28 00:32:1334
35 void SetUpOnMainThread() override {
36 host_resolver()->AddRule("*", "127.0.0.1");
37 https_server_.ServeFilesFromSourceDirectory("content/test/data");
38 ASSERT_TRUE(https_server_.Start());
39 }
40
41 GURL GetHttpsURL(const std::string& hostname,
42 const std::string& relative_url) {
43 return https_server_.GetURL(hostname, relative_url);
44 }
45
Nina Satragno8db5a0d2019-04-08 14:37:3246 bool permission_requested() { return permission_requested_; }
47
48 AuthenticatorRequestDialogModel* dialog_model_;
Adam Langley573d3ac2018-04-28 00:32:1349
50 private:
51 void SetUpCommandLine(base::CommandLine* command_line) override {
52 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
53 }
54
Nina Satragno8db5a0d2019-04-08 14:37:3255 // AuthenticatorRequestDialogModel::Observer:
56 void OnStepTransition() override {
57 if (dialog_model_->current_step() !=
58 AuthenticatorRequestDialogModel::Step::kAttestationPermissionRequest)
59 return;
60
61 // Simulate accepting the permission request.
62 dialog_model_->OnAttestationPermissionResponse(true);
63 permission_requested_ = true;
64 }
65
66 void OnModelDestroyed() override {}
67
Adam Langley573d3ac2018-04-28 00:32:1368 net::EmbeddedTestServer https_server_;
69
Nina Satragno8db5a0d2019-04-08 14:37:3270 // Set to true when the permission sheet is triggered.
71 bool permission_requested_;
72
Adam Langley573d3ac2018-04-28 00:32:1373 DISALLOW_COPY_AND_ASSIGN(WebAuthFocusTest);
74};
75
76IN_PROC_BROWSER_TEST_F(WebAuthFocusTest, Focus) {
77 // Web Authentication requests will often trigger machine-wide indications,
78 // such as a Security Key flashing for a touch. If background tabs were able
79 // to trigger this, there would be a risk of user confusion since the user
80 // would not know which tab they would be interacting with if they touched a
81 // Security Key. Because of that, some Web Authentication APIs require that
82 // the frame be in the foreground in a focused window.
83
84 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
85 ui_test_utils::NavigateToURL(browser(),
86 GetHttpsURL("www.example.com", "/title1.html"));
87
Nina Satragnoacf403f92019-05-23 17:16:5288 auto owned_virtual_device_factory =
89 std::make_unique<device::test::VirtualFidoDeviceFactory>();
90 auto* virtual_device_factory = owned_virtual_device_factory.get();
91 content::AuthenticatorEnvironment::GetInstance()
92 ->ReplaceDefaultDiscoveryFactoryForTesting(
93 std::move(owned_virtual_device_factory));
Adam Langley573d3ac2018-04-28 00:32:1394
95 constexpr char kRegisterTemplate[] =
96 "navigator.credentials.create({publicKey: {"
97 " rp: {name: 't'},"
98 " user: {id: new Uint8Array([1]), name: 't', displayName: 't'},"
99 " challenge: new Uint8Array([1,2,3,4]),"
100 " timeout: 10000,"
101 " attestation: '$1',"
102 " pubKeyCredParams: [{type: 'public-key', alg: -7}]"
103 "}}).then(c => window.domAutomationController.send('OK'),"
104 " e => window.domAutomationController.send(e.toString()));";
105 const std::string register_script = base::ReplaceStringPlaceholders(
106 kRegisterTemplate, std::vector<std::string>{"none"}, nullptr);
107
108 content::WebContents* const initial_web_contents =
109 browser()->tab_strip_model()->GetActiveWebContents();
110
111 std::string result;
112 // When operating in the foreground, the operation should succeed.
113 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
114 register_script, &result));
115 EXPECT_EQ(result, "OK");
116
117 // Open a new tab to put the previous page in the background.
118 chrome::NewTab(browser());
119
120 // When in the background, the same request should result in a focus error.
121 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
122 register_script, &result));
123 constexpr char kFocusErrorSubstring[] = "the page does not have focus";
124 EXPECT_THAT(result, ::testing::HasSubstr(kFocusErrorSubstring));
125
126 // Close the tab and the action should succeed again.
127 chrome::CloseTab(browser());
128 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
129 register_script, &result));
130 EXPECT_EQ(result, "OK");
131
132 // Start the request in the foreground and open a new tab between starting and
133 // finishing the request. This should fail because we don't want foreground
134 // pages to be able to start a request, open a trusted site in a new
135 // tab/window, and have the user believe that they are interacting with that
136 // trusted site.
Nina Satragnoacf403f92019-05-23 17:16:52137 virtual_device_factory->mutable_state()->simulate_press_callback =
Nina Satragno9d6389e2019-06-14 21:21:35138 base::BindRepeating(
139 [](Browser* browser, device::VirtualFidoDevice* device) {
140 chrome::NewTab(browser);
141 return true;
142 },
143 browser());
Adam Langley573d3ac2018-04-28 00:32:13144 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
145 register_script, &result));
146 EXPECT_THAT(result, ::testing::HasSubstr(kFocusErrorSubstring));
147
148 // Close the tab and the action should succeed again.
149 chrome::CloseTab(browser());
Nina Satragnoacf403f92019-05-23 17:16:52150 virtual_device_factory->mutable_state()->simulate_press_callback.Reset();
Adam Langley573d3ac2018-04-28 00:32:13151 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
152 register_script, &result));
153 EXPECT_EQ(result, "OK");
154
155 // Open dev tools and check that operations still succeed.
156 DevToolsWindow* dev_tools_window =
157 DevToolsWindowTesting::OpenDevToolsWindowSync(
158 initial_web_contents, true /* docked, not a separate window */);
159 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
160 register_script, &result));
161 EXPECT_EQ(result, "OK");
162 DevToolsWindowTesting::CloseDevToolsWindowSync(dev_tools_window);
163
164 // Open a second browser window.
Adam Langley573d3ac2018-04-28 00:32:13165 chrome::NewWindow(browser());
Evan Stade9ce42602019-07-29 23:56:14166 Browser* new_window = BrowserList::GetInstance()->GetLastActive();
Adam Langley573d3ac2018-04-28 00:32:13167 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(new_window));
168
Balazs Engedy9311dc32018-06-14 13:56:06169 // Operations in the (now unfocused) window should still succeed, as the
170 // calling tab is still the active tab in that window.
Adam Langley573d3ac2018-04-28 00:32:13171 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
172 register_script, &result));
Balazs Engedy9311dc32018-06-14 13:56:06173 EXPECT_THAT(result, "OK");
Adam Langley573d3ac2018-04-28 00:32:13174
175 // Check that closing the window brings things back to a focused state.
176 chrome::CloseWindow(new_window);
177 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
178 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
179 register_script, &result));
180 EXPECT_EQ(result, "OK");
181
182 // Requesting "direct" attestation will trigger a permissions prompt.
Nina Satragnoacf403f92019-05-23 17:16:52183 virtual_device_factory->mutable_state()->simulate_press_callback =
Nina Satragno9d6389e2019-06-14 21:21:35184 base::BindLambdaForTesting([&](device::VirtualFidoDevice* device) {
Nina Satragno8db5a0d2019-04-08 14:37:32185 dialog_model_ =
186 AuthenticatorRequestScheduler::GetRequestDelegateForTest(
187 initial_web_contents)
188 ->WeakDialogModelForTesting();
189 dialog_model_->AddObserver(this);
Nina Satragno9d6389e2019-06-14 21:21:35190 return true;
Nina Satragno8db5a0d2019-04-08 14:37:32191 });
192
Adam Langley573d3ac2018-04-28 00:32:13193 const std::string get_assertion_with_attestation_script =
194 base::ReplaceStringPlaceholders(
195 kRegisterTemplate, std::vector<std::string>{"direct"}, nullptr);
Adam Langley573d3ac2018-04-28 00:32:13196 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
197 initial_web_contents, get_assertion_with_attestation_script, &result));
Nina Satragno8db5a0d2019-04-08 14:37:32198
199 EXPECT_TRUE(permission_requested());
Adam Langley573d3ac2018-04-28 00:32:13200 EXPECT_EQ(result, "OK");
Adam Langley573d3ac2018-04-28 00:32:13201}
202
203} // anonymous namespace