import { NextRequest, NextResponse } from "next/server"; import { COOKIE, isAllowed, signSession, SESSION_MAX_AGE } from "@/lib/auth"; const API_URL = process.env.NEXT_PUBLIC_API_URL || "https://api.devops.norteamericano.cl"; // Rate-limit en memoria: 10 intentos por IP cada 60s. // Suficiente para una sola instancia (standalone); con más // réplicas usar un store compartido (Redis/Upstash). const WINDOW_MS = 60_000; const MAX_ATTEMPTS = 10; const attempts = new Map(); function clientIp(req: NextRequest): string { const fwd = req.headers.get("x-forwarded-for"); if (fwd) return fwd.split(",")[0].trim(); return req.headers.get("x-real-ip") || "unknown"; } function rateLimited(ip: string): boolean { const now = Date.now(); const cur = attempts.get(ip); if (!cur || cur.reset <= now) { attempts.set(ip, { count: 1, reset: now + WINDOW_MS }); return false; } cur.count += 1; // Limpieza oportunista para no crecer sin cota. if (attempts.size > 5000) { for (const [k, v] of attempts) { if (v.reset <= now) attempts.delete(k); } } return cur.count > MAX_ATTEMPTS; } // Login IT: USUARIO = idUsuario. Valida en el backend // (sige_sam_v3.usuario, plano o SHA256) y exige que el email // retornado esté en IT_USERS (allowlist). export async function POST(req: NextRequest) { try { if (rateLimited(clientIp(req))) { return NextResponse.json( { error: "Demasiados intentos, espera un minuto" }, { status: 429 } ); } const { idUsuario, email, password } = await req.json(); const id = String(idUsuario ?? email ?? "").trim(); if (!id || !password) { return NextResponse.json({ error: "Faltan credenciales" }, { status: 400 }); } let mail = ""; let backendDown = false; try { const res = await fetch(`${API_URL}/api/Auth/it-login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ idUsuario: id, email: id, password: String(password) }), cache: "no-store", }); if (res.status >= 500) { backendDown = true; } else if (!res.ok) { mail = ""; } else { const j = await res.json().catch(() => ({})); mail = String(j?.user?.email || "").trim().toLowerCase(); } } catch (e) { console.error("[api/auth] backend it-login no disponible:", e); backendDown = true; } if (backendDown) { return NextResponse.json( { error: "Servicio de autenticación no disponible, reintenta más tarde" }, { status: 503 } ); } if (!mail || !isAllowed(mail)) { await new Promise((r) => setTimeout(r, 600)); return NextResponse.json({ error: "Credenciales inválidas" }, { status: 401 }); } let session: string; try { session = await signSession(mail); } catch (e) { console.error("[api/auth] IT_SESSION_SECRET inválido:", e); return NextResponse.json({ error: "Error de configuración del servidor" }, { status: 500 }); } const secure = process.env.NODE_ENV === "production"; const res = NextResponse.json({ ok: true, email: mail }); res.cookies.set(COOKIE, session, { httpOnly: true, secure, sameSite: "lax", path: "/", maxAge: SESSION_MAX_AGE, }); return res; } catch { return NextResponse.json({ error: "Error interno" }, { status: 500 }); } } export async function DELETE() { const secure = process.env.NODE_ENV === "production"; const res = NextResponse.json({ ok: true }); res.cookies.set(COOKIE, "", { httpOnly: true, secure, sameSite: "lax", path: "/", maxAge: 0 }); return res; }