diff --git a/apps/frontend/app/login/actions.ts b/apps/frontend/app/login/actions.ts index 07f345d..143f5cf 100644 --- a/apps/frontend/app/login/actions.ts +++ b/apps/frontend/app/login/actions.ts @@ -1,6 +1,6 @@ "use server"; -import { cookies } from "next/headers"; +import { cookies, headers } from "next/headers"; import { redirect } from "next/navigation"; import bcrypt from "bcryptjs"; import { prisma } from "@streamclipper/db"; @@ -11,9 +11,16 @@ const SESSION_MAX_AGE_SEC = 60 * 60 * 24 * 30; async function setSessionCookie(userId: string, username: string) { const token = await createSessionToken({ sub: userId, username }); const jar = await cookies(); + // NODE_ENV alone isn't a reliable signal for this — the panel is often + // served over plain HTTP (e.g. Coolify's auto-generated sslip.io preview + // domain has no TLS). A `Secure` cookie set on an HTTP origin is silently + // dropped by the browser, which made every login look successful for an + // instant and then immediately bounce back to /login. Check the actual + // request scheme instead (Traefik/Coolify sets x-forwarded-proto). + const proto = (await headers()).get("x-forwarded-proto"); jar.set(SESSION_COOKIE_NAME, token, { httpOnly: true, - secure: process.env.NODE_ENV === "production", + secure: proto === "https", sameSite: "lax", path: "/", maxAge: SESSION_MAX_AGE_SEC,