import { SignJWT, jwtVerify } from 'jose'; import bcrypt from 'bcryptjs'; import { cookies } from 'next/headers'; const JWT_SECRET = new TextEncoder().encode( process.env.JWT_SECRET ?? 'dev-secret-change-in-production-please' ); const COOKIE_NAME = 'professor_auth'; const COOKIE_MAX_AGE = 60 * 60 * 24 * 30; // 30 days export interface JWTPayload { userId: string; email: string; } // ─── Password helpers ──────────────────────────────────────────────────────── export async function hashPassword(password: string): Promise { return bcrypt.hash(password, 12); } export async function verifyPassword(password: string, hash: string): Promise { return bcrypt.compare(password, hash); } // ─── JWT helpers ───────────────────────────────────────────────────────────── export async function signToken(payload: JWTPayload): Promise { return new SignJWT(payload as unknown as Record) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime('30d') .sign(JWT_SECRET); } export async function verifyToken(token: string): Promise { try { const { payload } = await jwtVerify(token, JWT_SECRET); return payload as unknown as JWTPayload; } catch { return null; } } // ─── Cookie helpers ─────────────────────────────────────────────────────────── export async function setAuthCookie(token: string): Promise { const cookieStore = await cookies(); cookieStore.set(COOKIE_NAME, token, { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', maxAge: COOKIE_MAX_AGE, path: '/', }); } export async function clearAuthCookie(): Promise { const cookieStore = await cookies(); cookieStore.delete(COOKIE_NAME); } export async function getAuthUser(): Promise { const cookieStore = await cookies(); const token = cookieStore.get(COOKIE_NAME)?.value; if (!token) return null; return verifyToken(token); }