import { NextRequest, NextResponse } from "next/server"; import { getExpectedToken, isAuthenticated } from "@/lib/admin-auth"; import { getClientIp, constantTimeCompare, checkAuthRateLimit, recordAuthFailure, recordAuthSuccess, } from "@/lib/security"; export async function POST(req: NextRequest) { const ip = getClientIp(req); // 1. Check brute-force lockout const authLimit = checkAuthRateLimit(ip, 5, 15); if (!authLimit.allowed) { const waitMins = Math.ceil(authLimit.lockoutSeconds / 60); console.warn(`[Portal Auth Security] IP ${ip} blocked due to too many failed login attempts.`); return NextResponse.json( { success: false, error: `Too many failed login attempts. IP temporarily blocked. Try again in ${waitMins} minute${waitMins > 1 ? "s" : ""}.`, }, { status: 429 } ); } try { const body = await req.json().catch(() => ({})); const password = body.password; const expectedPassword = process.env.ADMIN_PASSWORD || "admin123"; // 2. Validate password using timing-safe comparison if (!password || typeof password !== "string" || !constantTimeCompare(password, expectedPassword)) { recordAuthFailure(ip, 5, 15); const remaining = authLimit.remainingAttempts - 1; return NextResponse.json( { success: false, error: "Incorrect password", remainingAttempts: Math.max(0, remaining), }, { status: 401 } ); } // 3. Clear failed attempts on successful login recordAuthSuccess(ip); const token = getExpectedToken(); const response = NextResponse.json({ success: true, token, message: "Authentication successful", }); response.cookies.set({ name: "booth_portal_token", value: token, httpOnly: true, path: "/", maxAge: 60 * 60 * 24 * 7, // 7 days sameSite: "lax", }); return response; } catch (error: any) { console.error("[Portal Auth] Login error:", error); return NextResponse.json( { success: false, error: "Internal authentication error" }, { status: 500 } ); } } export async function GET(req: NextRequest) { const authed = isAuthenticated(req); if (authed) { return NextResponse.json({ authenticated: true }); } return NextResponse.json({ authenticated: false }, { status: 401 }); } export async function DELETE() { const response = NextResponse.json({ success: true, message: "Logged out" }); response.cookies.set({ name: "booth_portal_token", value: "", httpOnly: true, path: "/", maxAge: 0, sameSite: "lax", }); return response; }