/** * passkey-utils.ts — WebAuthn relying-party-id validation. * * The native browser enforces that a page may only request a passkey whose * rpId is a registrable-domain-suffix of the page's own origin. On Firefox, * Peach intercepts `navigator.credentials` in a MAIN-world content script and * services the ceremony itself, so Peach — not the browser — must re-impose * that rule. Without it a page at `evil.com` could request (and Peach would * happily mint) a credential scoped to `google.com` (SEV-1-A). */ import { parse } from "tldts"; /** * Validates that `rpId` is equal to, or a registrable-domain-suffix of, the * hostname of `callerOrigin`. Mirrors WebAuthn §7.1 step 6 / §7.2 step 8. * Private suffixes are enabled because services such as github.io host * unrelated tenants beneath the same public suffix. */ export function isValidRpId(rpId: string, callerOrigin: string): boolean { let callerHost: string; try { callerHost = new URL(callerOrigin).hostname; } catch { return false; } if (!rpId || !callerHost) return false; const id = rpId.toLowerCase(); const host = callerHost.toLowerCase(); if (!id.includes(".")) return false; const rp = parse(id, { allowPrivateDomains: true }); const caller = parse(host, { allowPrivateDomains: true }); return rp.domain === id && caller.domain === rp.domain; }