Skip to content

Commit 1c2240d

Browse files
TamsilAmanipujaganiharsha509
authored
[JS] feat: Added virtual authenticator (#10663)
* [JS] feat: Added virtual authenticator Relates #10541 * [JS] fix: code review changes Co-authored-by: Puja Jagani <[email protected]> Co-authored-by: Sri Harsha <[email protected]>
1 parent f2e40dc commit 1c2240d

File tree

9 files changed

+1209
-1
lines changed

9 files changed

+1209
-1
lines changed

javascript/node/selenium-webdriver/lib/command.js

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,6 +164,15 @@ const Name = {
164164
FIND_ELEMENT_FROM_SHADOWROOT: 'findElementFromShadowRoot',
165165
FIND_ELEMENTS_FROM_SHADOWROOT: 'findElementsFromShadowRoot',
166166

167+
// Virtual Authenticator Commands
168+
ADD_VIRTUAL_AUTHENTICATOR : 'addVirtualAuthenticator',
169+
REMOVE_VIRTUAL_AUTHENTICATOR : 'removeVirtualAuthenticator',
170+
ADD_CREDENTIAL : 'addCredential',
171+
GET_CREDENTIALS : 'getCredentials',
172+
REMOVE_CREDENTIAL : 'removeCredential',
173+
REMOVE_ALL_CREDENTIALS : 'removeAllCredentials',
174+
SET_USER_VERIFIED : 'setUserVerified',
175+
167176
GET_AVAILABLE_LOG_TYPES: 'getAvailableLogTypes',
168177
GET_LOG: 'getLog',
169178

javascript/node/selenium-webdriver/lib/http.js

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -347,6 +347,15 @@ const W3C_COMMAND_MAP = new Map([
347347

348348
// Server Extensions
349349
[cmd.Name.UPLOAD_FILE, post('/session/:sessionId/se/file')],
350+
351+
// Virtual Authenticator
352+
[cmd.Name.ADD_VIRTUAL_AUTHENTICATOR, post('/session/:sessionId/webauthn/authenticator')],
353+
[cmd.Name.REMOVE_VIRTUAL_AUTHENTICATOR, del('/session/:sessionId/webauthn/authenticator/:authenticatorId')],
354+
[cmd.Name.ADD_CREDENTIAL, post('/session/:sessionId/webauthn/authenticator/:authenticatorId/credential')],
355+
[cmd.Name.GET_CREDENTIALS, get('/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')],
356+
[cmd.Name.REMOVE_CREDENTIAL, del('/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials/:credentialId')],
357+
[cmd.Name.REMOVE_ALL_CREDENTIALS, del('/session/:sessionId/webauthn/authenticator/:authenticatorId/credentials')],
358+
[cmd.Name.SET_USER_VERIFIED, post('/session/:sessionId/webauthn/authenticator/:authenticatorId/uv')],
350359
])
351360

352361
/**
@@ -472,6 +481,7 @@ class Executor {
472481
this.log_.finer(() => `>>>\n${request}\n<<<\n${response}`)
473482

474483
let httpResponse = /** @type {!Response} */ (response)
484+
475485
let { isW3C, value } = parseHttpResponse(command, httpResponse)
476486

477487
if (command.getName() === cmd.Name.NEW_SESSION) {
@@ -530,6 +540,7 @@ function parseHttpResponse(command, httpResponse) {
530540
}
531541

532542
let parsed = tryParse(httpResponse.body)
543+
533544
if (parsed && typeof parsed === 'object') {
534545
let value = parsed.value
535546
let isW3C =

javascript/node/selenium-webdriver/lib/test/fileserver.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ const Pages = (function () {
106106
addPage('webComponents', 'webComponents.html')
107107
addPage('xhtmlTestPage', 'xhtmlTest.html')
108108
addPage('uploadInvisibleTestPage', 'upload_invisible.html')
109+
addPage('virtualAuthenticator', 'virtual-authenticator.html')
109110

110111
return pages
111112
})()
Lines changed: 230 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,230 @@
1+
// Licensed to the Software Freedom Conservancy (SFC) under one
2+
// or more contributor license agreements. See the NOTICE file
3+
// distributed with this work for additional information
4+
// regarding copyright ownership. The SFC licenses this file
5+
// to you under the Apache License, Version 2.0 (the
6+
// "License"); you may not use this file except in compliance
7+
// with the License. You may obtain a copy of the License at
8+
//
9+
// http://www.apache.org/licenses/LICENSE-2.0
10+
//
11+
// Unless required by applicable law or agreed to in writing,
12+
// software distributed under the License is distributed on an
13+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
// KIND, either express or implied. See the License for the
15+
// specific language governing permissions and limitations
16+
// under the License.
17+
18+
'use strict'
19+
20+
/**
21+
* Options for the creation of virtual authenticators.
22+
* @see http://w3c.github.io/webauthn/#sctn-automation
23+
*/
24+
class VirtualAuthenticatorOptions {
25+
26+
static Protocol = {
27+
"CTAP2": 'ctap2',
28+
"U2F": 'ctap1/u2f',
29+
}
30+
31+
static Transport = {
32+
"BLE": 'ble',
33+
"USB": 'usb',
34+
"NFC": 'nfc',
35+
"INTERNAL": 'internal',
36+
}
37+
38+
/**
39+
* Constructor to initialise VirtualAuthenticatorOptions object.
40+
*/
41+
constructor() {
42+
this._protocol = VirtualAuthenticatorOptions.Protocol["CTAP2"]
43+
this._transport = VirtualAuthenticatorOptions.Transport["USB"]
44+
this._hasResidentKey = false
45+
this._hasUserVerification = false
46+
this._isUserConsenting = true
47+
this._isUserVerified = false
48+
}
49+
50+
getProtocol() {
51+
return this._protocol
52+
}
53+
54+
setProtocol(protocol) {
55+
this._protocol = protocol
56+
}
57+
58+
getTransport() {
59+
return this._transport
60+
}
61+
62+
setTransport(transport) {
63+
this._transport = transport
64+
}
65+
66+
getHasResidentKey() {
67+
return this._hasResidentKey
68+
}
69+
70+
setHasResidentKey(value) {
71+
this._hasResidentKey = value
72+
}
73+
74+
getHasUserVerification() {
75+
return this._hasUserVerification
76+
}
77+
78+
setHasUserVerification(value) {
79+
this._hasUserVerification = value
80+
}
81+
82+
getIsUserConsenting() {
83+
return this._isUserConsenting
84+
}
85+
86+
setIsUserConsenting(value) {
87+
this._isUserConsenting = value
88+
}
89+
90+
getIsUserVerified() {
91+
return this._isUserVerified
92+
}
93+
94+
setIsUserVerified(value) {
95+
this._isUserVerified = value
96+
}
97+
98+
toDict() {
99+
return {
100+
"protocol": this.getProtocol(),
101+
"transport": this.getTransport(),
102+
"hasResidentKey": this.getHasResidentKey(),
103+
"hasUserVerification": this.getHasUserVerification(),
104+
"isUserConsenting": this.getIsUserConsenting(),
105+
"isUserVerified": this.getIsUserVerified(),
106+
107+
}
108+
}
109+
}
110+
111+
/**
112+
* A credential stored in a virtual authenticator.
113+
* @see https://w3c.github.io/webauthn/#credential-parameters
114+
*/
115+
class Credential {
116+
constructor(
117+
credentialId,
118+
isResidentCredential,
119+
rpId,
120+
userHandle,
121+
privateKey,
122+
signCount
123+
) {
124+
this._id = credentialId
125+
this._isResidentCredential = isResidentCredential
126+
this._rpId = rpId
127+
this._userHandle = userHandle
128+
this._privateKey = privateKey
129+
this._signCount = signCount
130+
}
131+
132+
id() {
133+
return this._id
134+
}
135+
136+
isResidentCredential() {
137+
return this._isResidentCredential
138+
}
139+
140+
rpId() {
141+
return this._rpId
142+
}
143+
144+
userHandle() {
145+
if (this._userHandle != null) {
146+
return this._userHandle
147+
}
148+
return null
149+
}
150+
151+
privateKey() {
152+
return this._privateKey
153+
}
154+
155+
signCount() {
156+
return this._signCount
157+
}
158+
159+
/**
160+
* Creates a resident (i.e. stateless) credential.
161+
* @param id Unique base64 encoded string.
162+
* @param rpId Relying party identifier.
163+
* @param userHandle userHandle associated to the credential. Must be Base64 encoded string.
164+
* @param privateKey Base64 encoded PKCS
165+
* @param signCount intital value for a signature counter.
166+
* @returns A resident credential
167+
*/
168+
createResidentCredential(id, rpId, userHandle, privateKey, signCount) {
169+
return new Credential(id, true, rpId, userHandle, privateKey, signCount)
170+
}
171+
172+
/**
173+
* Creates a non resident (i.e. stateless) credential.
174+
* @param id Unique base64 encoded string.
175+
* @param rpId Relying party identifier.
176+
* @param privateKey Base64 encoded PKCS
177+
* @param signCount intital value for a signature counter.
178+
* @returns A non-resident credential
179+
*/
180+
createNonResidentCredential(id, rpId, privateKey, signCount) {
181+
return new Credential(id, false, rpId, null, privateKey, signCount)
182+
}
183+
184+
toDict() {
185+
let credentialData = {
186+
'credentialId': Buffer.from(this._id).toString('base64url'),
187+
'isResidentCredential': this._isResidentCredential,
188+
'rpId': this._rpId,
189+
'privateKey': Buffer.from(this._privateKey, 'binary').toString('base64url'),
190+
'signCount': this._signCount,
191+
}
192+
193+
if (this.userHandle() != null) {
194+
credentialData['userHandle'] = Buffer.from(this._userHandle).toString('base64url')
195+
}
196+
197+
return credentialData
198+
}
199+
200+
/**
201+
* Creates a credential from a map.
202+
*/
203+
fromDict(data) {
204+
let id = new Uint8Array(Buffer.from(data['credentialId'], 'base64url'))
205+
let isResidentCredential = data['isResidentCredential']
206+
let rpId = data['rpId']
207+
let privateKey = Buffer.from(data['privateKey'], 'base64url').toString('binary')
208+
let signCount = data['signCount']
209+
let userHandle
210+
211+
if ('userHandle' in data) {
212+
userHandle = new Uint8Array(Buffer.from(data['userHandle'], 'base64url'))
213+
} else {
214+
userHandle = null
215+
}
216+
return new Credential(
217+
id,
218+
isResidentCredential,
219+
rpId,
220+
userHandle,
221+
privateKey,
222+
signCount
223+
)
224+
}
225+
}
226+
227+
module.exports = {
228+
Credential,
229+
VirtualAuthenticatorOptions,
230+
}

0 commit comments

Comments
 (0)