blob: 5bd0ef46cf4f274d63a8c860759e544d60cd5f56 [file] [log] [blame]
// Copyright 2019 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 <vector>
#include "base/files/file_util.h"
#include "base/files/scoped_temp_dir.h"
#include "chrome/browser/extensions/extension_service.h"
#include "chrome/browser/extensions/install_verifier.h"
#include "chrome/browser/extensions/test_extension_system.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/test/base/in_process_browser_test.h"
#include "chrome/test/base/ui_test_utils.h"
#include "content/public/browser/authenticator_environment.h"
#include "content/public/test/browser_test_utils.h"
#include "device/fido/virtual_fido_device_factory.h"
#include "extensions/common/extension_builder.h"
#include "url/gurl.h"
class WebAuthnBrowserTest : public InProcessBrowserTest {
public:
WebAuthnBrowserTest() = default;
private:
DISALLOW_COPY_AND_ASSIGN(WebAuthnBrowserTest);
};
IN_PROC_BROWSER_TEST_F(WebAuthnBrowserTest, ChromeExtensions) {
// Test that WebAuthn works inside of Chrome extensions. WebAuthn is based on
// Relying Party IDs, which are domain names. But Chrome extensions don't have
// domain names therefore the origin is used in their case.
//
// This test creates and installs an extension and then loads an HTML page
// from inside that extension. A WebAuthn call is injected into that context
// and it should get an assertion from a credential that's injected into the
// virtual authenticator, scoped to the origin string.
base::ScopedAllowBlockingForTesting allow_blocking;
extensions::ScopedInstallVerifierBypassForTest install_verifier_bypass;
base::ScopedTempDir temp_dir;
ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
static constexpr char kPageFile[] = "page.html";
std::vector<base::Value> resources;
resources.emplace_back(std::string(kPageFile));
static constexpr char kContents[] = R"(
<html>
<head>
<title>WebAuthn in extensions test</title>
</head>
<body>
</body>
</html>
)";
WriteFile(temp_dir.GetPath().AppendASCII(kPageFile), kContents,
sizeof(kContents) - 1);
extensions::ExtensionBuilder builder("test");
builder.SetPath(temp_dir.GetPath())
.SetVersion("1.0")
.SetLocation(extensions::Manifest::EXTERNAL_POLICY_DOWNLOAD)
.SetManifestKey("web_accessible_resources", std::move(resources));
extensions::ExtensionService* service =
extensions::ExtensionSystem::Get(browser()->profile())
->extension_service();
scoped_refptr<const extensions::Extension> extension = builder.Build();
service->OnExtensionInstalled(extension.get(), syncer::StringOrdinal(), 0);
auto virtual_device_factory =
std::make_unique<device::test::VirtualFidoDeviceFactory>();
const GURL url = extension->GetResourceURL(kPageFile);
auto extension_id = url.host();
static const uint8_t kCredentialID[] = {1, 2, 3, 4};
virtual_device_factory->mutable_state()->InjectRegistration(
kCredentialID, "chrome-extension://" + extension_id);
content::AuthenticatorEnvironment::GetInstance()
->ReplaceDefaultDiscoveryFactoryForTesting(
std::move(virtual_device_factory));
EXPECT_TRUE(ui_test_utils::NavigateToURL(browser(), url));
std::string result;
ASSERT_TRUE(content::ExecuteScriptAndExtractString(
browser()->tab_strip_model()->GetActiveWebContents(), R"((() => {
let cred_id = new Uint8Array([1,2,3,4]);
navigator.credentials.get({ publicKey: {
challenge: cred_id,
timeout: 10000,
userVerification: 'discouraged',
allowCredentials: [{type: 'public-key', id: cred_id}],
}}).then(c => window.domAutomationController.send('webauthn: OK'),
e => window.domAutomationController.send('error ' + e));
})())",
&result));
EXPECT_EQ("webauthn: OK", result);
}