passkeys-firefox-relay.ts

1.9 KB · raw

1/**2 * passkeys-firefox-relay.ts — extension-world relay for the MAIN-world3 * passkey proxy (passkeys-firefox.ts).4 *5 * The MAIN-world script cannot call chrome.runtime.sendMessage — it lives in6 * the page's JS context, not the extension's. This relay listens for the7 * MAIN-world's PEACH_PASSKEY_REQUEST window messages, forwards each payload8 * to the background, and posts the result back as PEACH_PASSKEY_RESPONSE9 * with the same request id.10 *11 * Trust boundary: anything the page can synthesize on `window.message` lands12 * here. The `type` and `id` fields gate dispatch; the `payload` is forwarded13 * verbatim to the background, which validates required fields and runs the14 * Pro/vault checks before performing any cryptographic work. Vault data is15 * never sent the other direction over postMessage — only the credential16 * shape the RP needs.17 *18 * Removal: deleted alongside the MAIN-world proxy when Firefox ships19 * chrome.webAuthenticationProxy. See src/shared/passkey-flags.ts.20 */2122window.addEventListener("message", async (event: MessageEvent) => {23  if (event.source !== window) return;24  const data = event.data as25    | { type?: string; id?: string; payload?: unknown }26    | undefined;27  if (!data || data.type !== "PEACH_PASSKEY_REQUEST") return;28  const { id, payload } = data;29  if (typeof id !== "string" || !payload || typeof payload !== "object") return;3031  const reply = (replyPayload: unknown) => {32    window.postMessage(33      { type: "PEACH_PASSKEY_RESPONSE", id, payload: replyPayload },34      window.location.origin35    );36  };3738  try {39    const response = await chrome.runtime.sendMessage(payload);40    reply(response);41  } catch {42    // The background may be momentarily unreachable (service worker spin-up,43    // extension reload). Tell the proxy to fall through to the platform44    // rather than throwing into the page.45    reply({ ok: false, error: "extension_error" });46  }47});