Skip to content

Commit

Permalink
[dotnet] Add Virtual Authenticator support (#10772)
Browse files Browse the repository at this point in the history
* [dotnet] Add Virtual Authenticator support

* [dotnet] Fix conflict resolution changes

* [dotnet] Formatting changes

Co-authored-by: Titus Fortner <titusfortner@users.noreply.github.com>
  • Loading branch information
pujagani and titusfortner committed Jul 5, 2022
1 parent daa0423 commit 943081b
Show file tree
Hide file tree
Showing 10 changed files with 952 additions and 2 deletions.
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;
}
}
}

0 comments on commit 943081b

Please sign in to comment.