Skip to content

Commit 943081b

Browse files
[dotnet] Add Virtual Authenticator support (#10772)
* [dotnet] Add Virtual Authenticator support * [dotnet] Fix conflict resolution changes * [dotnet] Formatting changes Co-authored-by: Titus Fortner <titusfortner@users.noreply.github.com>
1 parent daa0423 commit 943081b

File tree

10 files changed

+952
-2
lines changed

10 files changed

+952
-2
lines changed

dotnet/src/webdriver/DriverCommand.cs

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -361,6 +361,16 @@ public static class DriverCommand
361361
/// </summary>
362362
public static readonly string GetLog = "getLog";
363363

364+
// Virtual Authenticator API
365+
// http://w3c.github.io/webauthn#sctn-automation
366+
public static readonly string AddVirtualAuthenticator = "addVirtualAuthenticator";
367+
public static readonly string RemoveVirtualAuthenticator = "removeVirtualAuthenticator";
368+
public static readonly string AddCredential = "addCredential";
369+
public static readonly string GetCredentials = "getCredentials";
370+
public static readonly string RemoveCredential = "removeCredential";
371+
public static readonly string RemoveAllCredentials = "removeAllCredentials";
372+
public static readonly string SetUserVerified = "setUserVerified";
373+
364374
public static readonly IList<string> KnownCommands = new List<string>()
365375
{
366376
Status,
@@ -426,7 +436,14 @@ public static class DriverCommand
426436
IsElementDisplayed,
427437
UploadFile,
428438
GetLog,
429-
GetAvailableLogTypes
439+
GetAvailableLogTypes,
440+
AddVirtualAuthenticator,
441+
RemoveVirtualAuthenticator,
442+
AddCredential,
443+
GetCredentials,
444+
RemoveCredential,
445+
RemoveAllCredentials,
446+
SetUserVerified
430447
}.AsReadOnly();
431448
}
432449
}

