/** * API route authentication helper * Validates user session for protected API endpoints */ import { API_URL } from '@/config'; /** * Check if the request has a valid authentication session * @param {Request} request - The incoming request * @returns {Promise<{user: object|null, error: Response|null, setCookieHeader: string|null}>} */ export async function requireApiAuth(request) { try { const cookieHeader = request.headers.get('cookie') ?? ''; if (!cookieHeader) { return { user: null, error: new Response(JSON.stringify({ error: 'Authentication required' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }), setCookieHeader: null, }; } // Try to get user identity let res = await fetch(`${API_URL}/auth/id`, { headers: { Cookie: cookieHeader }, credentials: 'include', }); let newSetCookieHeader = null; // If unauthorized, try to refresh the token if (res.status === 401) { const refreshRes = await fetch(`${API_URL}/auth/refresh`, { method: 'POST', headers: { Cookie: cookieHeader }, credentials: 'include', }); if (!refreshRes.ok) { return { user: null, error: new Response(JSON.stringify({ error: 'Session expired' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }), setCookieHeader: null, }; } // Capture the Set-Cookie header from the refresh response to forward to client newSetCookieHeader = refreshRes.headers.get('set-cookie'); let newCookieHeader = cookieHeader; if (newSetCookieHeader) { const cookiesArray = newSetCookieHeader.split(/,(?=\s*\w+=)/); newCookieHeader = cookiesArray.map(c => c.split(';')[0]).join('; '); } else { return { user: null, error: new Response(JSON.stringify({ error: 'Session refresh failed' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }), setCookieHeader: null, }; } res = await fetch(`${API_URL}/auth/id`, { headers: { Cookie: newCookieHeader }, credentials: 'include', }); } if (!res.ok) { return { user: null, error: new Response(JSON.stringify({ error: 'Authentication failed' }), { status: 401, headers: { 'Content-Type': 'application/json' }, }), setCookieHeader: null, }; } const user = await res.json(); return { user, error: null, setCookieHeader: newSetCookieHeader }; } catch (err) { console.error('API auth error:', err); return { user: null, error: new Response(JSON.stringify({ error: 'Authentication error' }), { status: 500, headers: { 'Content-Type': 'application/json' }, }), setCookieHeader: null, }; } } /** * Helper to create a response with optional Set-Cookie header forwarding * @param {any} data - Response data * @param {number} status - HTTP status code * @param {string|null} setCookieHeader - Set-Cookie header from auth refresh */ export function createApiResponse(data, status = 200, setCookieHeader = null) { const headers = { 'Content-Type': 'application/json' }; if (setCookieHeader) { headers['Set-Cookie'] = setCookieHeader; } return new Response(JSON.stringify(data), { status, headers }); }