Commit 27190349 authored by Nina Satragno's avatar Nina Satragno Committed by Commit Bot

[webauthn] Add WebAuthn DevTools Domain.

Add a DevTools Domain that allows enabling and disabling the WebAuthn Virtual
Authenticator Environment. Eventually the domain will have a set of methods to
add and interact with authenticators.

This patch also removes a race condition from the
devtools/agents-enable-disable.js web test that was made apparent by adding this
domain. Domains' `.disable` methods are no longer assumed to be finish in the
same order as their `.enable` methods.

For an overview of the overall design, please see
https://docs.google.com/document/d/1bp2cMgjm2HSpvL9-WsJoIQMsBi1oKGQY6CvWD-9WmIQ/edit?usp=sharing

Bug: 922572
Change-Id: Id43c184e5830de72736410049fdacba9a965a6d3
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1617550
Commit-Queue: Nina Satragno <nsatragno@chromium.org>
Reviewed-by: default avatarAndrey Kosyakov <caseq@chromium.org>
Reviewed-by: default avatarKim Paulhamus <kpaulhamus@chromium.org>
Reviewed-by: default avatarAlexei Filippov <alph@chromium.org>
Cr-Commit-Position: refs/heads/master@{#664392}
parent 31c95bcd
...@@ -2496,9 +2496,15 @@ jumbo_source_set("browser") { ...@@ -2496,9 +2496,15 @@ jumbo_source_set("browser") {
} else { } else {
# Not Android. # Not Android.
sources += [ sources += [
# The WebAuthn devtools protocol API is not supported in Android yet.
"$target_gen_dir/devtools/protocol/web_authn.cc",
"$target_gen_dir/devtools/protocol/web_authn.h",
# Devtools frontend not included in Android # Devtools frontend not included in Android
"devtools/devtools_frontend_host_impl.cc", "devtools/devtools_frontend_host_impl.cc",
"devtools/devtools_frontend_host_impl.h", "devtools/devtools_frontend_host_impl.h",
"devtools/protocol/webauthn_handler.cc",
"devtools/protocol/webauthn_handler.h",
"host_zoom_level_context.cc", "host_zoom_level_context.cc",
"host_zoom_level_context.h", "host_zoom_level_context.h",
"host_zoom_map_impl.cc", "host_zoom_map_impl.cc",
......
...@@ -123,6 +123,8 @@ inspector_protocol_generate("protocol_sources") { ...@@ -123,6 +123,8 @@ inspector_protocol_generate("protocol_sources") {
"protocol/tethering.h", "protocol/tethering.h",
"protocol/tracing.cc", "protocol/tracing.cc",
"protocol/tracing.h", "protocol/tracing.h",
"protocol/web_authn.cc",
"protocol/web_authn.h",
] ]
} }
......
// 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 "content/browser/devtools/protocol/webauthn_handler.h"
#include "content/browser/frame_host/render_frame_host_impl.h"
#include "content/browser/webauth/authenticator_environment_impl.h"
namespace content {
namespace protocol {
WebAuthnHandler::WebAuthnHandler()
: DevToolsDomainHandler(WebAuthn::Metainfo::domainName) {}
WebAuthnHandler::~WebAuthnHandler() = default;
void WebAuthnHandler::SetRenderer(int process_host_id,
RenderFrameHostImpl* frame_host) {
frame_host_ = frame_host;
}
void WebAuthnHandler::Wire(UberDispatcher* dispatcher) {
WebAuthn::Dispatcher::wire(dispatcher, this);
}
Response WebAuthnHandler::Enable() {
AuthenticatorEnvironmentImpl::GetInstance()->EnableVirtualAuthenticatorFor(
frame_host_);
return Response::OK();
}
Response WebAuthnHandler::Disable() {
AuthenticatorEnvironmentImpl::GetInstance()->DisableVirtualAuthenticatorFor(
frame_host_);
return Response::OK();
}
} // namespace protocol
} // namespace content
// 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.
#ifndef CONTENT_BROWSER_DEVTOOLS_PROTOCOL_WEBAUTHN_HANDLER_H_
#define CONTENT_BROWSER_DEVTOOLS_PROTOCOL_WEBAUTHN_HANDLER_H_
#include "base/macros.h"
#include "content/browser/devtools/protocol/devtools_domain_handler.h"
#include "content/browser/devtools/protocol/web_authn.h"
namespace content {
namespace protocol {
class WebAuthnHandler : public DevToolsDomainHandler, public WebAuthn::Backend {
public:
WebAuthnHandler();
~WebAuthnHandler() override;
// DevToolsDomainHandler:
void SetRenderer(int process_host_id,
RenderFrameHostImpl* frame_host) override;
void Wire(UberDispatcher* dispatcher) override;
// WebAuthn::Backend
Response Enable() override;
Response Disable() override;
private:
RenderFrameHostImpl* frame_host_;
DISALLOW_COPY_AND_ASSIGN(WebAuthnHandler);
};
} // namespace protocol
} // namespace content
#endif // CONTENT_BROWSER_DEVTOOLS_PROTOCOL_WEBAUTHN_HANDLER_H_
...@@ -99,6 +99,10 @@ ...@@ -99,6 +99,10 @@
{ {
"domain": "Fetch", "domain": "Fetch",
"async": ["enable", "continueRequest", "failRequest", "fulfillRequest", "continueWithAuth", "getResponseBody", "takeResponseBodyAsStream"] "async": ["enable", "continueRequest", "failRequest", "fulfillRequest", "continueWithAuth", "getResponseBody", "takeResponseBodyAsStream"]
},
{
"domain": "WebAuthn",
"include": ["enable", "disable"]
} }
] ]
}, },
......
...@@ -65,6 +65,8 @@ ...@@ -65,6 +65,8 @@
#include "content/browser/renderer_host/compositor_impl_android.h" #include "content/browser/renderer_host/compositor_impl_android.h"
#include "content/public/browser/render_widget_host_view.h" #include "content/public/browser/render_widget_host_view.h"
#include "services/device/public/mojom/wake_lock_context.mojom.h" #include "services/device/public/mojom/wake_lock_context.mojom.h"
#else
#include "content/browser/devtools/protocol/webauthn_handler.h"
#endif #endif
namespace content { namespace content {
...@@ -323,6 +325,9 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) { ...@@ -323,6 +325,9 @@ bool RenderFrameDevToolsAgentHost::AttachSession(DevToolsSession* session) {
session->AddHandler(std::make_unique<protocol::TracingHandler>( session->AddHandler(std::make_unique<protocol::TracingHandler>(
frame_tree_node_, GetIOContext())); frame_tree_node_, GetIOContext()));
} }
#if !defined(OS_ANDROID)
session->AddHandler(std::make_unique<protocol::WebAuthnHandler>());
#endif // !defined(OS_ANDROID)
if (sessions().empty()) { if (sessions().empty()) {
bool use_video_capture_api = true; bool use_video_capture_api = true;
......
...@@ -6794,3 +6794,13 @@ experimental domain WebAudio ...@@ -6794,3 +6794,13 @@ experimental domain WebAudio
event contextChanged event contextChanged
parameters parameters
BaseAudioContext context BaseAudioContext context
# This domain allows configuring virtual authenticators to test the WebAuthn
# API.
experimental domain WebAuthn
# Enable the WebAuthn domain and start intercepting credential storage and
# retrieval with a virtual authenticator.
command enable
# Disable the WebAuthn domain.
command disable
...@@ -19,6 +19,7 @@ Performance.disable finished successfully ...@@ -19,6 +19,7 @@ Performance.disable finished successfully
Profiler.disable finished successfully Profiler.disable finished successfully
Runtime.disable finished successfully Runtime.disable finished successfully
WebAudio.disable finished successfully WebAudio.disable finished successfully
WebAuthn.disable finished successfully
Accessibility.enable finished successfully Accessibility.enable finished successfully
Accessibility.disable finished successfully Accessibility.disable finished successfully
...@@ -77,3 +78,6 @@ Runtime.disable finished successfully ...@@ -77,3 +78,6 @@ Runtime.disable finished successfully
WebAudio.enable finished successfully WebAudio.enable finished successfully
WebAudio.disable finished successfully WebAudio.disable finished successfully
WebAuthn.enable finished successfully
WebAuthn.disable finished successfully
...@@ -5,21 +5,13 @@ ...@@ -5,21 +5,13 @@
(async function() { (async function() {
TestRunner.addResult(`Test that each agent could be enabled/disabled separately.\n`); TestRunner.addResult(`Test that each agent could be enabled/disabled separately.\n`);
function printResult(agentName, action, errorString) {
var requestsSent = 0;
var responsesReceived = 0;
function finishWhenDone(agentName, action, errorString) {
if (action === 'enable') if (action === 'enable')
TestRunner.addResult(''); TestRunner.addResult('');
if (errorString) if (errorString)
TestRunner.addResult(agentName + '.' + action + ' finished with error ' + errorString); TestRunner.addResult(agentName + '.' + action + ' finished with error ' + errorString);
else else
TestRunner.addResult(agentName + '.' + action + ' finished successfully'); TestRunner.addResult(agentName + '.' + action + ' finished successfully');
++responsesReceived;
if (responsesReceived === requestsSent)
TestRunner.completeTest();
} }
var targets = SDK.targetManager.targets(); var targets = SDK.targetManager.targets();
...@@ -39,24 +31,25 @@ ...@@ -39,24 +31,25 @@
.sort(); .sort();
async function disableAgent(agentName) { async function disableAgent(agentName) {
++requestsSent;
var agent = target._agents[agentName]; var agent = target._agents[agentName];
var response = await agent.invoke_disable({}); var response = await agent.invoke_disable({});
finishWhenDone(agentName, 'disable', response[Protocol.Error]); printResult(agentName, 'disable', response[Protocol.Error]);
} }
async function enableAgent(agentName) { async function enableAgent(agentName) {
++requestsSent;
var agent = target._agents[agentName]; var agent = target._agents[agentName];
var response = await agent.invoke_enable({}); var response = await agent.invoke_enable({});
finishWhenDone(agentName, 'enable', response[Protocol.Error]); printResult(agentName, 'enable', response[Protocol.Error]);
} }
agentNames.forEach(disableAgent); for (agentName of agentNames)
await disableAgent(agentName);
agentNames.forEach(agentName => { for (agentName of agentNames) {
enableAgent(agentName); await enableAgent(agentName);
disableAgent(agentName); await disableAgent(agentName);
}); }
} }
TestRunner.completeTest();
})(); })();
<!DOCTYPE html>
<html lang="en">
<head>
<script>
async function registerCredential() {
try {
await navigator.credentials.create({
publicKey: {
authenticatorSelection: {
requireResidentKey: false,
},
rp: {
id: 'devtools.test',
name: 'DevTools Test',
},
challenge: Uint8Array.from('challenge'),
pubKeyCredParams: [
{type: 'public-key', alg: -7},
],
user: {
name: 'name',
displayName: 'displayName',
id: Uint8Array.from([1]),
}
}});
return "OK";
} catch (error) {
return error.toString();
}
}
</script>
</head>
<body>
</body>
</html>
Check that calling WebAuthn.enable starts the WebAuthn virtual authenticator environment.
OK
(async function(testRunner) {
var {page, session, dp} =
await testRunner.startBlank(
"Check that calling WebAuthn.enable starts the WebAuthn virtual " +
"authenticator environment.");
await page.navigate(
"https://devtools.test:8443/inspector-protocol/webauthn/resources/create-credential-test.https.html");
await dp.WebAuthn.enable();
const result = await session.evaluateAsync("registerCredential()");
testRunner.log(result);
testRunner.completeTest();
})
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment