import { redirect } from '@sveltejs/kit'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ url, cookies, fetch }) => { // Obtener el código y state de los query params const code = url.searchParams.get('code'); const state = url.searchParams.get('state'); const errorParam = url.searchParams.get('error'); const errorDescription = url.searchParams.get('error_description'); if (errorParam) { console.error('❌ [Callback Server] Error en autenticación:', errorParam, errorDescription); throw redirect(303, `/login?error=${encodeURIComponent(errorDescription || errorParam)}`); } if (!code) { console.error('❌ [Callback Server] No se recibió código de autorización'); throw redirect(303, '/login?error=No se recibió código de autorización'); } try { // Intercambiar código por tokens usando el backend de Keycloak // En el servidor (SSR), usar KEYCLOAK_URL que apunta a http://keycloak:8080 // En producción o fuera de Docker, usar VITE_KEYCLOAK_URL como fallback const KEYCLOAK_URL = process.env.KEYCLOAK_URL || process.env.VITE_KEYCLOAK_URL || 'http://localhost:8080'; const KEYCLOAK_REALM = process.env.KEYCLOAK_REALM || process.env.VITE_KEYCLOAK_REALM || 'master'; const KEYCLOAK_CLIENT_ID = process.env.KEYCLOAK_CLIENT_ID || process.env.VITE_KEYCLOAK_CLIENT_ID || 'anexo76-backend'; const KEYCLOAK_CLIENT_SECRET = process.env.KEYCLOAK_CLIENT_SECRET || ''; // La redirect_uri debe coincidir exactamente con la registrada en Keycloak const redirectUri = `${url.origin}/auth/callback`; const tokenEndpoint = `${KEYCLOAK_URL}/realms/${KEYCLOAK_REALM}/protocol/openid-connect/token`; const body = new URLSearchParams({ grant_type: 'authorization_code', code: code, redirect_uri: redirectUri, client_id: KEYCLOAK_CLIENT_ID, ...(KEYCLOAK_CLIENT_SECRET && { client_secret: KEYCLOAK_CLIENT_SECRET }) }); const tokenResponse = await fetch(tokenEndpoint, { method: 'POST', headers: { 'Content-Type': 'application/x-www-form-urlencoded' }, body: body.toString() }); if (!tokenResponse.ok) { const errorData = await tokenResponse.text(); console.error('❌ [Callback Server] Error al intercambiar código:', errorData); throw new Error('Error al obtener tokens'); } const tokens = await tokenResponse.json(); // Establecer las cookies en el servidor (esto es lo importante) // Las cookies deben ser HttpOnly y Secure en producción const isProduction = process.env.NODE_ENV === 'production'; cookies.set('access_token', tokens.access_token, { path: '/', httpOnly: true, secure: isProduction, sameSite: 'lax', maxAge: 60 * 60 * 24 * 7 // 7 días }); if (tokens.refresh_token) { cookies.set('refresh_token', tokens.refresh_token, { path: '/', httpOnly: true, secure: isProduction, sameSite: 'lax', maxAge: 60 * 60 * 24 * 30 // 30 días }); } // Obtener la URL de redirección del state o ir al dashboard let redirectTo = '/dashboard'; if (state) { try { const stateObj = JSON.parse(state); redirectTo = stateObj.redirect_url || '/dashboard'; } catch (e) { console.warn('⚠️ [Callback Server] No se pudo obtener redirect_url del state'); } } // Redirigir a la página de destino throw redirect(303, redirectTo); } catch (err: any) { console.error('❌ [Callback Server] Error procesando autenticación:', err); throw redirect(303, `/login?error=${encodeURIComponent(err.message || 'Error procesando autenticación')}`); } };