83 lines
2.2 KiB
TypeScript
83 lines
2.2 KiB
TypeScript
|
|
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 response = 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 (!response.ok) {
|
||
|
|
const errorData = await response.json().catch(() => ({}));
|
||
|
|
return NextResponse.json(
|
||
|
|
{
|
||
|
|
error: errorData.message || 'Login failed',
|
||
|
|
status: response.status
|
||
|
|
},
|
||
|
|
{ status: response.status }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
|
||
|
|
const data = await response.json();
|
||
|
|
|
||
|
|
// Prepare response with user data (no token exposed)
|
||
|
|
const responseData = {
|
||
|
|
message: data.message,
|
||
|
|
user: data.user,
|
||
|
|
tenant: data.tenant,
|
||
|
|
menus: data.menus,
|
||
|
|
};
|
||
|
|
|
||
|
|
// Set HttpOnly cookie with token
|
||
|
|
const cookieOptions = [
|
||
|
|
`user_token=${data.user_token}`,
|
||
|
|
'HttpOnly', // ✅ JavaScript cannot access
|
||
|
|
'Secure', // ✅ HTTPS only (production)
|
||
|
|
'SameSite=Strict', // ✅ CSRF protection
|
||
|
|
'Path=/',
|
||
|
|
'Max-Age=604800', // 7 days
|
||
|
|
].join('; ');
|
||
|
|
|
||
|
|
console.log('✅ Login successful - Token stored in HttpOnly cookie');
|
||
|
|
|
||
|
|
return NextResponse.json(responseData, {
|
||
|
|
status: 200,
|
||
|
|
headers: {
|
||
|
|
'Set-Cookie': cookieOptions,
|
||
|
|
},
|
||
|
|
});
|
||
|
|
|
||
|
|
} catch (error) {
|
||
|
|
console.error('Login proxy error:', error);
|
||
|
|
return NextResponse.json(
|
||
|
|
{ error: 'Internal server error' },
|
||
|
|
{ status: 500 }
|
||
|
|
);
|
||
|
|
}
|
||
|
|
}
|