/** 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 or private-network * address gets its own origin back so a phone on the LAN is not sent to * 127.0.0.1; any other caller gets the configured WEB_ORIGIN. */ export function requestWebOrigin(origin: string | undefined, webOrigin: 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; return isPrivateOrLocalHost(url.hostname) ? url.origin : webOrigin; } catch { return webOrigin; } }