Skip to content

Commit

Permalink
Add support for creating and removing virtual authenticators (#7760)
Browse files Browse the repository at this point in the history
This patch adds support for the Virtual Authenticators WebDriver API,
allowing developers to write E2E tests that exercise the WebAuthn API.
At the moment, the API is only supported on Chrome 79 onwards.

Fixes #7753

[1] https://w3c.github.io/webauthn#sctn-automation
  • Loading branch information
nsatragno authored and shs96c committed Nov 29, 2019
1 parent 3950338 commit 4c568fe
Show file tree
Hide file tree
Showing 10 changed files with 350 additions and 1 deletion.
1 change: 1 addition & 0 deletions java/client/src/org/openqa/selenium/BUILD.bazel
Expand Up @@ -17,6 +17,7 @@ java_export(
"logging/**/*.java",
"mobile/*.java",
"net/*.java",
"virtualauthenticator/*.java",
]),
maven_coordinates = "org.seleniumhq.selenium:selenium-api:%s" % SE_VERSION,
pom_template = ":template-pom",
Expand Down
5 changes: 5 additions & 0 deletions java/client/src/org/openqa/selenium/remote/DriverCommand.java
Expand Up @@ -318,4 +318,9 @@ static CommandPayload SET_CURRENT_WINDOW_SIZE(Dimension targetSize) {
// Mobile API
String GET_NETWORK_CONNECTION = "getNetworkConnection";
String SET_NETWORK_CONNECTION = "setNetworkConnection";

// Virtual Authenticator API
// http://w3c.github.io/webauthn#sctn-automation
String ADD_VIRTUAL_AUTHENTICATOR = "addVirtualAuthenticator";
String REMOVE_VIRTUAL_AUTHENTICATOR = "removeVirtualAuthenticator";
}
19 changes: 18 additions & 1 deletion java/client/src/org/openqa/selenium/remote/RemoteWebDriver.java
Expand Up @@ -67,6 +67,9 @@
import org.openqa.selenium.logging.Logs;
import org.openqa.selenium.logging.NeedsLocalLogs;
import org.openqa.selenium.remote.internal.WebElementToJsonConverter;
import org.openqa.selenium.virtualauthenticator.HasVirtualAuthenticator;
import org.openqa.selenium.virtualauthenticator.VirtualAuthenticator;
import org.openqa.selenium.virtualauthenticator.VirtualAuthenticatorOptions;

