import type { H3Event } from "h3"; import { getCookie, setCookie, deleteCookie, getHeader } from "h3"; import { prisma } from "./prisma"; import { hashPassword } from "./password"; export type AuthContext = { teamId: string; userId: string; conversationId: string; }; const COOKIE_USER = "cf_user"; const COOKIE_TEAM = "cf_team"; const COOKIE_CONV = "cf_conv"; function cookieOpts() { return { httpOnly: true, sameSite: "lax" as const, path: "/", secure: process.env.NODE_ENV === "production", }; } export function clearAuthSession(event: H3Event) { deleteCookie(event, COOKIE_USER, { path: "/" }); deleteCookie(event, COOKIE_TEAM, { path: "/" }); deleteCookie(event, COOKIE_CONV, { path: "/" }); } export function setSession(event: H3Event, ctx: AuthContext) { setCookie(event, COOKIE_USER, ctx.userId, cookieOpts()); setCookie(event, COOKIE_TEAM, ctx.teamId, cookieOpts()); setCookie(event, COOKIE_CONV, ctx.conversationId, cookieOpts()); } export async function getAuthContext(event: H3Event): Promise { const cookieUser = getCookie(event, COOKIE_USER)?.trim(); const cookieTeam = getCookie(event, COOKIE_TEAM)?.trim(); const cookieConv = getCookie(event, COOKIE_CONV)?.trim(); // Temporary compatibility: allow passing via headers for debugging/dev tools. const hdrTeam = getHeader(event, "x-team-id")?.trim(); const hdrUser = getHeader(event, "x-user-id")?.trim(); const hdrConv = getHeader(event, "x-conversation-id")?.trim(); const hasAnySession = Boolean(cookieUser || cookieTeam || cookieConv || hdrTeam || hdrUser || hdrConv); if (!hasAnySession) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } const userId = cookieUser || hdrUser; const teamId = cookieTeam || hdrTeam; const conversationId = cookieConv || hdrConv; if (!userId || !teamId || !conversationId) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } const user = await prisma.user.findUnique({ where: { id: userId } }); const team = await prisma.team.findUnique({ where: { id: teamId } }); if (!user || !team) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } const conv = await prisma.aiConversation.findFirst({ where: { id: conversationId, teamId: team.id, createdByUserId: user.id }, }); if (!conv) { throw createError({ statusCode: 401, statusMessage: "Unauthorized" }); } return { teamId: team.id, userId: user.id, conversationId: conv.id }; } export async function ensureDemoAuth() { const demoPasswordHash = hashPassword("DemoPass123!"); const user = await prisma.user.upsert({ where: { id: "demo-user" }, update: { phone: "+15550000099", email: "demo@clientsflow.local", passwordHash: demoPasswordHash, name: "Demo User" }, create: { id: "demo-user", phone: "+15550000099", email: "demo@clientsflow.local", passwordHash: demoPasswordHash, name: "Demo User" }, }); const team = await prisma.team.upsert({ where: { id: "demo-team" }, update: { name: "Demo Team" }, create: { id: "demo-team", name: "Demo Team" }, }); await prisma.teamMember.upsert({ where: { teamId_userId: { teamId: team.id, userId: user.id } }, update: {}, create: { teamId: team.id, userId: user.id, role: "OWNER" }, }); const conv = await prisma.aiConversation.upsert({ where: { id: `pilot-${team.id}` }, update: {}, create: { id: `pilot-${team.id}`, teamId: team.id, createdByUserId: user.id, title: "Pilot" }, }); return { teamId: team.id, userId: user.id, conversationId: conv.id }; }