dotnet/src/webdriver/Remote/W3CWireProtocolCommandInfoRepository.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,13 @@ protected override void InitializeCommandDictionary()
120120
this.TryAddCommand(DriverCommand.Screenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/screenshot"));
121121
this.TryAddCommand(DriverCommand.ElementScreenshot, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/element/{id}/screenshot"));
122122
this.TryAddCommand(DriverCommand.Print, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/print"));
123+
this.TryAddCommand(DriverCommand.AddVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator"));
124+
this.TryAddCommand(DriverCommand.RemoveVirtualAuthenticator, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}"));
125+
this.TryAddCommand(DriverCommand.AddCredential, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credential"));
126+
this.TryAddCommand(DriverCommand.GetCredentials, new HttpCommandInfo(HttpCommandInfo.GetCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials"));
127+
this.TryAddCommand(DriverCommand.RemoveCredential, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials/{credentialId}"));
128+
this.TryAddCommand(DriverCommand.RemoveAllCredentials, new HttpCommandInfo(HttpCommandInfo.DeleteCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/credentials"));
129+
this.TryAddCommand(DriverCommand.SetUserVerified, new HttpCommandInfo(HttpCommandInfo.PostCommand, "/session/{sessionId}/webauthn/authenticator/{authenticatorId}/uv"));
123130

124131
// Commands below here are not included in the W3C specification,
125132
// but are required for full fidelity of execution with Selenium's
Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
// </copyright>
18+
19+
using System.Collections.Generic;
20+
using Microsoft.IdentityModel.Tokens;
21+
22+
namespace OpenQA.Selenium.VirtualAuth
23+
{
24+
/// <summary>
25+
/// A credential stored in a virtual authenticator.
26+
/// Refer https://w3c.github.io/webauthn/#credential-parameters
27+
/// </summary>
28+
public class Credential
29+
{
30+
private readonly byte[] id;
31+
private readonly bool isResidentCredential;
32+
private readonly string rpId;
33+
private readonly string privateKey;
34+
private readonly byte[] userHandle;
35+
private readonly int signCount;
36+
37+
private Credential(
38+
byte[] id,
39+
bool isResidentCredential,
40+
string rpId,
41+
string privateKey,
42+
byte[] userHandle,
43+
int signCount)
44+
{
45+
this.id = id;
46+
this.isResidentCredential = isResidentCredential;
47+
this.rpId = rpId;
48+
this.privateKey = privateKey;
49+
this.userHandle = userHandle;
50+
this.signCount = signCount;
51+
}
52+
53+
public static Credential CreateNonResidentCredential(
54+
byte[] id,
55+
string rpId,
56+
string privateKey,
57+
int signCount)
58+
{
59+
return new Credential(id, false, rpId, privateKey, null, signCount);
60+
}
61+
62+
public static Credential CreateResidentCredential(
63+
byte[] id,
64+
string rpId,
65+
string privateKey,
66+
byte[] userHandle,
67+
int signCount)
68+
{
69+
return new Credential(
70+
id,
71+
true,
72+
rpId,
73+
privateKey,
74+
userHandle,
75+
signCount);
76+
}
77+
78+
public byte[] Id
79+
{
80+
get { return (byte[])id.Clone(); }
81+
}
82+
83+
public bool IsResidentCredential
84+
{
85+
get { return this.isResidentCredential; }
86+
}
87+
public string RpId
88+
{
89+
get { return this.rpId; }
90+
}
91+
92+
public string PrivateKey
93+
{
94+
get { return this.privateKey; }
95+
}
96+
97+
public byte[] UserHandle
98+
{
99+
get { return userHandle == null ? null : (byte[])userHandle.Clone(); }
100+
}
101+
102+
public int SignCount
103+
{
104+
get { return this.signCount; }
105+
}
106+
107+
public static Credential FromDictionary(Dictionary<string, object> dictionary)
108+
{
109+
return new Credential(
110+
Base64UrlEncoder.DecodeBytes((string)dictionary["credentialId"]),
111+
(bool)dictionary["isResidentCredential"],
112+
dictionary.ContainsKey("rpId") ? (string)dictionary["rpId"] : null,
113+
(string)dictionary["privateKey"],
114+
dictionary.ContainsKey("userHandle") ? Base64UrlEncoder.DecodeBytes((string)dictionary["userHandle"]) : null,
115+
(int)((long)dictionary["signCount"]));
116+
}
117+
118+
public Dictionary<string, object> ToDictionary()
119+
{
120+
Dictionary<string, object> toReturn = new Dictionary<string, object>();
121+
122+
toReturn["credentialId"] = Base64UrlEncoder.Encode(this.id);
123+
toReturn["isResidentCredential"] = this.isResidentCredential;
124+
toReturn["rpId"] = this.rpId;
125+
toReturn["privateKey"] = this.privateKey;
126+
toReturn["signCount"] = this.signCount;
127+
if (this.userHandle != null)
128+
{
129+
toReturn["userHandle"] = Base64UrlEncoder.Encode(this.userHandle);
130+
}
131+
132+
return toReturn;
133+
}
134+
}
135+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
// </copyright>
18+
using System.Collections.Generic;
19+
20+
namespace OpenQA.Selenium.VirtualAuth
21+
{
22+
public interface IHasVirtualAuthenticator
23+
{
24+
string AddVirtualAuthenticator(VirtualAuthenticatorOptions options);
25+
26+
void RemoveVirtualAuthenticator(string id);
27+
28+
void AddCredential(Credential credential);
29+
30+
List<Credential> GetCredentials();
31+
32+
void RemoveCredential(byte[] credentialId);
33+
34+
void RemoveCredential(string credentialId);
35+
36+
void RemoveAllCredentials();
37+
38+
void SetUserVerified(bool verified);
39+
}
40+
41+
}
Lines changed: 154 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
// <copyright file="DesiredCapabilities.cs" company="WebDriver Committers">
2+
// Licensed to the Software Freedom Conservancy (SFC) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The SFC licenses this file
6+
// to you under the Apache License, Version 2.0 (the "License");
7+
// you may not use this file except in compliance with the License.
8+
// You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing, software
13+
// distributed under the License is distributed on an "AS IS" BASIS,
14+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15+
// See the License for the specific language governing permissions and
16+
// limitations under the License.
17+
// </copyright>
18+
19+
using System;
20+
using System.Collections.Generic;
21+
22+
namespace OpenQA.Selenium.VirtualAuth
23+
{
24+
/// <summary>
25+
/// Options for the creation of virtual authenticators.
26+
/// Refer https://w3c.github.io/webauthn/#sctn-automation
27+
/// </summary>
28+
public class VirtualAuthenticatorOptions
29+
{
30+
public static class Protocol
31+
{
32+
public static readonly string CTAP2 = "ctap2";
33+
public static readonly string U2F = "ctap1/u2f";
34+
}
35+
36+
public static class Transport
37+
{
38+
public static readonly string BLE = "ble";
39+
public static readonly string INTERNAL = "internal";
40+
public static readonly string NFC = "nfc";
41+
public static readonly string USB = "usb";
42+
}
43+
44+
private string protocol = Protocol.CTAP2;
45+
private string transport = Transport.USB;
46+
private bool hasResidentKey = false;
47+
private bool hasUserVerification = false;
48+
private bool isUserConsenting = true;
49+
private bool isUserVerified = false;
50+
51+
/// <summary>
52+
/// Sets the protocol the Virtual Authenticator speaks
53+
/// </summary>
54+
/// <param name="protocol">Valid protocol value</param>
55+
/// <returns>VirtualAuthenticatorOptions</returns>
56+
public VirtualAuthenticatorOptions SetProtocol(string protocol)
57+
{
58+
if (string.Equals(Protocol.CTAP2, protocol) || string.Equals(Protocol.U2F, protocol))
59+
{
60+
this.protocol = protocol;
61+
return this;
62+
}
63+
else
64+
{
65+
throw new ArgumentException("Enter a valid protocol value." +
66+
"Refer to https://www.w3.org/TR/webauthn-2/#sctn-automation-virtual-authenticators for supported protocols.");
67+
}
68+
}
69+
70+
/// <summary>
71+
/// Sets the transport authenticator needs to implement to communicate with clients
72+
/// </summary>
73+
/// <param name="transport">Valid transport value</param>
74+
/// <returns>VirtualAuthenticatorOptions</returns>
75+
public VirtualAuthenticatorOptions SetTransport(string transport)
76+
{
77+
if (Transport.BLE.Equals(transport) ||
78+
Transport.INTERNAL.Equals(transport) ||
79+
Transport.NFC.Equals(transport) ||
80+
Transport.USB.Equals(transport))
81+
{
82+
this.transport = transport;
83+
return this;
84+
}
85+
else
86+
{
87+
throw new ArgumentException("Enter a valid transport value." +
88+
"Refer to https://www.w3.org/TR/webauthn-2/#enum-transport for supported transport values.");
89+
}
90+
}
91+
92+
/// <summary>
93+
/// If set to true the authenticator will support client-side discoverable credentials.
94+
/// Refer https://w3c.github.io/webauthn/#client-side-discoverable-credential
95+
/// </summary>
96+
/// <param name="hasResidentKey">boolean value to set</param>
97+
/// <returns>VirtualAuthenticatorOptions</returns>
98+
public VirtualAuthenticatorOptions SetHasResidentKey(bool hasResidentKey)
99+
{
100+
this.hasResidentKey = hasResidentKey;
101+
return this;
102+
}
103+
104+
/// <summary>
105+
/// If set to true, the authenticator supports user verification.
106+
/// Refer https://w3c.github.io/webauthn/#user-verification.
107+
/// </summary>
108+
/// <param name="hasUserVerification">boolean value to set</param>
109+
/// <returns></returns>
110+
public VirtualAuthenticatorOptions SetHasUserVerification(bool hasUserVerification)
111+
{
112+
this.hasUserVerification = hasUserVerification;
113+
return this;
114+
}
115+
116+
/// <summary>
117+
/// If set to true, a user consent will always be granted.
118+
/// Refer https://w3c.github.io/webauthn/#user-consent
119+
/// </summary>
120+
/// <param name="isUserConsenting">boolean value to set</param>
121+
/// <returns>VirtualAuthenticatorOptions</returns>
122+
public VirtualAuthenticatorOptions SetIsUserConsenting(bool isUserConsenting)
123+
{
124+
this.isUserConsenting = isUserConsenting;
125+
return this;
126+
}
127+
128+
/// <summary>
129+
/// If set to true, User Verification will always succeed.
130+
/// Refer https://w3c.github.io/webauthn/#user-verification
131+
/// </summary>
132+
/// <param name="isUserVerified">boolean value to set</param>
133+
/// <returns>VirtualAuthenticatorOptions</returns>
134+
public VirtualAuthenticatorOptions SetIsUserVerified(bool isUserVerified)
135+
{
136+
this.isUserVerified = isUserVerified;
137+
return this;
138+
}
139+
140+
public Dictionary<string, object> ToDictionary()
141+
{
142+
Dictionary<string, object> toReturn = new Dictionary<string, object>();
143+
144+
toReturn["protocol"] = this.protocol;
145+
toReturn["transport"] = this.transport;
146+
toReturn["hasResidentKey"] = this.hasResidentKey;
147+
toReturn["hasUserVerification"] = this.hasUserVerification;
148+
toReturn["isUserConsenting"] = this.isUserConsenting;
149+
toReturn["isUserVerified"] = this.isUserVerified;
150+
151+
return toReturn;
152+
}
153+
}
154+
}

0 commit comments

Comments
 (0)