Enhance session management with improved error handling and secure cookie settings; update Next.js config for proxy trust

This commit is contained in:
2025-10-08 22:25:25 +01:00
parent 69b456f3f8
commit 00f1814f27
2 changed files with 38 additions and 4 deletions
+34 -4
View File
@@ -3,6 +3,11 @@ import { cookies } from 'next/headers';
import { NextRequest } from 'next/server'; import { NextRequest } from 'next/server';
const secretKey = process.env.NEXTAUTH_SECRET; const secretKey = process.env.NEXTAUTH_SECRET;
if (!secretKey) {
throw new Error('NEXTAUTH_SECRET environment variable is not set');
}
const encodedKey = new TextEncoder().encode(secretKey); const encodedKey = new TextEncoder().encode(secretKey);
export interface SessionPayload { export interface SessionPayload {
@@ -27,17 +32,31 @@ export async function encrypt(payload: SessionPayload) {
export async function decrypt(session: string | undefined = '') { export async function decrypt(session: string | undefined = '') {
try { try {
if (!session) {
console.log('Failed to verify session: No session provided');
return null;
}
const { payload } = await jwtVerify(session, encodedKey, { const { payload } = await jwtVerify(session, encodedKey, {
algorithms: ['HS256'], algorithms: ['HS256'],
}); });
return {
const sessionData = {
userId: payload.userId as string, userId: payload.userId as string,
email: payload.email as string, email: payload.email as string,
role: payload.role as 'user' | 'admin', role: payload.role as 'user' | 'admin',
expiresAt: new Date(payload.expiresAt as number), 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) { } catch (error) {
console.log('Failed to verify session'); console.log('Failed to verify session:', error instanceof Error ? error.message : 'Unknown error');
return null; return null;
} }
} }
@@ -47,9 +66,16 @@ export async function createSession(payload: Omit<SessionPayload, 'expiresAt'>)
const session = await encrypt({ ...payload, expiresAt }); const session = await encrypt({ ...payload, expiresAt });
const cookieStore = await cookies(); 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, { cookieStore.set('session', session, {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'), secure: isSecure,
expires: expiresAt, expires: expiresAt,
sameSite: 'lax', sameSite: 'lax',
path: '/', path: '/',
@@ -68,9 +94,13 @@ export async function updateSession() {
const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000); const expires = new Date(Date.now() + 7 * 24 * 60 * 60 * 1000);
const newSession = await encrypt({ ...payload, expiresAt: expires }); 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, { cookieStore.set('session', newSession, {
httpOnly: true, httpOnly: true,
secure: process.env.NODE_ENV === 'production' && process.env.NEXTAUTH_URL?.startsWith('https'), secure: isSecure,
expires: expires, expires: expires,
sameSite: 'lax', sameSite: 'lax',
path: '/', path: '/',
+4
View File
@@ -12,6 +12,10 @@ const nextConfig = {
output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined, output: process.env.NODE_ENV === 'production' ? 'standalone' : undefined,
// External packages for better SQLite3 compatibility (Next.js 15+ syntax) // External packages for better SQLite3 compatibility (Next.js 15+ syntax)
serverExternalPackages: ['better-sqlite3'], serverExternalPackages: ['better-sqlite3'],
// Trust proxy headers for Cloudflare tunnel
experimental: {
trustHost: true,
},
}; };
module.exports = nextConfig; module.exports = nextConfig;