'use server'; import { cookies } from 'next/headers'; import { redirect } from 'next/navigation'; const API_URL = process.env.API_INTERNAL_URL || process.env.NEXT_PUBLIC_API_URL || 'http://localhost:3001'; export interface AuthUser { id: string; username: string; displayName: string | null; role: 'SUPER_ADMIN' | 'ADMIN' | 'USER'; tenantId: string; mustChangePassword: boolean; } export interface LoginResult { success: boolean; error?: string; user?: AuthUser; } /** * Login action: POST credentials to API, forward session cookie. * In development, the API runs on a different port (3001) so we * must manually forward the Set-Cookie header from the API response. */ export async function login(formData: FormData): Promise { const username = formData.get('username') as string; const password = formData.get('password') as string; const rememberMe = formData.get('rememberMe') === 'on'; if (!username || !password) { return { success: false, error: 'invalidCredentials' }; } try { const response = await fetch(`${API_URL}/auth/login`, { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ username, password }), }); if (!response.ok) { return { success: false, error: 'invalidCredentials' }; } const user: AuthUser = await response.json(); // Forward the session cookie from the API response to the browser const setCookieHeader = response.headers.get('set-cookie'); if (setCookieHeader) { const sessionMatch = setCookieHeader.match(/session=([^;]+)/); if (sessionMatch) { const cookieStore = await cookies(); cookieStore.set('session', sessionMatch[1], { httpOnly: true, secure: process.env.NODE_ENV === 'production', sameSite: 'lax', ...(rememberMe ? { maxAge: 30 * 24 * 60 * 60 } : {}), path: '/', }); } } return { success: true, user }; } catch { return { success: false, error: 'networkError' }; } } /** * Logout action: POST to API, clear local cookie, redirect to /login. */ export async function logout(): Promise { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value; try { await fetch(`${API_URL}/auth/logout`, { method: 'POST', headers: { 'Content-Type': 'application/json', ...(session ? { Cookie: `session=${session}` } : {}), }, credentials: 'include', }); } catch { // Logout should still clear the cookie even if API call fails } cookieStore.delete('session'); redirect('/login'); } /** * Fetch the current authenticated user from the API. * Uses the session cookie for authentication. */ export async function fetchCurrentUser(): Promise { const cookieStore = await cookies(); const session = cookieStore.get('session')?.value; if (!session) { return null; } try { const response = await fetch(`${API_URL}/auth/me`, { headers: { Cookie: `session=${session}`, }, credentials: 'include', cache: 'no-store', }); if (!response.ok) { return null; } return await response.json(); } catch { return null; } }