import { NextResponse } from 'next/server'; import type { NextRequest } from 'next/server'; /** * Login Proxy Route Handler * * Purpose: * - Proxy login requests to PHP backend * - Store token in HttpOnly cookie (XSS protection) * - Never expose token to client JavaScript */ export async function POST(request: NextRequest) { try { const body = await request.json(); const { user_id, user_pwd } = body; // Validate input if (!user_id || !user_pwd) { return NextResponse.json( { error: 'User ID and password are required' }, { status: 400 } ); } // Call PHP backend API const backendResponse = await fetch(`${process.env.NEXT_PUBLIC_API_URL}/api/v1/login`, { method: 'POST', headers: { 'Content-Type': 'application/json', 'Accept': 'application/json', 'X-API-KEY': process.env.NEXT_PUBLIC_API_KEY || '', }, body: JSON.stringify({ user_id, user_pwd }), }); if (!backendResponse.ok) { // Don't expose detailed backend error messages to client // Use generic error messages based on status code let errorMessage = 'Authentication failed'; if (backendResponse.status === 422) { errorMessage = 'Invalid credentials provided'; } else if (backendResponse.status === 429) { errorMessage = 'Too many login attempts. Please try again later'; } else if (backendResponse.status >= 500) { errorMessage = 'Service temporarily unavailable'; } return NextResponse.json( { error: errorMessage }, { status: backendResponse.status === 422 ? 401 : backendResponse.status } ); } const data = await backendResponse.json(); // Prepare response with user data (no token exposed) const responseData = { message: data.message, user: data.user, tenant: data.tenant, menus: data.menus, token_type: data.token_type, expires_in: data.expires_in, expires_at: data.expires_at, }; // Set HttpOnly cookies for both access_token and refresh_token const accessTokenCookie = [ `access_token=${data.access_token}`, 'HttpOnly', // ✅ JavaScript cannot access 'Secure', // ✅ HTTPS only (production) 'SameSite=Strict', // ✅ CSRF protection 'Path=/', `Max-Age=${data.expires_in || 7200}`, // Use backend expiry (default 2 hours) ].join('; '); const refreshTokenCookie = [ `refresh_token=${data.refresh_token}`, 'HttpOnly', // ✅ JavaScript cannot access 'Secure', // ✅ HTTPS only (production) 'SameSite=Strict', // ✅ CSRF protection 'Path=/', 'Max-Age=604800', // 7 days (longer for refresh token) ].join('; '); console.log('✅ Login successful - Access & Refresh tokens stored in HttpOnly cookies'); const response = NextResponse.json(responseData, { status: 200 }); response.headers.append('Set-Cookie', accessTokenCookie); response.headers.append('Set-Cookie', refreshTokenCookie); return response; } catch (error) { console.error('Login proxy error:', error); return NextResponse.json( { error: 'Internal server error' }, { status: 500 } ); } }