import java.net.URL;
import java.util.Collection;
Expand All @@ -88,7 +91,8 @@
public class RemoteWebDriver implements WebDriver, JavascriptExecutor,
FindsById, FindsByClassName, FindsByLinkText, FindsByName,
FindsByCssSelector, FindsByTagName, FindsByXPath,
HasInputDevices, HasCapabilities, Interactive, TakesScreenshot {
HasInputDevices, HasCapabilities, Interactive, TakesScreenshot,
HasVirtualAuthenticator {

// TODO(dawagner): This static logger should be unified with the per-instance localLogs
private static final Logger logger = Logger.getLogger(RemoteWebDriver.class.getName());
Expand Down Expand Up @@ -661,6 +665,19 @@ public Mouse getMouse() {
return mouse;
}

@Override
public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions options) {
String authenticatorId = (String)
execute(DriverCommand.ADD_VIRTUAL_AUTHENTICATOR, options.toMap()).getValue();
return new VirtualAuthenticator(authenticatorId);
}

@Override
public void removeVirtualAuthenticator(VirtualAuthenticator authenticator) {
execute(DriverCommand.REMOVE_VIRTUAL_AUTHENTICATOR,
ImmutableMap.of("authenticatorId", authenticator.getId()));
}

/**
* Override this to be notified at key points in the execution of a command.
*
Expand Down
Expand Up @@ -26,6 +26,7 @@
import static java.nio.charset.StandardCharsets.UTF_8;
import static org.openqa.selenium.json.Json.MAP_TYPE;
import static org.openqa.selenium.remote.DriverCommand.ADD_COOKIE;
import static org.openqa.selenium.remote.DriverCommand.ADD_VIRTUAL_AUTHENTICATOR;
import static org.openqa.selenium.remote.DriverCommand.CLEAR_ELEMENT;
import static org.openqa.selenium.remote.DriverCommand.CLICK_ELEMENT;
import static org.openqa.selenium.remote.DriverCommand.CLOSE;
Expand Down Expand Up @@ -76,6 +77,7 @@
import static org.openqa.selenium.remote.DriverCommand.NEW_SESSION;
import static org.openqa.selenium.remote.DriverCommand.QUIT;
import static org.openqa.selenium.remote.DriverCommand.REFRESH;
import static org.openqa.selenium.remote.DriverCommand.REMOVE_VIRTUAL_AUTHENTICATOR;
import static org.openqa.selenium.remote.DriverCommand.SCREENSHOT;
import static org.openqa.selenium.remote.DriverCommand.SEND_KEYS_TO_ELEMENT;
import static org.openqa.selenium.remote.DriverCommand.SET_ALERT_CREDENTIALS;
Expand Down Expand Up @@ -214,6 +216,11 @@ public AbstractHttpCommandCodec() {
defineCommand(SWITCH_TO_CONTEXT, post("/session/:sessionId/context"));
defineCommand(GET_CURRENT_CONTEXT_HANDLE, get("/session/:sessionId/context"));
defineCommand(GET_CONTEXT_HANDLES, get("/session/:sessionId/contexts"));

// Virtual Authenticator API
defineCommand(ADD_VIRTUAL_AUTHENTICATOR, post("/session/:sessionId/webauthn/authenticator"));
defineCommand(REMOVE_VIRTUAL_AUTHENTICATOR,
delete("/session/:sessionId/webauthn/authenticator/:authenticatorId"));
}

@Override
Expand Down
@@ -0,0 +1,30 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.virtualauthenticator;

import org.openqa.selenium.virtualauthenticator.VirtualAuthenticator;
import org.openqa.selenium.virtualauthenticator.VirtualAuthenticatorOptions;

/**
* Interface implemented by each driver that allows access to the virtual authenticator API.
*/
public interface HasVirtualAuthenticator {
public VirtualAuthenticator addVirtualAuthenticator(VirtualAuthenticatorOptions options);

public void removeVirtualAuthenticator(VirtualAuthenticator authenticator);
}
@@ -0,0 +1,36 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.virtualauthenticator;

import java.util.Objects;

/**
* Represents a virtual authenticator.
*/
public class VirtualAuthenticator {

private final String id;

public VirtualAuthenticator(final String id) {
this.id = Objects.requireNonNull(id);
}

public String getId() {
return id;
}
}
@@ -0,0 +1,102 @@
// Licensed to the Software Freedom Conservancy (SFC) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The SFC licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.

package org.openqa.selenium.virtualauthenticator;

import java.util.HashMap;
import java.util.Map;

/**
* Options for the creation of virtual authenticators.
* @see http://w3c.github.io/webauthn/#sctn-automation
*/
public class VirtualAuthenticatorOptions {

enum Protocol {
CTAP2("ctap2"),
U2F("ctap1/u2f");

public final String id;

private Protocol(String id) {
this.id = id;
}
}

enum Transport {
BLE("ble"),
INTERNAL("internal"),
NFC("nfc"),
USB("usb");

public final String id;

private Transport(String id) {
this.id = id;
}
}

private Protocol protocol = Protocol.CTAP2;
private Transport transport = Transport.USB;
private boolean hasResidentKey = false;
private boolean hasUserVerification = false;
private boolean isUserConsenting = true;
private boolean isUserVerified = false;

public VirtualAuthenticatorOptions() { }

public VirtualAuthenticatorOptions setProtocol(Protocol protocol) {
this.protocol = protocol;
return this;
}

public VirtualAuthenticatorOptions setTransport(Transport transport) {
this.transport = transport;
return this;
}

public VirtualAuthenticatorOptions setHasResidentKey(boolean hasResidentKey) {
this.hasResidentKey = hasResidentKey;
return this;
}

public VirtualAuthenticatorOptions setHasUserVerification(boolean hasUserVerification) {
this.hasUserVerification = hasUserVerification;
return this;
}

public VirtualAuthenticatorOptions setIsUserConsenting(boolean isUserConsenting) {
this.isUserConsenting = isUserConsenting;
return this;
}

public VirtualAuthenticatorOptions setIsUserVerified(boolean isUserVerified) {
this.isUserVerified = isUserVerified;
return this;
}

public Map<String, Object> toMap() {
HashMap<String, Object> map = new HashMap();
map.put("protocol", protocol.id);
map.put("transport", transport.id);
map.put("hasResidentKey", hasResidentKey);
map.put("hasUserVerification", hasUserVerification);
map.put("isUserConsenting", isUserConsenting);
map.put("isUserVerified", isUserVerified);
return map;
}
}
1 change: 1 addition & 0 deletions java/client/test/org/openqa/selenium/BUILD.bazel
Expand Up @@ -89,6 +89,7 @@ java_library(
"logging/*.java",
"net/*Test.java",
"os/*Test.java",
"virtualauthenticator/*Test.java",
]) + [
"interactions/touch/TouchTestBase.java",
],
Expand Down
@@ -0,0 +1,19 @@
load("@rules_jvm_external//:defs.bzl", "artifact")
load("//java:defs.bzl", "java_selenium_test_suite")

java_selenium_test_suite(
name = "LargeTests",
size = "large",
srcs = glob(["*.java"]),
browsers = ["chrome", "edge", "ie", "safari"],
deps = [
"//java/client/src/org/openqa/selenium:core",
"//java/client/src/org/openqa/selenium/remote",
"//java/client/test/org/openqa/selenium/environment",
"//java/client/test/org/openqa/selenium/testing:annotations",
"//java/client/test/org/openqa/selenium/testing:test-base",
"//java/client/test/org/openqa/selenium/testing/drivers",
artifact("junit:junit"),
artifact("org.assertj:assertj-core"),
],
)

0 comments on commit 4c568fe

Please sign in to comment.