blob: de0392748f90e735141161b20874f7a1864dacd8 [file] [log] [blame]
// <copyright file="VirtualAuthenticatorTest.cs" company="Selenium 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 NUnit.Framework;
using OpenQA.Selenium.Environment;
using OpenQA.Selenium.Internal;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using static OpenQA.Selenium.VirtualAuth.VirtualAuthenticatorOptions;
namespace OpenQA.Selenium.VirtualAuth;
[TestFixture]
public class VirtualAuthenticatorTest : DriverTestFixture
{
private IJavaScriptExecutor jsDriver;
private WebDriver webDriver;
private string base64EncodedPK;
[SetUp]
public void Setup()
{
//A pkcs#8 encoded encrypted RSA private key as a base64 string.
string base64EncodedRSAPK =
"MIIEvAIBADANBgkqhkiG9w0BAQEFAASCBKYwggSiAgEAAoIBAQDbBOu5Lhs4vpowbCnmCyLUpIE7JM9sm9QXzye2G+jr+Kr"
+ "MsinWohEce47BFPJlTaDzHSvOW2eeunBO89ZcvvVc8RLz4qyQ8rO98xS1jtgqi1NcBPETDrtzthODu/gd0sjB2Tk3TLuB"
+ "GVoPXt54a+Oo4JbBJ6h3s0+5eAfGplCbSNq6hN3Jh9YOTw5ZA6GCEy5l8zBaOgjXytd2v2OdSVoEDNiNQRkjJd2rmS2oi"
+ "9AyQFR3B7BrPSiDlCcITZFOWgLF5C31Wp/PSHwQhlnh7/6YhnE2y9tzsUvzx0wJXrBADW13+oMxrneDK3WGbxTNYgIi1P"
+ "vSqXlqGjHtCK+R2QkXAgMBAAECggEAVc6bu7VAnP6v0gDOeX4razv4FX/adCao9ZsHZ+WPX8PQxtmWYqykH5CY4TSfsui"
+ "zAgyPuQ0+j4Vjssr9VODLqFoanspT6YXsvaKanncUYbasNgUJnfnLnw3an2XpU2XdmXTNYckCPRX9nsAAURWT3/n9ljc/"
+ "XYY22ecYxM8sDWnHu2uKZ1B7M3X60bQYL5T/lVXkKdD6xgSNLeP4AkRx0H4egaop68hoW8FIwmDPVWYVAvo8etzWCtib"
+ "RXz5FcNld9MgD/Ai7ycKy4Q1KhX5GBFI79MVVaHkSQfxPHpr7/XcmpQOEAr+BMPon4s4vnKqAGdGB3j/E3d/+4F2swyko"
+ "QKBgQD8hCsp6FIQ5umJlk9/j/nGsMl85LgLaNVYpWlPRKPc54YNumtvj5vx1BG+zMbT7qIE3nmUPTCHP7qb5ERZG4CdMC"
+ "S6S64/qzZEqijLCqepwj6j4fV5SyPWEcpxf6ehNdmcfgzVB3Wolfwh1ydhx/96L1jHJcTKchdJJzlfTvq8wwKBgQDeCnK"
+ "ws1t5GapfE1rmC/h4olL2qZTth9oQmbrXYohVnoqNFslDa43ePZwL9Jmd9kYb0axOTNMmyrP0NTj41uCfgDS0cJnNTc63"
+ "ojKjegxHIyYDKRZNVUR/dxAYB/vPfBYZUS7M89pO6LLsHhzS3qpu3/hppo/Uc/AM /r8PSflNHQKBgDnWgBh6OQncChPUl"
+ "OLv9FMZPR1ZOfqLCYrjYEqiuzGm6iKM13zXFO4AGAxu1P/IAd5BovFcTpg79Z8tWqZaUUwvscnl+cRlj+mMXAmdqCeO8V"
+ "ASOmqM1ml667axeZDIR867ZG8K5V029Wg+4qtX5uFypNAAi6GfHkxIKrD04yOHAoGACdh4wXESi0oiDdkz3KOHPwIjn6B"
+ "hZC7z8mx+pnJODU3cYukxv3WTctlUhAsyjJiQ/0bK1yX87ulqFVgO0Knmh+wNajrb9wiONAJTMICG7tiWJOm7fW5cfTJw"
+ "WkBwYADmkfTRmHDvqzQSSvoC2S7aa9QulbC3C/qgGFNrcWgcT9kCgYAZTa1P9bFCDU7hJc2mHwJwAW7/FQKEJg8SL33KI"
+ "NpLwcR8fqaYOdAHWWz636osVEqosRrHzJOGpf9x2RSWzQJ+dq8+6fACgfFZOVpN644+sAHfNPAI/gnNKU5OfUv+eav8fB"
+ "nzlf1A3y3GIkyMyzFN3DE7e0n/lyqxE4HBYGpI8g==";
byte[] bytes = System.Convert.FromBase64String(base64EncodedRSAPK);
base64EncodedPK = Base64UrlEncoder.Encode(bytes);
jsDriver = (IJavaScriptExecutor)driver;
webDriver = (WebDriver)driver;
// Maximize window to ensure focus
webDriver.Manage().Window.Maximize();
webDriver.Url = EnvironmentManager.Instance.UrlBuilder.WhereIs("virtual-authenticator.html");
}
[TearDown]
public void Teardown()
{
if (webDriver.AuthenticatorId is not null &&
webDriver.SessionId is not null)
{
webDriver.RemoveVirtualAuthenticator(webDriver.AuthenticatorId);
}
}
private void CreateRKEnabledU2FAuthenticator()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions()
.SetProtocol(Protocol.U2F)
.SetHasResidentKey(true);
webDriver.AddVirtualAuthenticator(options);
}
private void CreateRKDisabledU2FAuthenticator()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions()
.SetProtocol(Protocol.U2F)
.SetHasResidentKey(false);
webDriver.AddVirtualAuthenticator(options);
}
private void CreateRKEnabledCTAP2Authenticator()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions()
.SetProtocol(Protocol.CTAP2)
.SetHasResidentKey(true)
.SetHasUserVerification(true)
.SetIsUserVerified(true);
webDriver.AddVirtualAuthenticator(options);
}
private void CreateRKDisabledCTAP2Authenticator()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions()
.SetProtocol(Protocol.CTAP2)
.SetHasResidentKey(false)
.SetHasUserVerification(true)
.SetIsUserVerified(true);
webDriver.AddVirtualAuthenticator(options);
}
private List<long> ExtractRawIdFrom(object response)
{
Dictionary<string, object> responseJson = (Dictionary<string, object>)response;
Dictionary<string, object> credentialJson = (Dictionary<string, object>)responseJson["credential"];
ReadOnlyCollection<object> readOnlyCollection = (ReadOnlyCollection<object>)credentialJson["rawId"];
List<long> rawIdList = new List<long>();
foreach (object id in readOnlyCollection)
{
rawIdList.Add((long)id);
}
return rawIdList;
}
private string ExtractIdFrom(object response)
{
Dictionary<string, object> responseJson = (Dictionary<string, object>)response;
Dictionary<string, object> credentialJson = (Dictionary<string, object>)responseJson["credential"];
return (string)credentialJson["id"];
}
private object GetAssertionFor(object credentialId)
{
return jsDriver.ExecuteAsyncScript(
"getCredential([{"
+ " \"type\": \"public-key\","
+ " \"id\": Int8Array.from(arguments[0]),"
+ "}]).then(arguments[arguments.length - 1]);", credentialId);
}
private byte[] ConvertListIntoArrayOfBytes(List<long> list)
{
byte[] ret = new byte[list.Count];
for (int i = 0; i < list.Count; i++)
{
ret[i] = ((byte)list[i]);
}
return ret;
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldCreateAuthenticator()
{
// Register a credential on the Virtual Authenticator.
CreateRKDisabledU2FAuthenticator();
object response = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
object assertionResponse = GetAssertionFor(ExtractRawIdFrom(response));
// Attempt to use the credential to get an assertion.
Assert.That((Dictionary<string, object>)assertionResponse, Does.ContainKey("status").WithValue("OK"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldRemoveAuthenticator()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
string authenticatorId = webDriver.AddVirtualAuthenticator(options);
Assert.That(webDriver.AuthenticatorId, Is.EqualTo(authenticatorId));
webDriver.RemoveVirtualAuthenticator(authenticatorId);
Assert.That(webDriver.AuthenticatorId, Is.Null);
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldSupportMultipleVirtualAuthenticatorsAtOnce()
{
VirtualAuthenticatorOptions options = new VirtualAuthenticatorOptions();
string authenticatorId1 = webDriver.AddVirtualAuthenticator(options);
Assert.That(webDriver.AuthenticatorId, Is.EqualTo(authenticatorId1));
string authenticatorId2 = webDriver.AddVirtualAuthenticator(options);
webDriver.RemoveVirtualAuthenticator(authenticatorId1);
webDriver.RemoveVirtualAuthenticator(authenticatorId2);
Assert.That(webDriver.AuthenticatorId, Is.Null);
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldAddNonResidentCredential()
{
CreateRKDisabledCTAP2Authenticator();
byte[] credentialId = { 1, 2, 3, 4 };
Credential credential = Credential.CreateNonResidentCredential(
credentialId, "localhost", base64EncodedPK, /*signCount=*/0);
webDriver.AddCredential(credential);
List<int> id = new List<int>();
id.Add(1);
id.Add(2);
id.Add(3);
id.Add(4);
// Attempt to use the credential to generate an assertion.
object response = GetAssertionFor(id);
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldAddNonResidentCredentialWhenAuthenticatorUsesU2FProtocol()
{
CreateRKDisabledU2FAuthenticator();
// A pkcs#8 encoded unencrypted EC256 private key as a base64url string.
string base64EncodedEC256PK =
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q"
+ "hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU"
+ "RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB";
byte[] credentialId = { 1, 2, 3, 4 };
Credential credential = Credential.CreateNonResidentCredential(
credentialId, "localhost", base64EncodedEC256PK, /*signCount=*/0);
webDriver.AddCredential(credential);
List<int> id = new List<int>();
id.Add(1);
id.Add(2);
id.Add(3);
id.Add(4);
// Attempt to use the credential to generate an assertion.
object response = GetAssertionFor(id);
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldAddResidentCredential()
{
// Add a resident credential using the testing API.
CreateRKEnabledCTAP2Authenticator();
byte[] credentialId = { 1, 2, 3, 4 };
byte[] userHandle = { 1 };
Credential credential = Credential.CreateResidentCredential(
credentialId, "localhost", base64EncodedPK, userHandle, /*signCount=*/0);
webDriver.AddCredential(credential);
// Attempt to use the credential to generate an assertion. Notice we use an
// empty allowCredentials array.
object response = jsDriver.ExecuteAsyncScript(
"getCredential([]).then(arguments[arguments.length - 1]);");
Assert.That(((Dictionary<string, object>)response)["status"], Is.EqualTo("OK"));
Dictionary<string, object> attestation = (Dictionary<string, object>)((Dictionary<string, object>)response)["attestation"];
ReadOnlyCollection<object> returnedUserHandle = (ReadOnlyCollection<object>)attestation["userHandle"];
Assert.That(returnedUserHandle, Has.One.Items);
Assert.That(returnedUserHandle.IndexOf(1L), Is.Zero);
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void AddResidentCredentialNotSupportedWhenAuthenticatorUsesU2FProtocol()
{
// Add a resident credential using the testing API.
CreateRKEnabledU2FAuthenticator();
// A pkcs#8 encoded unencrypted EC256 private key as a base64url string.
string base64EncodedEC256PK =
"MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg8_zMDQDYAxlU-Q"
+ "hk1Dwkf0v18GZca1DMF3SaJ9HPdmShRANCAASNYX5lyVCOZLzFZzrIKmeZ2jwU"
+ "RmgsJYxGP__fWN_S-j5sN4tT15XEpN_7QZnt14YvI6uvAgO0uJEboFaZlOEB";
byte[] credentialId = { 1, 2, 3, 4 };
byte[] userHandle = { 1 };
Credential credential = Credential.CreateResidentCredential(
credentialId, "localhost", base64EncodedEC256PK, userHandle, /*signCount=*/0);
Assert.That(
() => webDriver.AddCredential(credential),
Throws.TypeOf<WebDriverArgumentException>().With.Message.Contains("The Authenticator does not support Resident Credentials."));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldGetCredential()
{
CreateRKEnabledCTAP2Authenticator();
// Create an authenticator and add two credentials.
// Register a resident credential.
object response1 = jsDriver.ExecuteAsyncScript(
"registerCredential({authenticatorSelection: {requireResidentKey: true}})"
+ " .then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response1, Does.ContainKey("status").WithValue("OK"));
// Register a non resident credential.
object response2 = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response2, Does.ContainKey("status").WithValue("OK"));
byte[] credential1Id = ConvertListIntoArrayOfBytes(ExtractRawIdFrom(response1));
byte[] credential2Id = ConvertListIntoArrayOfBytes(ExtractRawIdFrom(response2));
Assert.That(credential2Id, Is.Not.EqualTo(credential1Id));
// Retrieve the two credentials.
List<Credential> credentials = webDriver.GetCredentials();
Assert.That(credentials, Has.Exactly(2).Items);
Credential credential1 = null;
Credential credential2 = null;
foreach (Credential credential in credentials)
{
if (Enumerable.SequenceEqual(credential.Id, credential1Id))
{
credential1 = credential;
}
else if (Enumerable.SequenceEqual(credential.Id, credential2Id))
{
credential2 = credential;
}
else
{
Assert.Fail("Unrecognized credential id");
}
}
Assert.That(credential1.IsResidentCredential, Is.True);
Assert.That(credential1.PrivateKey, Is.Not.Null);
Assert.That(credential2.IsResidentCredential, Is.False);
Assert.That(credential2.PrivateKey, Is.Not.Null);
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldRemoveCredentialByRawId()
{
CreateRKDisabledU2FAuthenticator();
// Register credential.
object response = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
// Remove a credential by its ID as an array of bytes.
List<long> rawId = ExtractRawIdFrom(response);
byte[] rawCredentialId = ConvertListIntoArrayOfBytes(rawId);
webDriver.RemoveCredential(rawCredentialId);
// Trying to get an assertion should fail.
object assertionResponse = GetAssertionFor(rawId);
string error = (string)((Dictionary<string, object>)assertionResponse)["status"];
Assert.That(error, Does.StartWith("NotAllowedError"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldRemoveCredentialByBase64UrlId()
{
CreateRKDisabledU2FAuthenticator();
// Register credential.
object response = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
// Remove a credential by its base64url ID.
String credentialId = ExtractIdFrom(response);
webDriver.RemoveCredential(credentialId);
// Trying to get an assertion should fail.
object assertionResponse = GetAssertionFor(credentialId);
string error = (string)((Dictionary<string, object>)assertionResponse)["status"];
Assert.That(error, Does.StartWith("NotAllowedError"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldRemoveAllCredentials()
{
CreateRKDisabledU2FAuthenticator();
// Register two credentials.
object response1 = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response1, Does.ContainKey("status").WithValue("OK"));
List<long> rawId1 = ExtractRawIdFrom(response1);
object response2 = jsDriver.ExecuteAsyncScript(
"registerCredential().then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response2, Does.ContainKey("status").WithValue("OK"));
List<long> rawId2 = ExtractRawIdFrom(response1);
// Remove all credentials.
webDriver.RemoveAllCredentials();
// Trying to get an assertion allowing for any of both should fail.
object response = jsDriver.ExecuteAsyncScript(
"getCredential([{"
+ " \"type\": \"public-key\","
+ " \"id\": Int8Array.from(arguments[0]),"
+ "}, {"
+ " \"type\": \"public-key\","
+ " \"id\": Int8Array.from(arguments[1]),"
+ "}]).then(arguments[arguments.length - 1]);",
rawId1, rawId2);
string error = (string)((Dictionary<string, object>)response)["status"];
Assert.That(error, Does.StartWith("NotAllowedError"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void TestSetUserVerified()
{
CreateRKEnabledCTAP2Authenticator();
// Register a credential requiring UV.
Object response = jsDriver.ExecuteAsyncScript(
"registerCredential({authenticatorSelection: {userVerification: 'required'}})"
+ " .then(arguments[arguments.length - 1]);");
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
List<long> rawId = ExtractRawIdFrom(response);
// Getting an assertion requiring user verification should succeed.
response = jsDriver.ExecuteAsyncScript(
"getCredential([{"
+ " \"type\": \"public-key\","
+ " \"id\": Int8Array.from(arguments[0]),"
+ "}], {userVerification: 'required'}).then(arguments[arguments.length - 1]);",
rawId);
Assert.That((Dictionary<string, object>)response, Does.ContainKey("status").WithValue("OK"));
// Disable user verification.
webDriver.SetUserVerified(false);
// Getting an assertion requiring user verification should fail.
response = jsDriver.ExecuteAsyncScript(
"getCredential([{"
+ " \"type\": \"public-key\","
+ " \"id\": Int8Array.from(arguments[0]),"
+ "}], {userVerification: 'required'}).then(arguments[arguments.length - 1]);",
rawId);
string error = (string)((Dictionary<string, object>)response)["status"];
Assert.That(error, Does.StartWith("NotAllowedError"));
}
[Test]
[NeedsFreshDriver(IsCreatedAfterTest = true)]
[IgnoreBrowser(Selenium.Browser.IE, "IE does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Firefox, "Firefox does not support Virtual Authenticator")]
[IgnoreBrowser(Selenium.Browser.Safari, "Safari does not support Virtual Authenticator")]
public void ShouldThrowOnInvalidArguments()
{
Assert.That(
() => webDriver.AddVirtualAuthenticator(null),
Throws.ArgumentNullException);
Assert.That(
() => webDriver.RemoveVirtualAuthenticator(null),
Throws.ArgumentNullException);
Assert.That(
() => webDriver.AddCredential(null),
Throws.ArgumentNullException);
Assert.That(
() => webDriver.RemoveCredential((byte[])null),
Throws.ArgumentNullException);
Assert.That(
() => webDriver.RemoveCredential((string)null),
Throws.ArgumentNullException);
Assert.That(
() => webDriver.RemoveVirtualAuthenticator("non-existent"),
Throws.TypeOf<WebDriverArgumentException>());
}
}