{projectName} Gatekeeper
Infrastructure Protection
© 2026 MINTEL
import { cookies } from "next/headers"; import { redirect } from "next/navigation"; import { ArrowRight, ShieldCheck } from "lucide-react"; import Image from "next/image"; interface LoginPageProps { searchParams: Promise<{ [key: string]: string | string[] | undefined }>; } export default async function LoginPage({ searchParams }: LoginPageProps) { const params = await searchParams; const redirectUrl = (params.redirect as string) || "/"; const error = params.error === "1"; const projectName = process.env.PROJECT_NAME || "Mintel"; async function login(formData: FormData) { "use server"; const email = (formData.get("email") as string || "").trim(); const password = (formData.get("password") as string || "").trim(); const expectedCode = process.env.GATEKEEPER_PASSWORD || "mintel"; const adminEmail = process.env.DIRECTUS_ADMIN_EMAIL; const adminPassword = process.env.DIRECTUS_ADMIN_PASSWORD; const authCookieName = process.env.AUTH_COOKIE_NAME || "mintel_gatekeeper_session"; const targetRedirect = formData.get("redirect") as string; const cookieDomain = process.env.COOKIE_DOMAIN; let userIdentity = ""; let userCompany: any = null; // 1. Check Generic Code (Guest) - High Priority to prevent autofill traps if (password === expectedCode) { userIdentity = "Guest"; } // 2. Check Global Admin (from ENV) else if ( adminEmail && adminPassword && email === adminEmail.trim() && password === adminPassword.trim() ) { userIdentity = "Admin"; } // 3. Check Lightweight Client Users (dedicated collection) if (email && password && process.env.INFRA_DIRECTUS_URL) { try { const clientUsersRes = await fetch( `${process.env.INFRA_DIRECTUS_URL}/items/client_users?filter[email][_eq]=${encodeURIComponent( email, )}&fields=*,company.*`, { headers: { Authorization: `Bearer ${process.env.INFRA_DIRECTUS_TOKEN}`, }, }, ); if (clientUsersRes.ok) { const { data: users } = await clientUsersRes.json(); const clientUser = users[0]; // ⚠️ NOTE: Plain text check for demo/dev, should use argon2 in production if ( clientUser && (clientUser.password === password || clientUser.temporary_password === password) ) { userIdentity = clientUser.first_name || clientUser.email; userCompany = { id: clientUser.company?.id, name: clientUser.company?.name, }; } } } catch (e) { console.error("Client User Auth Error:", e); } } // 4. Fallback to Directus Staff Auth if still not identified if (!userIdentity && email && password && process.env.DIRECTUS_URL) { try { const loginRes = await fetch(`${process.env.DIRECTUS_URL}/auth/login`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ email, password }), }); if (loginRes.ok) { const { data } = await loginRes.json(); const accessToken = data.access_token; // Fetch user info with company depth const userRes = await fetch( `${process.env.DIRECTUS_URL}/users/me?fields=*,company.*`, { headers: { Authorization: `Bearer ${accessToken}` }, }, ); if (userRes.ok) { const { data: user } = await userRes.json(); userIdentity = user.first_name || user.email; userCompany = { id: user.company?.id, name: user.company?.name, }; } } } catch (e) { console.error("Directus Auth Error:", e); } } if (userIdentity) { console.log(`[Login] Success: ${userIdentity} | Redirect: ${targetRedirect}`); const cookieStore = await cookies(); // Store identity in the cookie (simplified for now, ideally signed) const sessionValue = JSON.stringify({ identity: userIdentity, company: userCompany, timestamp: Date.now(), }); const isDev = process.env.NODE_ENV === "development"; console.log(`[Login] Setting Cookie: ${authCookieName} | Domain: ${cookieDomain || "Default"}`); cookieStore.set(authCookieName, sessionValue, { httpOnly: true, secure: !isDev, path: "/", maxAge: 30 * 24 * 60 * 60, // 30 days sameSite: "lax", ...(cookieDomain ? { domain: cookieDomain } : {}), }); redirect(targetRedirect); } else { console.log(`[Login] Failed for inputs. Redirecting back with error.`); redirect(`/login?error=1&redirect=${encodeURIComponent(targetRedirect)}`); } } return (
Infrastructure Protection
© 2026 MINTEL