@@ -8,8 +8,37 @@ import type {CDPSession} from '../third_party/index.js';
88import { zod } from '../third_party/index.js' ;
99
1010import { ToolCategory } from './categories.js' ;
11+ import type { Context } from './ToolDefinition.js' ;
1112import { defineTool } from './ToolDefinition.js' ;
1213
14+ /**
15+ * Gets the CDP session from the current page context.
16+ */
17+ function getCDPSession ( context : Context ) : CDPSession {
18+ const page = context . getSelectedPage ( ) ;
19+ // @ts -expect-error _client is internal Puppeteer API
20+ return page . _client ( ) as CDPSession ;
21+ }
22+
23+ /**
24+ * Wraps CDP errors with more helpful messages.
25+ */
26+ function handleWebAuthnError ( error : unknown ) : never {
27+ const message = error instanceof Error ? error . message : String ( error ) ;
28+
29+ if ( message . includes ( 'not been enabled' ) ) {
30+ throw new Error (
31+ 'WebAuthn virtual authenticator environment not enabled. Call webauthn_enable first.' ,
32+ ) ;
33+ }
34+ if ( message . includes ( 'authenticator' ) ) {
35+ throw new Error (
36+ `Invalid or unknown authenticator ID. Use webauthn_add_authenticator to create one. Original error: ${ message } ` ,
37+ ) ;
38+ }
39+ throw error ;
40+ }
41+
1342export const enableWebAuthn = defineTool ( {
1443 name : 'webauthn_enable' ,
1544 description :
@@ -20,9 +49,7 @@ export const enableWebAuthn = defineTool({
2049 } ,
2150 schema : { } ,
2251 handler : async ( _request , response , context ) => {
23- const page = context . getSelectedPage ( ) ;
24- // @ts -expect-error _client is internal Puppeteer API
25- const session = page . _client ( ) as CDPSession ;
52+ const session = getCDPSession ( context ) ;
2653 await session . send ( 'WebAuthn.enable' ) ;
2754 response . appendResponseLine (
2855 'WebAuthn virtual authenticator environment enabled.' ,
@@ -58,10 +85,7 @@ export const addVirtualAuthenticator = defineTool({
5885 . describe ( 'Whether user verification is currently enabled/verified.' ) ,
5986 } ,
6087 handler : async ( request , response , context ) => {
61- const page = context . getSelectedPage ( ) ;
62- // @ts -expect-error _client is internal Puppeteer API
63- const session = page . _client ( ) as CDPSession ;
64-
88+ const session = getCDPSession ( context ) ;
6589 const {
6690 protocol,
6791 transport,
@@ -70,19 +94,22 @@ export const addVirtualAuthenticator = defineTool({
7094 isUserVerified,
7195 } = request . params ;
7296
73- const result = await session . send ( 'WebAuthn.addVirtualAuthenticator' , {
74- options : {
75- protocol,
76- transport,
77- hasResidentKey : hasResidentKey ?? false ,
78- hasUserVerification : hasUserVerification ?? false ,
79- isUserVerified : isUserVerified ?? false ,
80- } ,
81- } ) ;
82-
83- response . appendResponseLine (
84- `Added virtual authenticator (authenticatorId: ${ result . authenticatorId } )` ,
85- ) ;
97+ try {
98+ const result = await session . send ( 'WebAuthn.addVirtualAuthenticator' , {
99+ options : {
100+ protocol,
101+ transport,
102+ hasResidentKey : hasResidentKey ?? false ,
103+ hasUserVerification : hasUserVerification ?? false ,
104+ isUserVerified : isUserVerified ?? false ,
105+ } ,
106+ } ) ;
107+ response . appendResponseLine (
108+ `Added virtual authenticator (authenticatorId: ${ result . authenticatorId } )` ,
109+ ) ;
110+ } catch ( error ) {
111+ handleWebAuthnError ( error ) ;
112+ }
86113 } ,
87114} ) ;
88115
@@ -99,17 +126,17 @@ export const removeVirtualAuthenticator = defineTool({
99126 . describe ( 'The ID of the authenticator to remove.' ) ,
100127 } ,
101128 handler : async ( request , response , context ) => {
102- const page = context . getSelectedPage ( ) ;
103- // @ts -expect-error _client is internal Puppeteer API
104- const session = page . _client ( ) as CDPSession ;
105-
106- await session . send ( 'WebAuthn.removeVirtualAuthenticator' , {
107- authenticatorId : request . params . authenticatorId ,
108- } ) ;
109-
110- response . appendResponseLine (
111- `Removed virtual authenticator (authenticatorId: ${ request . params . authenticatorId } )` ,
112- ) ;
129+ const session = getCDPSession ( context ) ;
130+ try {
131+ await session . send ( 'WebAuthn.removeVirtualAuthenticator' , {
132+ authenticatorId : request . params . authenticatorId ,
133+ } ) ;
134+ response . appendResponseLine (
135+ `Removed virtual authenticator (authenticatorId: ${ request . params . authenticatorId } )` ,
136+ ) ;
137+ } catch ( error ) {
138+ handleWebAuthnError ( error ) ;
139+ }
113140 } ,
114141} ) ;
115142
@@ -126,25 +153,26 @@ export const getCredentials = defineTool({
126153 . describe ( 'The ID of the authenticator to get credentials from.' ) ,
127154 } ,
128155 handler : async ( request , response , context ) => {
129- const page = context . getSelectedPage ( ) ;
130- // @ts -expect-error _client is internal Puppeteer API
131- const session = page . _client ( ) as CDPSession ;
156+ const session = getCDPSession ( context ) ;
157+ try {
158+ const result = await session . send ( 'WebAuthn.getCredentials' , {
159+ authenticatorId : request . params . authenticatorId ,
160+ } ) ;
132161
133- const result = await session . send ( 'WebAuthn.getCredentials' , {
134- authenticatorId : request . params . authenticatorId ,
135- } ) ;
136-
137- if ( result . credentials . length === 0 ) {
138- response . appendResponseLine ( 'No credentials registered.' ) ;
139- } else {
140- response . appendResponseLine (
141- `Found ${ result . credentials . length } credential(s):` ,
142- ) ;
143- for ( const cred of result . credentials ) {
162+ if ( result . credentials . length === 0 ) {
163+ response . appendResponseLine ( 'No credentials registered.' ) ;
164+ } else {
144165 response . appendResponseLine (
145- `- credentialId: ${ cred . credentialId } , rpId: ${ cred . rpId } , signCount: ${ cred . signCount } ` ,
166+ `Found ${ result . credentials . length } credential(s): ` ,
146167 ) ;
168+ for ( const cred of result . credentials ) {
169+ response . appendResponseLine (
170+ `- credentialId: ${ cred . credentialId } , rpId: ${ cred . rpId } , signCount: ${ cred . signCount } ` ,
171+ ) ;
172+ }
147173 }
174+ } catch ( error ) {
175+ handleWebAuthnError ( error ) ;
148176 }
149177 } ,
150178} ) ;
@@ -175,10 +203,7 @@ export const addCredential = defineTool({
175203 signCount : zod . number ( ) . int ( ) . optional ( ) . describe ( 'The signature counter.' ) ,
176204 } ,
177205 handler : async ( request , response , context ) => {
178- const page = context . getSelectedPage ( ) ;
179- // @ts -expect-error _client is internal Puppeteer API
180- const session = page . _client ( ) as CDPSession ;
181-
206+ const session = getCDPSession ( context ) ;
182207 const {
183208 authenticatorId,
184209 credentialId,
@@ -189,21 +214,35 @@ export const addCredential = defineTool({
189214 signCount,
190215 } = request . params ;
191216
192- await session . send ( 'WebAuthn.addCredential' , {
193- authenticatorId,
194- credential : {
195- credentialId,
196- isResidentCredential,
197- rpId,
198- privateKey,
199- userHandle,
200- signCount : signCount ?? 0 ,
201- } ,
202- } ) ;
203-
204- response . appendResponseLine (
205- `Added credential (credentialId: ${ credentialId } ) to authenticator ${ authenticatorId } ` ,
206- ) ;
217+ try {
218+ await session . send ( 'WebAuthn.addCredential' , {
219+ authenticatorId,
220+ credential : {
221+ credentialId,
222+ isResidentCredential,
223+ rpId,
224+ privateKey,
225+ userHandle,
226+ signCount : signCount ?? 0 ,
227+ } ,
228+ } ) ;
229+ response . appendResponseLine (
230+ `Added credential (credentialId: ${ credentialId } ) to authenticator ${ authenticatorId } ` ,
231+ ) ;
232+ } catch ( error ) {
233+ const message = error instanceof Error ? error . message : String ( error ) ;
234+ if ( message . includes ( 'User Handle is required' ) ) {
235+ throw new Error (
236+ 'Resident credentials require a userHandle. Provide userHandle parameter.' ,
237+ ) ;
238+ }
239+ if ( message . includes ( 'error occurred trying to create' ) ) {
240+ throw new Error (
241+ 'Failed to create credential. Ensure privateKey is a valid PKCS#8 EC P-256 key (base64 encoded).' ,
242+ ) ;
243+ }
244+ handleWebAuthnError ( error ) ;
245+ }
207246 } ,
208247} ) ;
209248
@@ -220,17 +259,17 @@ export const clearCredentials = defineTool({
220259 . describe ( 'The ID of the authenticator to clear credentials from.' ) ,
221260 } ,
222261 handler : async ( request , response , context ) => {
223- const page = context . getSelectedPage ( ) ;
224- // @ts -expect-error _client is internal Puppeteer API
225- const session = page . _client ( ) as CDPSession ;
226-
227- await session . send ( 'WebAuthn.clearCredentials' , {
228- authenticatorId : request . params . authenticatorId ,
229- } ) ;
230-
231- response . appendResponseLine (
232- `Cleared all credentials from authenticator ${ request . params . authenticatorId } ` ,
233- ) ;
262+ const session = getCDPSession ( context ) ;
263+ try {
264+ await session . send ( 'WebAuthn.clearCredentials' , {
265+ authenticatorId : request . params . authenticatorId ,
266+ } ) ;
267+ response . appendResponseLine (
268+ `Cleared all credentials from authenticator ${ request . params . authenticatorId } ` ,
269+ ) ;
270+ } catch ( error ) {
271+ handleWebAuthnError ( error ) ;
272+ }
234273 } ,
235274} ) ;
236275
@@ -249,17 +288,17 @@ export const setUserVerified = defineTool({
249288 . describe ( 'Whether user verification should succeed.' ) ,
250289 } ,
251290 handler : async ( request , response , context ) => {
252- const page = context . getSelectedPage ( ) ;
253- // @ts -expect-error _client is internal Puppeteer API
254- const session = page . _client ( ) as CDPSession ;
255-
256- await session . send ( 'WebAuthn.setUserVerified' , {
257- authenticatorId : request . params . authenticatorId ,
258- isUserVerified : request . params . isUserVerified ,
259- } ) ;
260-
261- response . appendResponseLine (
262- `Set user verification to ${ request . params . isUserVerified } for authenticator ${ request . params . authenticatorId } ` ,
263- ) ;
291+ const session = getCDPSession ( context ) ;
292+ try {
293+ await session . send ( 'WebAuthn.setUserVerified' , {
294+ authenticatorId : request . params . authenticatorId ,
295+ isUserVerified : request . params . isUserVerified ,
296+ } ) ;
297+ response . appendResponseLine (
298+ `Set user verification to ${ request . params . isUserVerified } for authenticator ${ request . params . authenticatorId } ` ,
299+ ) ;
300+ } catch ( error ) {
301+ handleWebAuthnError ( error ) ;
302+ }
264303 } ,
265304} ) ;
0 commit comments