diff --git a/app/api/auth/callback/route.ts b/app/api/auth/callback/route.ts index 21b78d4..575d75b 100644 --- a/app/api/auth/callback/route.ts +++ b/app/api/auth/callback/route.ts @@ -1,3 +1,4 @@ +import { getCookieDomain } from "@/lib/getDomain"; import { NextResponse } from "next/server"; export async function GET(req: Request) { @@ -5,6 +6,7 @@ export async function GET(req: Request) { const code = url.searchParams.get("code"); const origin = url.origin; const isHttps = url.protocol === "https:"; + const domain = getCookieDomain(url.hostname); // ← domain only if (!code) { return NextResponse.redirect(`${origin}/login?error=missing_code`); @@ -49,7 +51,7 @@ export async function GET(req: Request) { sameSite: "lax", path: "/", maxAge: data.expires_in, - domain: "localhost", + ...(domain ? { domain } : {}), }); return res; diff --git a/app/api/auth/route.ts b/app/api/auth/route.ts index 5e696ab..9829fd2 100644 --- a/app/api/auth/route.ts +++ b/app/api/auth/route.ts @@ -1,6 +1,10 @@ -export async function GET() { +export async function GET(req: Request) { + + const url = new URL(req.url); + const origin = url.origin; + const redirect = encodeURIComponent( - "http://localhost:3000/api/auth/callback" + `${origin}/api/auth/callback` ); const keycloakUrl = diff --git a/lib/getDomain.ts b/lib/getDomain.ts new file mode 100644 index 0000000..e7ae3d0 --- /dev/null +++ b/lib/getDomain.ts @@ -0,0 +1,41 @@ +const PUBLIC_SUFFIX_BLOCKLIST = new Set([ + "localhost", + "127.0.0.1", + ]); + + export function getCookieDomain(hostname: string): string | undefined { + if (!hostname) return undefined; + + const cleanHost = hostname.toLowerCase().split(":")[0]; + + // 1. Local / dev environments → no domain + if (PUBLIC_SUFFIX_BLOCKLIST.has(cleanHost) || cleanHost.endsWith(".local")) { + return undefined; + } + + const parts = cleanHost.split(".").filter(Boolean); + + // 2. IP address → no domain cookies + const isIp = parts.every((p) => /^\d+$/.test(p)); + if (isIp) return undefined; + + // 3. Must have at least domain + tld + if (parts.length < 2) return undefined; + + // 4. Handle common case: api.example.com → example.com + const rootDomain = parts.slice(-2).join("."); + + // 5. Safety: avoid setting cookie on known public suffix-like domains + const unsafeTlds = new Set([ + "vercel.app", + "netlify.app", + "github.io", + "firebaseapp.com", + ]); + + if (unsafeTlds.has(rootDomain)) { + return undefined; + } + + return `.${rootDomain}`; + } \ No newline at end of file