Skip to content

[Bug]: WebKit's injected PublicKeyCredential is a plain object, so WebAuthn libraries report no support #43711

Closed

Description

Version

1.64.1

Steps to reproduce

import { test, expect } from "@playwright/test";

test("PublicKeyCredential is a constructor", async ({ page }) => {
  await page.goto("https://example.com");
  const kind = await page.evaluate(() => typeof window.PublicKeyCredential);
  expect(kind).toBe("function");
});

Run it with --project webkit (WebKit 27.2 on Windows; devices["iPhone 17"] gives the same), both with and without context.credentials.install() first.

Expected behavior

typeof PublicKeyCredential is "function", as in Chromium (where the same test passes) and in Safari. PublicKeyCredential is an interface object, and feature checks across the WebAuthn ecosystem test for a function. @simplewebauthn/browser 13.3.0's browserSupportsWebAuthn() reads:

return globalThis?.PublicKeyCredential !== undefined && typeof globalThis.PublicKeyCredential === 'function';

Actual behavior

typeof PublicKeyCredential is "object", and String(PublicKeyCredential) is "[object Object]". The WebKit page bootstrap (wkPage.ts :: _publicKeyCredentialScript) runs window.PublicKeyCredential ??= { getClientCapabilities, isConditionalMediationAvailable, isUserVerifyingPlatformAuthenticatorAvailable }, so a plain object fills the global.

Because of that, any app whose passkey ceremony goes through @simplewebauthn/browser (directly, or through an auth library's passkey client) throws "WebAuthn is not supported in this browser" under Playwright's WebKit before navigator.credentials.create is ever called. Calling navigator.credentials.create directly works, so the credentials API is fine. Only the shape of the stand-in global trips library feature checks.

Additional context

A function stand-in would keep the static methods and satisfy the check, for example window.PublicKeyCredential ??= Object.assign(function PublicKeyCredential() { throw new TypeError("Illegal constructor"); }, { getClientCapabilities, isConditionalMediationAvailable, isUserVerifyingPlatformAuthenticatorAvailable }).

Environment

- Operating System: Windows 11
- Browser: WebKit 27.2 (Playwright 1.64.1)
- Library: @simplewebauthn/browser 13.3.0

Activity

  1. anirudhqwerty commented on Oct 28, 2026

    @anirudhqwerty

    I reproduced this on current main (f9a841f968dd9a4e315df2ec2930da1952971667) using real WebKit 27.2 on Linux and Windows, with Node.js 22.23.4.

    On desktop and with mobile emulation (isMobile: true), typeof PublicKeyCredential is "object", including after context.credentials.install(). The constructor-based WebAuthn feature check returns false; an instanceof check also throws. Chromium reports "function" and passes the control.

    The cause is the plain-object fallback in WKPage._publicKeyCredentialScript() and its matching WVPage implementation. The existing capability test calls the static methods but does not check the interface shape.

    The candidate replaces the fallback with a named class whose constructor throws TypeError, retaining ??= and the three async static methods. Default results remain {}, false, false; installing virtual credentials still changes the two availability results to true. This corrects feature detection without adding authentication functionality or dependencies.

    Verification:

    • Linux baseline: two expected constructor-shape failures and one passing preservation control.
    • Windows red/green: the same tests fail on baseline and all three pass with the candidate; Chromium control passes.
    • Final Linux verification: focused regressions, Chromium control, build and full flint pass.
    • Related WebKit suites: 54 passed, 1 skipped across capability and credential tests. That earlier job subsequently caught test-registry/setup issues, resolved in the final Linux run above.

    Both exact fallback scripts were also evaluated in a real Chromium page to verify native-constructor preservation and fallback behavior. Actual iOS WebView/simulator integration was not run.

    Candidate branch: four files, including the required test-registry entries. No PR opened.

    Would you approve a focused PR with this scope, including the matching WebView fallback correction?

  2. github-actions commented on Oct 29, 2026

    @github-actions
  3. github-actions commented on Oct 29, 2026

    @github-actions
    Contributor

    Hi, I'm the Playwright bot. I reproduced this on Windows.

    Reproduced on current main (1.65.0-next, bda54b052be6c5272557f3f1c972ad378bd1dc60) with WebKit 27.2. The fallback is a plain object, so constructor-based feature checks fail. The proposed fix uses a named class that rejects direct construction, retains the static methods, and leaves native constructors unchanged. It also updates the matching WebView fallback.

    Reproduction and verification

    The existing CA-029 test in tests/library/capabilities.spec.ts now checks:

    await page.goto(server.EMPTY_PAGE);
    expect(await page.evaluate(() => typeof PublicKeyCredential)).toBe('function');
    await context.credentials.install();
    expect(await page.evaluate(() => typeof PublicKeyCredential)).toBe('function');

    Before the fix, the first assertion fails with Received: "object". The test also checks the interface name, instanceof, rejection of direct construction with a TypeError, and the three existing static methods.

    On Windows, using the checkout's installed browsers:

    • Baseline: npm run wtest -- capabilities.spec.ts --grep CA-029 --workers=1 fails in WebKit 27.2, build 2386.
    • With the fix: npm run test -- capabilities.spec.ts browsercontext-webauthn.spec.ts --workers=2 --retries=0 gives 100 passed and 11 skipped across Chromium 157.0.8103.15, Firefox 158.0, and WebKit 27.2.
    • npm run build and npm run flint pass.
    • Both exact fallback functions pass isolated JavaScript checks for interface shape, unchanged default static-method results, and preservation of an existing constructor.

    These were desktop, headless runs. Actual iOS WebView integration was not run on this Windows runner.

    Draft PR: #43775

    Triaged on Windows by the Playwright bot - agent run

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions