passkeys-firefox-relay.ts

2.2 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. Only the message types in ALLOWED_RELAY_TYPES are allowed through to13 * the background. Vault/admin messages must never be proxied from page JS.14 *15 * Removal: deleted alongside the MAIN-world proxy when Firefox ships16 * chrome.webAuthenticationProxy. See src/shared/passkey-flags.ts.17 */1819const ALLOWED_RELAY_TYPES = new Set([20  "PASSKEY_CREATE",21  "PASSKEY_GET",22  "PASSKEY_HAS_CREDENTIALS",23  "INITIATE_FIREFOX_PASSKEY_CREATE",24  "COMPLETE_FIREFOX_PASSKEY_CREATE",25  "PASSKEY_OFFER_DECLINE",26  "GET_PASSKEY_OFFER_DECLINED",27  "GET_PASSKEY_FOR_DOMAIN",28  "PASSKEY_AUTH_RESPONSE",29]);3031window.addEventListener("message", async (event: MessageEvent) => {32  if (event.source !== window) return;33  const data = event.data as34    | { type?: string; id?: string; payload?: unknown }35    | undefined;36  if (!data || data.type !== "PEACH_PASSKEY_REQUEST") return;37  const { id, payload } = data;38  if (!payload || typeof payload !== "object") return;39  const payloadType = (payload as { type?: unknown }).type;40  if (typeof payloadType !== "string" || !ALLOWED_RELAY_TYPES.has(payloadType)) {41    return;42  }43  if (typeof id !== "string") return;4445  const reply = (replyPayload: unknown) => {46    window.postMessage(47      { type: "PEACH_PASSKEY_RESPONSE", id, payload: replyPayload },48      window.location.origin49    );50  };5152  try {53    const response = await chrome.runtime.sendMessage(payload);54    reply(response);55  } catch {56    // The background may be momentarily unreachable (service worker spin-up,57    // extension reload). Tell the proxy to fall through to the platform58    // rather than throwing into the page.59    reply({ ok: false, error: "extension_error" });60  }61});6263export {};