import { SignJWT, jwtVerify } from 'jose'; import { cookies } from 'next/headers'; import { NextRequest } from 'next/server'; const secretKey = process.env.NEXTAUTH_SECRET; if (!secretKey) { throw new Error('NEXTAUTH_SECRET environment variable is not set'); } const encodedKey = new TextEncoder().encode(secretKey); export interface SessionPayload { userId: string; email: string; role: 'user' | 'admin'; expiresAt: Date; } export async function encrypt(payload: SessionPayload) { return new SignJWT({ userId: payload.userId, email: payload.email, role: payload.role, expiresAt: payload.expiresAt.getTime(), }) .setProtectedHeader({ alg: 'HS256' }) .setIssuedAt() .setExpirationTime('7d') .sign(encodedKey); } export async function decrypt(session: string | undefined = '') { try { if (!session) { console.log('Failed to verify session: No session provided'); return null; } const { payload } = await jwtVerify(session, encodedKey, { algorithms: ['HS256'], }); const sessionData = { userId: payload.userId as string, email: payload.email as string, role: payload.role as 'user' | 'admin', expiresAt: new Date(payload.expiresAt as number), }; // Check if session is expired if (sessionData.expiresAt < new Date()) { console.log('Failed to verify session: Session expired'); return null; } return sessionData; } catch (error) { console.log('Failed to verify session:', error instanceof Error ? error.message : 'Unknown error'); return null; } } export async function createSession(payload: Omit) { const expiresAt = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); const session = await encrypt({ ...payload, expiresAt }); const cookieStore = await cookies(); // In production, always use secure cookies if NEXTAUTH_URL is https // This handles Cloudflare tunnel scenarios where external URL is https // but internal communication is http const isSecure = process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'); cookieStore.set('session', session, { httpOnly: true, secure: isSecure, expires: expiresAt, sameSite: 'lax', path: '/', }); } export async function updateSession() { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value; const payload = await decrypt(session); if (!session || !payload) { return null; } const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); const newSession = await encrypt({ ...payload, expiresAt: expires }); // In production, always use secure cookies if NEXTAUTH_URL is https const isSecure = process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'); cookieStore.set('session', newSession, { httpOnly: true, secure: isSecure, expires: expires, sameSite: 'lax', path: '/', }); } export async function deleteSession() { const cookieStore = await cookies(); cookieStore.delete('session'); } export async function getSession() { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value; return await decrypt(session); } export async function verifySession() { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value; const payload = await decrypt(session); if (!payload) { return { isAuth: false, userId: null, role: null }; } return { isAuth: true, userId: payload.userId, role: payload.role }; }