| // 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); |
| } |