Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[dotnet] Add Virtual Authenticator support #10772

Merged
merged 5 commits into from Jul 5, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
19 changes: 18 additions & 1 deletion dotnet/src/webdriver/DriverCommand.cs
Expand Up @@ -361,6 +361,16 @@ public static class DriverCommand
/// </summary>
public static readonly string GetLog = "getLog";

// Virtual Authenticator API
// http://w3c.github.io/webauthn#sctn-automation
public static readonly string AddVirtualAuthenticator = "addVirtualAuthenticator";
public static readonly string RemoveVirtualAuthenticator = "removeVirtualAuthenticator";
public static readonly string AddCredential = "addCredential";
public static readonly string GetCredentials = "getCredentials";
public static readonly string RemoveCredential = "removeCredential";
public static readonly string RemoveAllCredentials = "removeAllCredentials";
public static readonly string SetUserVerified = "setUserVerified";

public static readonly IList<string> KnownCommands = new List<string>()
{
Status,
Expand Down Expand Up @@ -426,7 +436,14 @@ public static class DriverCommand
IsElementDisplayed,
UploadFile,
GetLog,
GetAvailableLogTypes
GetAvailableLogTypes,
AddVirtualAuthenticator,
RemoveVirtualAuthenticator,
AddCredential,
GetCredentials,
RemoveCredential,
RemoveAllCredentials,
SetUserVerified
}.AsReadOnly();
}
}
Expand Up @@ -120,6 +120,13 @@ protected override void InitializeCommandDictionary()
this.TryAddCommand(DriverCommand.Screenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/screenshot"));
this.TryAddCommand(DriverCommand.ElementScreenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/screenshot"));
this.TryAddCommand(DriverCommand.Print, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/print"));
this.TryAddCommand(DriverCommand.AddVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator"));
this.TryAddCommand(DriverCommand.RemoveVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}"));
this.TryAddCommand(DriverCommand.AddCredential, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credential"));
this.TryAddCommand(DriverCommand.GetCredentials, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials"));
this.TryAddCommand(DriverCommand.RemoveCredential, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials/{credentialId}"));
this.TryAddCommand(DriverCommand.RemoveAllCredentials, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials"));
this.TryAddCommand(DriverCommand.SetUserVerified, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/uv"));

// Commands below here are not included in the W3C specification,
// but are required for full fidelity of execution with Selenium's
Expand Down
135 changes: 135 additions & 0 deletions dotnet/src/webdriver/VirtualAuth/Credential.cs
@@ -0,0 +1,135 @@
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
// 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.
// </copyright>

using System.Collections.Generic;
using Microsoft.IdentityModel.Tokens;

namespace OpenQA.Selenium.VirtualAuth
{
/// <summary>
/// A credential stored in a virtual authenticator.
/// Refer https://w3c.github.io/webauthn/#credential-parameters
/// </summary>
public class Credential
{
private readonly byte[] id;
private readonly bool isResidentCredential;
private readonly string rpId;
private readonly string privateKey;
private readonly byte[] userHandle;
private readonly int signCount;

private Credential(
byte[] id,
bool isResidentCredential,
string rpId,
string privateKey,
byte[] userHandle,
int signCount)
{
this.id = id;
this.isResidentCredential = isResidentCredential;
this.rpId = rpId;
this.privateKey = privateKey;
this.userHandle = userHandle;
this.signCount = signCount;
}

public static Credential CreateNonResidentCredential(
byte[] id,
string rpId,
string privateKey,
int signCount)
{
return new Credential(id, false, rpId, privateKey, null, signCount);
}

public static Credential CreateResidentCredential(
byte[] id,
string rpId,
string privateKey,
byte[] userHandle,
int signCount)
{
return new Credential(
id,
true,
rpId,
privateKey,
userHandle,
signCount);
}

public byte[] Id
{
get { return (byte[])id.Clone(); }
}

public bool IsResidentCredential
{
get { return this.isResidentCredential; }
}
public string RpId
{
get { return this.rpId; }
}

public string PrivateKey
{
get { return this.privateKey; }
}

public byte[] UserHandle
{
get { return userHandle == null ? null : (byte[])userHandle.Clone(); }
}

public int SignCount
{
get { return this.signCount; }
}

public static Credential FromDictionary(Dictionary<string, object> dictionary)
{
return new Credential(
Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]),
(bool)dictionary["isResidentCredential"],
dictionary.ContainsKey("rpId") ? (string)dictionary["rpId"] : null,
(string)dictionary["privateKey"],
dictionary.ContainsKey("userHandle") ? Base64UrlEncoder.DecodeBytes((string)dictionary["userHandle"]) : null,
(int)((long)dictionary["signCount"]));
}

public Dictionary<string, object> ToDictionary()
{
Dictionary<string, object> toReturn = new Dictionary<string, object>();

toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id);
toReturn["isResidentCredential"] = this.isResidentCredential;
toReturn["rpId"] = this.rpId;
toReturn["privateKey"] = this.privateKey;
toReturn["signCount"] = this.signCount;
if (this.userHandle != null)
{
toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle);
}

return toReturn;
}
}
}
41 changes: 41 additions & 0 deletions dotnet/src/webdriver/VirtualAuth/IHasVirtualAuthenticator.cs
@@ -0,0 +1,41 @@
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
// 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.
// </copyright>
using System.Collections.Generic;

namespace OpenQA.Selenium.VirtualAuth
{
public interface IHasVirtualAuthenticator
{
string AddVirtualAuthenticator(VirtualAuthenticatorOptions options);

void RemoveVirtualAuthenticator(string id);

void AddCredential(Credential credential);

List<Credential> GetCredentials();

void RemoveCredential(byte[] credentialId);

void RemoveCredential(string credentialId);

void RemoveAllCredentials();

void SetUserVerified(bool verified);
}

}
154 changes: 154 additions & 0 deletions dotnet/src/webdriver/VirtualAuth/VirtualAuthenticatorOptions.cs
@@ -0,0 +1,154 @@
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
// 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.
// </copyright>

using System;
using System.Collections.Generic;

namespace OpenQA.Selenium.VirtualAuth
{
/// <summary>
/// Options for the creation of virtual authenticators.
/// Refer https://w3c.github.io/webauthn/#sctn-automation
/// </summary>
public class VirtualAuthenticatorOptions
{
public static class Protocol
{
public static readonly string CTAP2 = "ctap2";
public static readonly string U2F = "ctap1/u2f";
}

public static class Transport
{
public static readonly string BLE = "ble";
public static readonly string INTERNAL = "internal";
public static readonly string NFC = "nfc";
public static readonly string USB = "usb";
}

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

/// <summary>
/// Sets the protocol the Virtual Authenticator speaks
/// </summary>
/// <param name="protocol">Valid protocol value</param>
/// <returns>VirtualAuthenticatorOptions</returns>
public VirtualAuthenticatorOptions SetProtocol(string protocol)
{
if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol))
{
this.protocol = protocol;
return this;
}
else
{
throw new ArgumentException("Enter a valid protocol value." +
"Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols.");
}
}

/// <summary>
/// Sets the transport authenticator needs to implement to communicate with clients
/// </summary>
/// <param name="transport">Valid transport value</param>
/// <returns>VirtualAuthenticatorOptions</returns>
public VirtualAuthenticatorOptions SetTransport(string transport)
{
if (Transport.BLE.Equals(transport) ||
Transport.INTERNAL.Equals(transport) ||
Transport.NFC.Equals(transport) ||
Transport.USB.Equals(transport))
{
this.transport = transport;
return this;
}
else
{
throw new ArgumentException("Enter a valid transport value." +
"Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values.");
}
}

/// <summary>
/// If set to true the authenticator will support client-side discoverable credentials.
/// Refer https://w3c.github.io/webauthn/#client-side-discoverable-credential
/// </summary>
/// <param name="hasResidentKey">boolean value to set</param>
/// <returns>VirtualAuthenticatorOptions</returns>
public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey)
{
this.hasResidentKey = hasResidentKey;
return this;
}

/// <summary>
/// If set to true, the authenticator supports user verification.
/// Refer https://w3c.github.io/webauthn/#user-verification.
/// </summary>
/// <param name="hasUserVerification">boolean value to set</param>
/// <returns></returns>
public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification)
{
this.hasUserVerification = hasUserVerification;
return this;
}

/// <summary>
/// If set to true, a user consent will always be granted.
/// Refer https://w3c.github.io/webauthn/#user-consent
/// </summary>
/// <param name="isUserConsenting">boolean value to set</param>
/// <returns>VirtualAuthenticatorOptions</returns>
public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting)
{
this.isUserConsenting = isUserConsenting;
return this;
}

/// <summary>
/// If set to true, User Verification will always succeed.
/// Refer https://w3c.github.io/webauthn/#user-verification
/// </summary>
/// <param name="isUserVerified">boolean value to set</param>
/// <returns>VirtualAuthenticatorOptions</returns>
public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified)
{
this.isUserVerified = isUserVerified;
return this;
}

public Dictionary<string, object> ToDictionary()
{
Dictionary<string, object> toReturn = new Dictionary<string, object>();

toReturn["protocol"] = this.protocol;
toReturn["transport"] = this.transport;
toReturn["hasResidentKey"] = this.hasResidentKey;
toReturn["hasUserVerification"] = this.hasUserVerification;
toReturn["isUserConsenting"] = this.isUserConsenting;
toReturn["isUserVerified"] = this.isUserVerified;

return toReturn;
}
}
}