mirror of
https://github.com/ChromeDevTools/chrome-devtools-mcp.git
synced 2026-11-04 15:00:11 +00:00
Closes #1004. Adds one opt-in tool, `configure_webauthn`, behind a new `--categoryWebauthn` flag (off by default), so agents can test passkey and security key flows with a virtual authenticator. It goes through the `WebAuthnModel` exported from devtools-frontend in https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/8606246, which is included in the current `third_party/devtools-frontend` roll. **Depends on** https://chromium-review.googlesource.com/c/devtools/devtools-frontend/+/8629668 (landed 2026-10-22, not rolled yet), which makes `WebAuthnModel` throw when a CDP command fails. Until that CL lands and is rolled into `third_party/devtools-frontend`, the model still ignores CDP errors, so the error and reset paths below only run in the unit tests. This PR should land after that roll. ### Shape One authenticator per page, all params top-level primitives so the tool also works from the CLI (reworked from an `authenticators: [...]` list during review): ``` configure_webauthn({ enabled, // false disables the virtual authenticator environment for the page; other params are ignored protocol?, // 'ctap2' | 'u2f', default ctap2 transport?, // 'internal' | 'usb' | 'nfc' | 'ble' | 'hybrid', default internal hasResidentKey?, // default true; false for u2f hasUserVerification?, // default true; false for u2f isUserVerified?, // default true; false tests a failed verification }) ``` - On a later call, omitted params keep the current values. - Changing only `isUserVerified` updates the authenticator in place, so passkeys the page registered survive (e.g. register, then make verification fail and check that sign-in is rejected). - Changing `protocol`, `transport`, `hasResidentKey` or `hasUserVerification` replaces the authenticator, since CDP can't change those on an existing one. - `enabled: false` always disables the environment for the whole page, which also removes authenticators other clients (such as the DevTools WebAuthn panel) added. - u2f supports neither resident keys nor user verification, so both are `false` for u2f, also when a later call switches from ctap2 with them omitted. An explicit `true` with u2f is rejected before any CDP call. - The tool description asks the agent to call it before the page calls `navigator.credentials`: a request made without a virtual authenticator stays pending, and later requests fail with `OperationError: A request is already pending` until the page navigates. - When a CDP command fails, the tool throws `Failed to configure WebAuthn for page <id>: <CDP message>`, drops its stored authenticator, removes it from the browser on a best-effort basis, and marks the environment as disabled, so the next call starts from scratch instead of reusing a stale authenticator id. - The response shows the authenticator and its stored credentials (`credentialId` as base64url without padding, matching `PublicKeyCredential.id`, `rpId`, `discoverable`, `signCount`, `userName`), formatted by `WebAuthnFormatter` and included in structured content. Private keys are never printed. - Page listings (`list_pages` and the other tools that print the page list) show `webauthn=<protocol>/<transport>` for pages that have one (`webauthn` in structured content). - The authenticator is scoped to the page and goes away when it closes. The tool uses `concurrency: 'page'`. - Importing credentials with a known private key is left out for now; it could come back as a follow-up. ### Testing - `tests/McpPage.test.ts`: `configureWebAuthn()` against a mocked `WebAuthnModel`: create with defaults, update `isUserVerified` in place and keep omitted options, replace on a creation option change, disable (also when another client enabled the environment), list stored credentials, u2f with both options false after a ctap2 authenticator, an explicit `true` with u2f rejected without a CDP call, no model, and a failing call for each CDP command (enable, add, remove, disable, `setUserVerified`, `getCredentials`), including that the next call creates a new authenticator. - `tests/tools/webauthn.test.ts`: the handler passes params through and attaches the result, plus the only real-browser run: register and sign in through (the response lists the ID the page got) `navigator.credentials.create()` / `get()`, the `list_pages` label, a rejected sign-in after `isUserVerified: false`, the passkey gone after a `transport` change, and disabling. - `tests/formatters/WebAuthnFormatter.test.ts`: snapshot tests for `toString` / `toJSON`, and a test that credential IDs print as base64url. - `McpPage`, `webauthn` and `WebAuthnFormatter` test files pass locally; `npm run gen`, eslint and prettier produce no further changes.