import { NextRequest } from 'next/server'; // Server-side authentication utilities export function verifyAdminAuth(request: NextRequest): boolean { // Check for basic auth header const authHeader = request.headers.get('authorization'); if (!authHeader || !authHeader.startsWith('Basic ')) { return false; } try { const base64Credentials = authHeader.split(' ')[1]; const credentials = atob(base64Credentials); const [username, password] = credentials.split(':'); // Get admin credentials from environment const adminAuth = process.env.ADMIN_BASIC_AUTH || 'admin:default_password_change_me'; const [expectedUsername, expectedPassword] = adminAuth.split(':'); return username === expectedUsername && password === expectedPassword; } catch { return false; } } export function requireAdminAuth(request: NextRequest): Response | null { if (!verifyAdminAuth(request)) { return new Response( JSON.stringify({ error: 'Unauthorized' }), { status: 401, headers: { 'Content-Type': 'application/json' } } ); } return null; } // Session-based authentication (no browser popup) export function verifySessionAuth(request: NextRequest): boolean { // Check for session token in headers const sessionToken = request.headers.get('x-session-token'); if (!sessionToken) return false; try { // Decode and validate session token const decodedJson = atob(sessionToken); const sessionData = JSON.parse(decodedJson); // Validate session data structure if (!sessionData.timestamp || !sessionData.random || !sessionData.ip || !sessionData.userAgent) { return false; } // Check if session is still valid (2 hours) const sessionTime = sessionData.timestamp; const now = Date.now(); const sessionDuration = 2 * 60 * 60 * 1000; // 2 hours if (now - sessionTime > sessionDuration) { return false; } // Validate IP address (optional, but good security practice) const currentIp = request.headers.get('x-forwarded-for') || request.headers.get('x-real-ip') || 'unknown'; if (sessionData.ip !== currentIp) { return false; } return true; } catch { return false; } } export function requireSessionAuth(request: NextRequest): Response | null { if (!verifySessionAuth(request)) { return new Response( JSON.stringify({ error: 'Session expired or invalid' }), { status: 401, headers: { 'Content-Type': 'application/json' } } ); } return null; } // Rate limiting for admin endpoints const rateLimitMap = new Map(); // Clear rate limit cache on startup if (typeof window === 'undefined') { // Server-side: clear cache periodically setInterval(() => { const now = Date.now(); for (const [key, value] of rateLimitMap.entries()) { if (now > value.resetTime) { rateLimitMap.delete(key); } } }, 60000); // Clear every minute } export function checkRateLimit(ip: string, maxRequests: number = 10, windowMs: number = 60000): boolean { const now = Date.now(); const key = `admin_${ip}`; const current = rateLimitMap.get(key); if (!current || now > current.resetTime) { rateLimitMap.set(key, { count: 1, resetTime: now + windowMs }); return true; } if (current.count >= maxRequests) { return false; } current.count++; return true; } export function getRateLimitHeaders(ip: string, maxRequests: number = 10, windowMs: number = 60000): Record { const current = rateLimitMap.get(`admin_${ip}`); const remaining = current ? Math.max(0, maxRequests - current.count) : maxRequests; return { 'X-RateLimit-Limit': maxRequests.toString(), 'X-RateLimit-Remaining': remaining.toString(), 'X-RateLimit-Reset': current ? Math.ceil(current.resetTime / 1000).toString() : Math.ceil((Date.now() + windowMs) / 1000).toString() }; }