/** Hosts the API already trusts as browser origins: loopback and private networks. */ export function isPrivateOrLocalHost(host: string): boolean { if (isLoopbackHost(host)) return true; if (host.endsWith(".local")) return true; if (/^192\.168\.\d{1,3}\.\d{1,3}$/.test(host)) return true; if (/^10\.\d{1,3}\.\d{1,3}\.\d{1,3}$/.test(host)) return true; if (/^172\.(1[6-9]|2\d|3[01])\.\d{1,3}\.\d{1,3}$/.test(host)) return true; if (/^100\.(6[4-9]|[7-9]\d|1[01]\d|12[0-7])\.\d{1,3}\.\d{1,3}$/.test(host)) return true; return false; } export function isLoopbackHost(host: string): boolean { return host === "localhost" || host === "127.0.0.1" || host === "::1" || host === "[::1]"; } /** The web origin that browser-facing URLs (OAuth returns, screen links) should * point at for this request. A browser opened over a loopback, private-network, * or explicitly trusted origin gets its own origin back so a phone on the LAN * or Tailscale is not sent to 127.0.0.1; any other caller gets WEB_ORIGIN. */ export function requestWebOrigin( origin: string | undefined, webOrigin: string, extraTrustedOrigins: readonly string[] = [], ): string { if (!origin) return webOrigin; try { const url = new URL(origin); if (url.protocol !== "http:" && url.protocol !== "https:") return webOrigin; if (url.origin === new URL(webOrigin).origin) return url.origin; if (extraTrustedOrigins.includes(url.origin)) return url.origin; return isPrivateOrLocalHost(url.hostname) ? url.origin : webOrigin; } catch { return webOrigin; } }