#23282·clients

Vault passkey is never offered on the Google sign-in challenge page, while the same passkey works on myaccount.google.com

Author: nickysteenhaut-ctrlCreated Sep 18, 2026Updated Sep 18, 2026

Steps To Reproduce

  1. Store a passkey for a Google account in the Bitwarden vault: Google Account -> Security -> Passkeys and security keys -> Create a passkey. Google lists it as "Bitwarden".
  2. On https://myaccount.google.com/signinoptions/passkeys, press "Give it a try". Bitwarden's own "Log in with passkey?" window opens and the assertion succeeds. This is the correct behaviour.
  3. Sign out of the Google account and sign in again.
  4. On the "Use your passkey to confirm that it's really you" screen (https://accounts.google.com/v3/signin/challenge/pk/presend), press Continue.

Expected Result

Bitwarden intercepts the WebAuthn request and offers the vault passkey, exactly as it does on myaccount.google.com in step 2.

Actual Result

Bitwarden is never consulted. Chrome's native passkey UI takes over: the QR code / hybrid dialog ("Use your phone or tablet"), or the macOS system credential provider if a platform passkey happens to exist. Google keeps reporting "Last used: Not yet used" for the Bitwarden passkey.

Net effect: a passkey stored in Bitwarden can be created and tested, but can never be used to actually sign in to a Google account in Chrome.

Screenshots or Videos

Console check on the affected page, in a normal window, extension unlocked:

javascript
navigator.credentials.get.toString().includes('[native code]')
URL Result Meaning
webauthn.io false page script injected, Bitwarden active
myaccount.google.com/signinoptions/passkeys false page script injected, Bitwarden active
accounts.google.com/v3/signin/challenge/pk/* true page script never injected

The last row is stable across hard reloads, fresh tabs and full sign-out cycles (tested more than six times).

Additional Context

Ruled out as causes:

  • Incognito: reproduced in a normal window.
  • Extension version: reproduced on both 2026.5.1 and 2026.7.0.
  • The regression in #20743: injection works fine on other sites in the same profile at the same moment.
  • Enterprise policy: the chrome://policy export contains no runtime_blocked_hosts or runtime_allowed_hosts; ExtensionSettings only sets toolbar_pin.
  • Site access: Chrome lists the extension under "Full access" on accounts.google.com, set to "On All Sites".
  • Excluded domains in the extension: google.com is not excluded.
  • Page CSP as a visible error: no CSP violation is logged on the challenge page. accounts.google.com and myaccount.google.com serve the same shape of CSP (script-src 'report-sample' 'nonce-...' 'unsafe-inline' plus require-trusted-types-for 'script'), yet only one of the two works, so CSP alone does not explain the difference.
  • Prerendering: with chrome://settings/performance -> "Preload pages" disabled the behaviour is unchanged. Worth noting that DevTools does show a warmup.html context labelled prerender on the challenge page.

Likely cause

Fido2Background.updateMv3ContentScriptsRegistration() registers the page script as:

javascript
{
  id: "fido2-page-script-registration",
  js: ["content/fido2-page-script.js"],
  world: "MAIN",
  matches: ["https://*/*", "http://localhost/*"],
  excludeMatches: ["https://*/*.xml*"],
  allFrames: true,
  runAt: "document_start"
}

There is no matchOriginAsFallback: true. If the Google sign-in challenge issues the WebAuthn call from a document whose URL does not match https://*/* (an about:blank, blob: or sandboxed frame, or the prerendered warmup.html context that DevTools shows on that page), the MAIN-world script is never injected into that document and navigator.credentials.get stays native. That is consistent with every observation above, including why the same vault passkey works on myaccount.google.com.

Suggested fix

  • Add matchOriginAsFallback: true to the registration, so about:blank and opaque-origin frames inherit the initiator origin and receive the page script.
  • Longer term, moving to chrome.webAuthenticationProxy (available since Chrome 115) would make routing deterministic instead of depending on script injection reaching the right document in time. See also #20973.

Operating System

macOS, Windows

Operating System Version

macOS 27.0 (26A428) and Windows 11

Web Browser

Chrome

Browser Version

153.0.8010.48 (arm64)

Build Version

2026.7.0 (also reproduced on 2026.5.1)

Issue-Link

No response