const WILDCARD_HOSTNAMES = new Set(["0.0.0.0", "::", "[::]"]); const LOCAL_HOSTNAMES = new Set([...WILDCARD_HOSTNAMES, "localhost"]); function isWildcardHostname(hostname: string) { return WILDCARD_HOSTNAMES.has(hostname); } function isLocalHostname(hostname: string) { return ( LOCAL_HOSTNAMES.has(hostname) || hostname === "127.0.0.1" || hostname.startsWith("127.") ); } export function resolvePublicAppOrigin(input: { configuredUrl?: string; authUrl?: string; requestUrl?: string; production: boolean; }) { const configured = input.configuredUrl?.trim() || input.authUrl?.trim(); if (!configured) { if (input.production) { throw new Error("NEXT_PUBLIC_APP_URL is required in production"); } if (input.requestUrl) { const requestOrigin = new URL(input.requestUrl); if (!isWildcardHostname(requestOrigin.hostname)) { return requestOrigin.origin; } } return "http://localhost:3000"; } const url = new URL(configured); if (isWildcardHostname(url.hostname)) { throw new Error( `NEXT_PUBLIC_APP_URL resolved to a wildcard bind address (${url.hostname})`, ); } if ( input.production && (url.protocol !== "https:" || isLocalHostname(url.hostname)) ) { throw new Error( "NEXT_PUBLIC_APP_URL must be a public HTTPS origin in production", ); } return url.origin; } export function publicAppOrigin(requestUrl?: string) { return resolvePublicAppOrigin({ configuredUrl: process.env.NEXT_PUBLIC_APP_URL, authUrl: process.env.BETTER_AUTH_URL, requestUrl, production: process.env.NODE_ENV === "production", }); }