Enhance session management with improved error handling and secure cookie settings; update Next.js config for proxy trust
This commit is contained in:
+34
-4
@@ -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: '/',
|
||||||
|
|||||||
@@ -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;
|
||||||
|
|||||||
Reference in New Issue
Block a user