import { SignJWT, jwtVerify } from "jose"; import { NextRequest } from "next/server"; const ALGORITHM = "HS256"; const TOKEN_EXP_HOURS = 24; function getSecret() { const secret = process.env.AUTH_SECRET || "dev-auth-secret"; return new TextEncoder().encode(secret); } export async function signAccessToken(payload: { userId: string; role: string; }) { const secret = getSecret(); const exp = Math.floor(Date.now() / 1000) + TOKEN_EXP_HOURS * 3600; return new SignJWT(payload) .setProtectedHeader({ alg: ALGORITHM }) .setExpirationTime(exp) .sign(secret); } export async function verifyAccessToken(token: string) { const secret = getSecret(); const { payload } = await jwtVerify(token, secret, { algorithms: [ALGORITHM], }); return payload as { userId: string; role: string }; } export async function getAuthFromRequest(request: Request | NextRequest) { let token: string | null = null; const header = request.headers.get("authorization"); if (header?.startsWith("Bearer ")) { token = header.slice("Bearer ".length); } if (!token) { const cookieHeader = request.headers.get("cookie") ?? ""; const match = cookieHeader .split(";") .map((c) => c.trim()) .find((c) => c.startsWith("session_token=")); if (match) { token = decodeURIComponent(match.split("=")[1]); } } if (!token) { return null; } try { return await verifyAccessToken(token); } catch { return null; } } export async function requireAuth(request: Request | NextRequest) { const auth = await getAuthFromRequest(request); if (!auth) { throw new Error("Unauthorized"); } return auth; } export function buildSessionCookie(token: string) { const secure = process.env.APP_URL?.startsWith("https://") || process.env.NODE_ENV === "production"; const maxAge = TOKEN_EXP_HOURS * 3600; return `session_token=${encodeURIComponent(token)}; Path=/; HttpOnly; SameSite=Lax; Max-Age=${maxAge};${secure ? " Secure;" : ""}`; } export function clearSessionCookie() { return "session_token=; Path=/; HttpOnly; SameSite=Lax; Max-Age=0;"; }