blob: 6f69443c9eb2ba1694bee6ae88c3ad0f9c6c7924 [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"
Keishi Hattori0e45c022021-11-27 09:25:526#include "base/memory/raw_ptr.h"
Guido Urdanetaef4e91942020-11-09 15:06:247#include "base/test/bind.h"
Adam Langley573d3ac2018-04-28 00:32:138#include "build/build_config.h"
9#include "chrome/browser/devtools/devtools_window_testing.h"
Adam Langley573d3ac2018-04-28 00:32:1310#include "chrome/browser/ui/browser.h"
11#include "chrome/browser/ui/browser_commands.h"
Evan Stade9ce42602019-07-29 23:56:1412#include "chrome/browser/ui/browser_list.h"
Nina Satragno8db5a0d2019-04-08 14:37:3213#include "chrome/browser/webauthn/authenticator_request_scheduler.h"
14#include "chrome/browser/webauthn/chrome_authenticator_request_delegate.h"
Adam Langley573d3ac2018-04-28 00:32:1315#include "chrome/test/base/in_process_browser_test.h"
16#include "chrome/test/base/interactive_test_utils.h"
Evan Stade4487c452019-07-11 00:58:0617#include "chrome/test/base/ui_test_utils.h"
Adam Langley573d3ac2018-04-28 00:32:1318#include "components/network_session_configurator/common/network_switches.h"
Nina Satragnoacf403f92019-05-23 17:16:5219#include "content/public/browser/authenticator_environment.h"
Peter Kasting919ce652020-05-07 10:22:3620#include "content/public/test/browser_test.h"
Adam Langley573d3ac2018-04-28 00:32:1321#include "content/public/test/browser_test_utils.h"
Nina Satragnoacf403f92019-05-23 17:16:5222#include "device/fido/virtual_fido_device_factory.h"
Adam Langley573d3ac2018-04-28 00:32:1323#include "net/dns/mock_host_resolver.h"
24#include "net/test/embedded_test_server/embedded_test_server.h"
25#include "testing/gmock/include/gmock/gmock.h"
26
27namespace {
28
29class WebAuthFocusTest : public InProcessBrowserTest,
Nina Satragno8db5a0d2019-04-08 14:37:3230 public AuthenticatorRequestDialogModel::Observer {
Adam Langley573d3ac2018-04-28 00:32:1331 protected:
Nina Satragno8db5a0d2019-04-08 14:37:3232 WebAuthFocusTest()
33 : https_server_(net::EmbeddedTestServer::TYPE_HTTPS),
34 permission_requested_(false) {}
Adam Langley573d3ac2018-04-28 00:32:1335
Peter Boströmfadb1752021-09-30 19:17:0136 WebAuthFocusTest(const WebAuthFocusTest&) = delete;
37 WebAuthFocusTest& operator=(const WebAuthFocusTest&) = delete;
38
Adam Langley573d3ac2018-04-28 00:32:1339 void SetUpOnMainThread() override {
40 host_resolver()->AddRule("*", "127.0.0.1");
41 https_server_.ServeFilesFromSourceDirectory("content/test/data");
42 ASSERT_TRUE(https_server_.Start());
43 }
44
45 GURL GetHttpsURL(const std::string& hostname,
46 const std::string& relative_url) {
47 return https_server_.GetURL(hostname, relative_url);
48 }
49
Nina Satragno8db5a0d2019-04-08 14:37:3250 bool permission_requested() { return permission_requested_; }
51
Keishi Hattori0e45c022021-11-27 09:25:5252 raw_ptr<AuthenticatorRequestDialogModel> dialog_model_;
Adam Langley573d3ac2018-04-28 00:32:1353
54 private:
55 void SetUpCommandLine(base::CommandLine* command_line) override {
56 command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
57 }
58
Nina Satragno8db5a0d2019-04-08 14:37:3259 // AuthenticatorRequestDialogModel::Observer:
60 void OnStepTransition() override {
61 if (dialog_model_->current_step() !=
62 AuthenticatorRequestDialogModel::Step::kAttestationPermissionRequest)
63 return;
64
65 // Simulate accepting the permission request.
66 dialog_model_->OnAttestationPermissionResponse(true);
67 permission_requested_ = true;
68 }
69
Nina Satragnodb5b1292021-02-19 20:33:3770 void OnModelDestroyed(AuthenticatorRequestDialogModel* model) override {}
Nina Satragno8db5a0d2019-04-08 14:37:3271
Adam Langley573d3ac2018-04-28 00:32:1372 net::EmbeddedTestServer https_server_;
73
Nina Satragno8db5a0d2019-04-08 14:37:3274 // Set to true when the permission sheet is triggered.
75 bool permission_requested_;
Adam Langley573d3ac2018-04-28 00:32:1376};
77
Takumi Fujimoto5a46d6912021-06-22 20:55:4778// TODO(crbug.com/1222768): Disabled for being flaky.
79IN_PROC_BROWSER_TEST_F(WebAuthFocusTest, DISABLED_Focus) {
Adam Langley573d3ac2018-04-28 00:32:1380 // Web Authentication requests will often trigger machine-wide indications,
81 // such as a Security Key flashing for a touch. If background tabs were able
82 // to trigger this, there would be a risk of user confusion since the user
83 // would not know which tab they would be interacting with if they touched a
84 // Security Key. Because of that, some Web Authentication APIs require that
85 // the frame be in the foreground in a focused window.
86
87 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
Lukasz Anforowiczb78290c2021-09-08 04:31:3888 ASSERT_TRUE(ui_test_utils::NavigateToURL(
89 browser(), GetHttpsURL("www.example.com", "/title1.html")));
Adam Langley573d3ac2018-04-28 00:32:1390
Nina Satragnoacf403f92019-05-23 17:16:5291 auto owned_virtual_device_factory =
92 std::make_unique<device::test::VirtualFidoDeviceFactory>();
93 auto* virtual_device_factory = owned_virtual_device_factory.get();
94 content::AuthenticatorEnvironment::GetInstance()
95 ->ReplaceDefaultDiscoveryFactoryForTesting(
96 std::move(owned_virtual_device_factory));
Adam Langley573d3ac2018-04-28 00:32:1397
98 constexpr char kRegisterTemplate[] =
99 "navigator.credentials.create({publicKey: {"
100 " rp: {name: 't'},"
101 " user: {id: new Uint8Array([1]), name: 't', displayName: 't'},"
102 " challenge: new Uint8Array([1,2,3,4]),"
103 " timeout: 10000,"
104 " attestation: '$1',"
105 " pubKeyCredParams: [{type: 'public-key', alg: -7}]"
106 "}}).then(c => window.domAutomationController.send('OK'),"
107 " e => window.domAutomationController.send(e.toString()));";
108 const std::string register_script = base::ReplaceStringPlaceholders(
109 kRegisterTemplate, std::vector<std::string>{"none"}, nullptr);
110
111 content::WebContents* const initial_web_contents =
112 browser()->tab_strip_model()->GetActiveWebContents();
113
114 std::string result;
115 // When operating in the foreground, the operation should succeed.
116 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
117 register_script, &result));
118 EXPECT_EQ(result, "OK");
119
120 // Open a new tab to put the previous page in the background.
121 chrome::NewTab(browser());
122
123 // When in the background, the same request should result in a focus error.
124 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
125 register_script, &result));
126 constexpr char kFocusErrorSubstring[] = "the page does not have focus";
127 EXPECT_THAT(result, ::testing::HasSubstr(kFocusErrorSubstring));
128
129 // Close the tab and the action should succeed again.
130 chrome::CloseTab(browser());
131 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
132 register_script, &result));
133 EXPECT_EQ(result, "OK");
134
135 // Start the request in the foreground and open a new tab between starting and
136 // finishing the request. This should fail because we don't want foreground
137 // pages to be able to start a request, open a trusted site in a new
138 // tab/window, and have the user believe that they are interacting with that
139 // trusted site.
Nina Satragnoacf403f92019-05-23 17:16:52140 virtual_device_factory->mutable_state()->simulate_press_callback =
Nina Satragno9d6389e2019-06-14 21:21:35141 base::BindRepeating(
142 [](Browser* browser, device::VirtualFidoDevice* device) {
143 chrome::NewTab(browser);
144 return true;
145 },
146 browser());
Adam Langley573d3ac2018-04-28 00:32:13147 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
148 register_script, &result));
149 EXPECT_THAT(result, ::testing::HasSubstr(kFocusErrorSubstring));
150
151 // Close the tab and the action should succeed again.
152 chrome::CloseTab(browser());
Nina Satragnoacf403f92019-05-23 17:16:52153 virtual_device_factory->mutable_state()->simulate_press_callback.Reset();
Adam Langley573d3ac2018-04-28 00:32:13154 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
155 register_script, &result));
156 EXPECT_EQ(result, "OK");
157
158 // Open dev tools and check that operations still succeed.
159 DevToolsWindow* dev_tools_window =
160 DevToolsWindowTesting::OpenDevToolsWindowSync(
161 initial_web_contents, true /* docked, not a separate window */);
162 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
163 register_script, &result));
164 EXPECT_EQ(result, "OK");
165 DevToolsWindowTesting::CloseDevToolsWindowSync(dev_tools_window);
166
167 // Open a second browser window.
Adam Langley573d3ac2018-04-28 00:32:13168 chrome::NewWindow(browser());
Evan Stade9ce42602019-07-29 23:56:14169 Browser* new_window = BrowserList::GetInstance()->GetLastActive();
Adam Langley573d3ac2018-04-28 00:32:13170 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(new_window));
171
Balazs Engedy9311dc32018-06-14 13:56:06172 // Operations in the (now unfocused) window should still succeed, as the
173 // calling tab is still the active tab in that window.
Adam Langley573d3ac2018-04-28 00:32:13174 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
175 register_script, &result));
Balazs Engedy9311dc32018-06-14 13:56:06176 EXPECT_THAT(result, "OK");
Adam Langley573d3ac2018-04-28 00:32:13177
178 // Check that closing the window brings things back to a focused state.
179 chrome::CloseWindow(new_window);
180 ASSERT_TRUE(ui_test_utils::BringBrowserWindowToFront(browser()));
181 ASSERT_TRUE(content::ExecuteScriptAndExtractString(initial_web_contents,
182 register_script, &result));
183 EXPECT_EQ(result, "OK");
184
185 // Requesting "direct" attestation will trigger a permissions prompt.
Nina Satragnoacf403f92019-05-23 17:16:52186 virtual_device_factory->mutable_state()->simulate_press_callback =
Nina Satragno9d6389e2019-06-14 21:21:35187 base::BindLambdaForTesting([&](device::VirtualFidoDevice* device) {
Nina Satragnodb5b1292021-02-19 20:33:37188 dialog_model_ = AuthenticatorRequestScheduler::GetRequestDelegate(
189 initial_web_contents)
190 ->dialog_model();
Nina Satragno8db5a0d2019-04-08 14:37:32191 dialog_model_->AddObserver(this);
Nina Satragno9d6389e2019-06-14 21:21:35192 return true;
Nina Satragno8db5a0d2019-04-08 14:37:32193 });
194
Adam Langley573d3ac2018-04-28 00:32:13195 const std::string get_assertion_with_attestation_script =
196 base::ReplaceStringPlaceholders(
197 kRegisterTemplate, std::vector<std::string>{"direct"}, nullptr);
Adam Langley573d3ac2018-04-28 00:32:13198 ASSERT_TRUE(content::ExecuteScriptAndExtractString(
199 initial_web_contents, get_assertion_with_attestation_script, &result));
Nina Satragno8db5a0d2019-04-08 14:37:32200
201 EXPECT_TRUE(permission_requested());
Adam Langley573d3ac2018-04-28 00:32:13202 EXPECT_EQ(result, "OK");
Adam Langley573d3ac2018-04-28 00:32:13203}
204
205} // anonymous namespace