blob: 58852d94a6c3e656bef9d48ba6bbbc003e63a124 [file] [log] [blame]
// Copyright 2018 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 <stdint.h>
#include <vector>
#include "base/command_line.h"
#include "base/macros.h"
#include "base/run_loop.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
#include "components/network_session_configurator/common/network_switches.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/webauth/authenticator_impl.h"
#include "content/public/browser/navigation_handle.h"
#include "content/public/browser/navigation_throttle.h"
#include "content/public/browser/web_contents.h"
#include "content/public/browser/web_contents_observer.h"
#include "content/public/common/content_features.h"
#include "content/public/common/content_switches.h"
#include "content/public/common/service_manager_connection.h"
#include "content/public/test/browser_test_utils.h"
#include "content/public/test/content_browser_test.h"
#include "content/public/test/content_browser_test_utils.h"
#include "content/shell/browser/shell.h"
#include "content/test/did_commit_provisional_load_interceptor.h"
#include "device/fido/fake_fido_discovery.h"
#include "device/fido/fake_hid_impl_for_testing.h"
#include "device/fido/test_callback_receiver.h"
#include "net/dns/mock_host_resolver.h"
#include "services/device/public/mojom/constants.mojom.h"
#include "services/service_manager/public/cpp/connector.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/blink/public/platform/modules/webauth/authenticator.mojom.h"
namespace content {
namespace {
using webauth::mojom::Authenticator;
using webauth::mojom::AuthenticatorPtr;
using webauth::mojom::AuthenticatorStatus;
using webauth::mojom::GetAssertionAuthenticatorResponsePtr;
using webauth::mojom::MakeCredentialAuthenticatorResponsePtr;
using TestCreateCallbackReceiver =
::device::test::StatusAndValueCallbackReceiver<
AuthenticatorStatus,
MakeCredentialAuthenticatorResponsePtr>;
using TestGetCallbackReceiver = ::device::test::StatusAndValueCallbackReceiver<
AuthenticatorStatus,
GetAssertionAuthenticatorResponsePtr>;
constexpr char kNotAllowedErrorMessage[] =
"NotAllowedError: The operation either timed out or was not allowed. See: "
"https://w3c.github.io/webauthn/#sec-assertion-privacy.";
constexpr char kRelyingPartySecurityErrorMessage[] =
"SecurityError: The relying party ID 'localhost' is not a registrable "
"domain suffix of, nor equal to 'https://www.example.com";
constexpr char kNotSupportedErrorMessage[] =
"NotSupportedError: Parameters for this operation are not supported.";
// Templates to be used with base::ReplaceStringPlaceholders. Can be
// modified to include up to 9 replacements. The default values for
// any additional replacements added should also be added to the
// CreateParameters struct.
constexpr char kCreatePublicKeyTemplate[] =
"navigator.credentials.create({ publicKey: {"
" challenge: new TextEncoder().encode('climb a mountain'),"
" rp: { id: '$3', name: 'Acme' },"
" user: { "
" id: new TextEncoder().encode('1098237235409872'),"
" name: 'avery.a.jones@example.com',"
" displayName: 'Avery A. Jones', "
" icon: 'https://pics.acme.com/00/p/aBjjjpqPb.png'},"
" pubKeyCredParams: [{ type: 'public-key', alg: '$4'}],"
" timeout: 60000,"
" excludeCredentials: [],"
" authenticatorSelection : {"
" requireResidentKey: $1,"
" userVerification: '$2',"
" authenticatorAttachment: '$5' }}"
"}).catch(c => window.domAutomationController.send(c.toString()));";
constexpr char kPlatform[] = "platform";
constexpr char kCrossPlatform[] = "cross-platform";
constexpr char kPreferredVerification[] = "preferred";
constexpr char kRequiredVerification[] = "required";
// Default values for kCreatePublicKeyTemplate.
struct CreateParameters {
const char* rp_id = "example.com";
bool require_resident_key = false;
const char* user_verification = kPreferredVerification;
const char* authenticator_attachment = kCrossPlatform;
const char* algorithm_identifier = "-7";
};
std::string BuildCreateCallWithParameters(const CreateParameters& parameters) {
std::vector<std::string> substititions;
substititions.push_back(parameters.require_resident_key ? "true" : "false");
substititions.push_back(parameters.user_verification);
substititions.push_back(parameters.rp_id);
substititions.push_back(parameters.algorithm_identifier);
substititions.push_back(parameters.authenticator_attachment);
return base::ReplaceStringPlaceholders(kCreatePublicKeyTemplate,
substititions, nullptr);
}
constexpr char kGetPublicKeyTemplate[] =
"navigator.credentials.get({ publicKey: {"
" challenge: new TextEncoder().encode('climb a mountain'),"
" rp: 'example.com',"
" timeout: 60000,"
" userVerification: '$1',"
" allowCredentials: [{ type: 'public-key',"
" id: new TextEncoder().encode('allowedCredential'),"
" transports: ['usb', 'nfc', 'ble']}] }"
"}).catch(c => window.domAutomationController.send(c.toString()));";
// Helper class that executes the given |closure| the very last moment before
// the next navigation commits in a given WebContents.
class ClosureExecutorBeforeNavigationCommit
: public DidCommitProvisionalLoadInterceptor {
public:
ClosureExecutorBeforeNavigationCommit(WebContents* web_contents,
base::OnceClosure closure)
: DidCommitProvisionalLoadInterceptor(web_contents),
closure_(std::move(closure)) {}
~ClosureExecutorBeforeNavigationCommit() override = default;
protected:
void WillDispatchDidCommitProvisionalLoad(
RenderFrameHost* render_frame_host,
::FrameHostMsg_DidCommitProvisionalLoad_Params* params,
service_manager::mojom::InterfaceProviderRequest*
interface_provider_request) override {
if (closure_)
std::move(closure_).Run();
}
private:
base::OnceClosure closure_;
DISALLOW_COPY_AND_ASSIGN(ClosureExecutorBeforeNavigationCommit);
};
// Cancels all navigations in a WebContents while in scope.
class ScopedNavigationCancellingThrottleInstaller : public WebContentsObserver {
public:
ScopedNavigationCancellingThrottleInstaller(WebContents* web_contents)
: WebContentsObserver(web_contents) {}
~ScopedNavigationCancellingThrottleInstaller() override = default;
protected:
class CancellingThrottle : public NavigationThrottle {
public:
CancellingThrottle(NavigationHandle* handle) : NavigationThrottle(handle) {}
~CancellingThrottle() override = default;
protected:
const char* GetNameForLogging() override {
return "ScopedNavigationCancellingThrottleInstaller::CancellingThrottle";
}
ThrottleCheckResult WillStartRequest() override {
return ThrottleCheckResult(CANCEL);
}
private:
DISALLOW_COPY_AND_ASSIGN(CancellingThrottle);
};
void DidStartNavigation(NavigationHandle* navigation_handle) override {
navigation_handle->RegisterThrottleForTesting(
std::make_unique<CancellingThrottle>(navigation_handle));
}
private:
DISALLOW_COPY_AND_ASSIGN(ScopedNavigationCancellingThrottleInstaller);
};
// Test fixture base class for common tasks.
class WebAuthBrowserTestBase : public content::ContentBrowserTest {
protected:
WebAuthBrowserTestBase()
: https_server_(net::EmbeddedTestServer::TYPE_HTTPS) {}
virtual std::vector<base::Feature> GetFeaturesToEnable() {
return {features::kWebAuth, features::kWebAuthBle};
}
void SetUpOnMainThread() override {
host_resolver()->AddRule("*", "127.0.0.1");
https_server().ServeFilesFromSourceDirectory("content/test/data");
ASSERT_TRUE(https_server().Start());
NavigateToURL(shell(), GetHttpsURL("www.example.com", "/title1.html"));
}
GURL GetHttpsURL(const std::string& hostname,
const std::string& relative_url) {
return https_server_.GetURL(hostname, relative_url);
}
net::EmbeddedTestServer& https_server() { return https_server_; }
device::test::ScopedFakeFidoDiscoveryFactory* discovery_factory() {
return &factory_;
}
private:
void SetUpCommandLine(base::CommandLine* command_line) override {
scoped_feature_list_.InitWithFeatures(GetFeaturesToEnable(), {});
command_line->AppendSwitch(
switches::kEnableExperimentalWebPlatformFeatures);
command_line->AppendSwitch(switches::kIgnoreCertificateErrors);
}
base::test::ScopedFeatureList scoped_feature_list_;
net::EmbeddedTestServer https_server_;
device::test::ScopedFakeFidoDiscoveryFactory factory_;
DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserTestBase);
};
} // namespace
// WebAuthLocalClientBrowserTest ----------------------------------------------
// Browser test fixture where the webauth::mojom::Authenticator interface is
// accessed from a testing client in the browser process.
class WebAuthLocalClientBrowserTest : public WebAuthBrowserTestBase {
public:
WebAuthLocalClientBrowserTest() = default;
~WebAuthLocalClientBrowserTest() override = default;
protected:
void SetUpOnMainThread() override {
WebAuthBrowserTestBase::SetUpOnMainThread();
ConnectToAuthenticator();
}
void ConnectToAuthenticator() {
auto* interface_provider =
static_cast<service_manager::mojom::InterfaceProvider*>(
static_cast<RenderFrameHostImpl*>(
shell()->web_contents()->GetMainFrame()));
interface_provider->GetInterface(
Authenticator::Name_,
mojo::MakeRequest(&authenticator_ptr_).PassMessagePipe());
}
webauth::mojom::PublicKeyCredentialCreationOptionsPtr
BuildBasicCreateOptions() {
auto rp = webauth::mojom::PublicKeyCredentialRpEntity::New(
"example.com", "example.com", base::nullopt);
std::vector<uint8_t> kTestUserId{0, 0, 0};
auto user = webauth::mojom::PublicKeyCredentialUserEntity::New(
kTestUserId, "name", base::nullopt, "displayName");
static constexpr int32_t kCOSEAlgorithmIdentifierES256 = -7;
auto param = webauth::mojom::PublicKeyCredentialParameters::New();
param->type = webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY;
param->algorithm_identifier = kCOSEAlgorithmIdentifierES256;
std::vector<webauth::mojom::PublicKeyCredentialParametersPtr> parameters;
parameters.push_back(std::move(param));
std::vector<uint8_t> kTestChallenge{0, 0, 0};
auto mojo_options = webauth::mojom::PublicKeyCredentialCreationOptions::New(
std::move(rp), std::move(user), kTestChallenge, std::move(parameters),
base::TimeDelta::FromSeconds(30),
std::vector<webauth::mojom::PublicKeyCredentialDescriptorPtr>(),
nullptr, webauth::mojom::AttestationConveyancePreference::NONE);
return mojo_options;
}
webauth::mojom::PublicKeyCredentialRequestOptionsPtr BuildBasicGetOptions() {
std::vector<webauth::mojom::PublicKeyCredentialDescriptorPtr> credentials;
std::vector<webauth::mojom::AuthenticatorTransport> transports;
transports.push_back(webauth::mojom::AuthenticatorTransport::USB);
std::vector<uint8_t> kCredentialId{0, 0, 0};
auto descriptor = webauth::mojom::PublicKeyCredentialDescriptor::New(
webauth::mojom::PublicKeyCredentialType::PUBLIC_KEY, kCredentialId,
transports);
credentials.push_back(std::move(descriptor));
std::vector<uint8_t> kTestChallenge{0, 0, 0};
auto mojo_options = webauth::mojom::PublicKeyCredentialRequestOptions::New(
kTestChallenge, base::TimeDelta::FromSeconds(30), "example.com",
std::move(credentials),
webauth::mojom::UserVerificationRequirement::PREFERRED, base::nullopt);
return mojo_options;
}
void WaitForConnectionError() {
ASSERT_TRUE(authenticator_ptr_);
ASSERT_TRUE(authenticator_ptr_.is_bound());
if (authenticator_ptr_.encountered_error())
return;
base::RunLoop run_loop;
authenticator_ptr_.set_connection_error_handler(run_loop.QuitClosure());
run_loop.Run();
}
AuthenticatorPtr& authenticator() { return authenticator_ptr_; }
private:
AuthenticatorPtr authenticator_ptr_;
DISALLOW_COPY_AND_ASSIGN(WebAuthLocalClientBrowserTest);
};
// Tests that no crash occurs when the implementation is destroyed with a
// pending navigator.credentials.create({publicKey: ...}) call.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
CreatePublicKeyCredentialThenNavigateAway) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestCreateCallbackReceiver create_callback_receiver;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
NavigateToURL(shell(), GetHttpsURL("www.example.com", "/title2.html"));
WaitForConnectionError();
// The next active document should be able to successfully call
// navigator.credentials.create({publicKey: ...}) again.
ConnectToAuthenticator();
fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
}
// Tests that no crash occurs when the implementation is destroyed with a
// pending navigator.credentials.get({publicKey: ...}) call.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
GetPublicKeyCredentialThenNavigateAway) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestGetCallbackReceiver get_callback_receiver;
authenticator()->GetAssertion(BuildBasicGetOptions(),
get_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
NavigateToURL(shell(), GetHttpsURL("www.example.com", "/title2.html"));
WaitForConnectionError();
// The next active document should be able to successfully call
// navigator.credentials.get({publicKey: ...}) again.
ConnectToAuthenticator();
fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
authenticator()->GetAssertion(BuildBasicGetOptions(),
get_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
}
// Tests that the webauth::mojom::Authenticator connection is not closed on a
// cancelled navigation.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
CreatePublicKeyCredentialAfterCancelledNavigation) {
ScopedNavigationCancellingThrottleInstaller navigation_canceller(
shell()->web_contents());
NavigateToURL(shell(), GetHttpsURL("www.example.com", "/title2.html"));
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestCreateCallbackReceiver create_callback_receiver;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
}
// Tests that a navigator.credentials.create({publicKey: ...}) issued at the
// moment just before a navigation commits is not serviced.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
CreatePublicKeyCredentialRacingWithNavigation) {
TestCreateCallbackReceiver create_callback_receiver;
auto request_options = BuildBasicCreateOptions();
ClosureExecutorBeforeNavigationCommit executor(
shell()->web_contents(), base::BindLambdaForTesting([&]() {
authenticator()->MakeCredential(std::move(request_options),
create_callback_receiver.callback());
}));
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
NavigateToURL(shell(), GetHttpsURL("www.example.com", "/title2.html"));
WaitForConnectionError();
// Normally, when the request is serviced, the implementation retrieves the
// factory as one of the first steps. Here, the request should not have been
// serviced at all, so the fake request should still be pending on the fake
// factory.
auto hid_discovery = ::device::FidoDiscovery::Create(
::device::U2fTransportProtocol::kUsbHumanInterfaceDevice, nullptr);
ASSERT_TRUE(!!hid_discovery);
// The next active document should be able to successfully call
// navigator.credentials.create({publicKey: ...}) again.
ConnectToAuthenticator();
fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
}
// Regression test for https://crbug.com/818219.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
CreatePublicKeyCredentialTwiceInARow) {
TestCreateCallbackReceiver callback_receiver_1;
TestCreateCallbackReceiver callback_receiver_2;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
callback_receiver_1.callback());
authenticator()->MakeCredential(BuildBasicCreateOptions(),
callback_receiver_2.callback());
callback_receiver_2.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver_2.status());
EXPECT_FALSE(callback_receiver_1.was_called());
}
// Regression test for https://crbug.com/818219.
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
GetPublicKeyCredentialTwiceInARow) {
TestGetCallbackReceiver callback_receiver_1;
TestGetCallbackReceiver callback_receiver_2;
authenticator()->GetAssertion(BuildBasicGetOptions(),
callback_receiver_1.callback());
authenticator()->GetAssertion(BuildBasicGetOptions(),
callback_receiver_2.callback());
callback_receiver_2.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver_2.status());
EXPECT_FALSE(callback_receiver_1.was_called());
}
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
CreatePublicKeyCredentialWhileRequestIsPending) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestCreateCallbackReceiver callback_receiver_1;
TestCreateCallbackReceiver callback_receiver_2;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
callback_receiver_1.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
authenticator()->MakeCredential(BuildBasicCreateOptions(),
callback_receiver_2.callback());
callback_receiver_2.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver_2.status());
EXPECT_FALSE(callback_receiver_1.was_called());
}
IN_PROC_BROWSER_TEST_F(WebAuthLocalClientBrowserTest,
GetPublicKeyCredentialWhileRequestIsPending) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
TestGetCallbackReceiver callback_receiver_1;
TestGetCallbackReceiver callback_receiver_2;
authenticator()->GetAssertion(BuildBasicGetOptions(),
callback_receiver_1.callback());
fake_hid_discovery->WaitForCallToStartAndSimulateSuccess();
authenticator()->GetAssertion(BuildBasicGetOptions(),
callback_receiver_2.callback());
callback_receiver_2.WaitForCallback();
EXPECT_EQ(AuthenticatorStatus::PENDING_REQUEST, callback_receiver_2.status());
EXPECT_FALSE(callback_receiver_1.was_called());
}
// WebAuthJavascriptClientBrowserTest -----------------------------------------
// Browser test fixture where the webauth::mojom::Authenticator interface is
// normally accessed from Javascript in the renderer process.
class WebAuthJavascriptClientBrowserTest : public WebAuthBrowserTestBase {
public:
WebAuthJavascriptClientBrowserTest() = default;
~WebAuthJavascriptClientBrowserTest() override = default;
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(WebAuthJavascriptClientBrowserTest);
};
// Tests that when navigator.credentials.create() is called with user
// verification required we get a NotAllowedError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
CreatePublicKeyCredentialWithUserVerification) {
CreateParameters parameters;
parameters.user_verification = kRequiredVerification;
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(),
BuildCreateCallWithParameters(parameters), &result));
ASSERT_EQ(kNotAllowedErrorMessage, result);
}
// Tests that when navigator.credentials.create() is called with resident key
// required, we get a NotAllowedError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
CreatePublicKeyCredentialWithResidentKeyRequired) {
CreateParameters parameters;
parameters.require_resident_key = true;
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(),
BuildCreateCallWithParameters(parameters), &result));
ASSERT_EQ(kNotAllowedErrorMessage, result);
}
// Tests that when navigator.credentials.get() is called with user verification
// required, we get a NotAllowedError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
GetPublicKeyCredentialUserVerification) {
const std::string kScript = base::ReplaceStringPlaceholders(
kGetPublicKeyTemplate, {"required"}, nullptr);
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(), kScript, &result));
ASSERT_EQ(kNotAllowedErrorMessage, result);
}
// Tests that when navigator.credentials.create() is called with an invalid
// relying party id, we get a SecurityError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
CreatePublicKeyCredentialInvalidRp) {
CreateParameters parameters;
parameters.rp_id = "localhost";
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(),
BuildCreateCallWithParameters(parameters), &result));
ASSERT_EQ(kRelyingPartySecurityErrorMessage,
result.substr(0, strlen(kRelyingPartySecurityErrorMessage)));
}
// Tests that when navigator.credentials.create() is called with an
// unsupported algorithm, we get a NotSupportedError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
CreatePublicKeyCredentialAlgorithmNotSupported) {
CreateParameters parameters;
parameters.algorithm_identifier = "123";
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(),
BuildCreateCallWithParameters(parameters), &result));
ASSERT_EQ(kNotSupportedErrorMessage, result);
}
// Tests that when navigator.credentials.create() is called with a
// platform authenticator requested, we get a NotAllowedError.
IN_PROC_BROWSER_TEST_F(WebAuthJavascriptClientBrowserTest,
CreatePublicKeyCredentialPlatformAuthenticator) {
CreateParameters parameters;
parameters.authenticator_attachment = kPlatform;
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
shell()->web_contents()->GetMainFrame(),
BuildCreateCallWithParameters(parameters), &result));
ASSERT_EQ(kNotAllowedErrorMessage, result);
}
// WebAuthBrowserBleDisabledTest
// ----------------------------------------------
// A test fixture that does not enable BLE discovery.
class WebAuthBrowserBleDisabledTest : public WebAuthLocalClientBrowserTest {
public:
WebAuthBrowserBleDisabledTest() = default;
protected:
std::vector<base::Feature> GetFeaturesToEnable() override {
return {features::kWebAuth};
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
DISALLOW_COPY_AND_ASSIGN(WebAuthBrowserBleDisabledTest);
};
// Tests that the BLE discovery does not start when the WebAuthnBle feature
// flag is disabled.
IN_PROC_BROWSER_TEST_F(WebAuthBrowserBleDisabledTest, CheckBleDisabled) {
auto* fake_hid_discovery = discovery_factory()->ForgeNextHidDiscovery();
auto* fake_ble_discovery = discovery_factory()->ForgeNextBleDiscovery();
// Do something that will start discoveries.
TestCreateCallbackReceiver create_callback_receiver;
authenticator()->MakeCredential(BuildBasicCreateOptions(),
create_callback_receiver.callback());
fake_hid_discovery->WaitForCallToStart();
EXPECT_TRUE(fake_hid_discovery->is_start_requested());
EXPECT_FALSE(fake_ble_discovery->is_start_requested());
}
} // namespace content