diff --git a/lib/session.ts b/lib/session.ts index 364479d..af9fae7 100644 --- a/lib/session.ts +++ b/lib/session.ts @@ -3,6 +3,11 @@ 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 { @@ -27,17 +32,31 @@ export async function encrypt(payload: SessionPayload) { 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'], }); - return { + + 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'); + console.log('Failed to verify session:', error instanceof Error ? error.message : 'Unknown error'); return null; } } @@ -47,9 +66,16 @@ export async function createSession(payload: Omit) 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: process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'), + secure: isSecure, expires: expiresAt, sameSite: 'lax', path: '/', @@ -68,9 +94,13 @@ export async function updateSession() { 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: process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'), + secure: isSecure, expires: expires, sameSite: 'lax', path: '/', diff --git a/next.config.js b/next.config.js index f32741d..e115e1b 100644 --- a/next.config.js +++ b/next.config.js @@ -12,6 +12,10 @@ const nextConfig = { output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined, // External packages for better SQLite3 compatibility (Next.js 15+ syntax) serverExternalPackages: ['better-sqlite3'], + // Trust proxy headers for Cloudflare tunnel + experimental: { + trustHost: true, + }, }; module.exports = nextConfig;