Concepto clave
La autenticación con cookies y sesiones es el mecanismo más robusto para manejar usuarios en aplicaciones web full-stack. Imagina que una cookie es como una tarjeta de identificación que el servidor le da al navegador, y la sesión es el expediente completo del usuario guardado en el servidor. Cada vez que el navegador visita el sitio, muestra su tarjeta (cookie), y el servidor consulta el expediente (sesión) para verificar quién es.
En SvelteKit, este flujo se integra naturalmente con las Server Actions y el manejo de estado global. A diferencia de soluciones cliente-only como JWT en localStorage, las cookies son automáticamente enviadas con cada request, lo que permite autenticación segura tanto en el servidor como en el cliente. Esto es crucial para aplicaciones performantes, ya que evita llamadas API redundantes y permite renderizado optimizado.
Cómo funciona en la práctica
El proceso sigue estos pasos:
- El usuario envía credenciales mediante un formulario que ejecuta una Server Action.
- La Server Action valida las credenciales en la base de datos.
- Si son válidas, crea una sesión en el servidor (por ejemplo, en una tabla de sesiones) y genera una cookie segura con un ID único.
- La cookie se envía al navegador con headers HTTP, y el navegador la almacena automáticamente.
- En requests futuros, SvelteKit verifica la cookie en hooks como
handleo load functions, recupera la sesión, y hace disponible el estado del usuario.
Este enfoque separa claramente la lógica: el servidor maneja la autenticación real, mientras el cliente solo necesita saber si el usuario está logueado o no, ideal para optimizar rendimiento.
Código en acción
Primero, configura una Server Action para login en src/routes/login/+page.server.js:
// Server Action para login
export const actions = {
default: async ({ cookies, request }) => {
const data = await request.formData();
const email = data.get('email');
const password = data.get('password');
// Validar con base de datos (ejemplo con Prisma)
const user = await prisma.user.findUnique({ where: { email } });
if (!user || !(await verifyPassword(password, user.passwordHash))) {
return { error: 'Credenciales inválidas' };
}
// Crear sesión en base de datos
const session = await prisma.session.create({
data: {
userId: user.id,
expiresAt: new Date(Date.now() + 1000 * 60 * 60 * 24 * 7) // 7 días
}
});
// Configurar cookie segura
cookies.set('session_id', session.id, {
path: '/',
httpOnly: true,
secure: process.env.NODE_ENV === 'production',
sameSite: 'strict',
maxAge: 60 * 60 * 24 * 7
});
return { success: true, redirect: '/dashboard' };
}
};Luego, en src/hooks.server.js, verifica la cookie en cada request:
// Hook para manejar autenticación global
export async function handle({ event, resolve }) {
const sessionId = event.cookies.get('session_id');
if (sessionId) {
// Recuperar sesión y usuario
const session = await prisma.session.findUnique({
where: { id: sessionId },
include: { user: true }
});
if (session && session.expiresAt > new Date()) {
event.locals.user = session.user; // Estado disponible globalmente
event.locals.session = session;
} else {
// Sesión expirada, limpiar cookie
event.cookies.delete('session_id', { path: '/' });
}
}
const response = await resolve(event);
return response;
}Errores comunes
- No configurar cookies como httpOnly: Deja las cookies accesibles desde JavaScript, exponiéndolas a ataques XSS. Siempre usa
httpOnly: truepara cookies de sesión. - Olvidar limpiar sesiones expiradas: Acumulan basura en la base de datos. Programa tareas periódicas o usa TTL en tu base de datos para eliminarlas automáticamente.
- No validar el origen de la cookie: Ataques CSRF pueden suplantar sesiones. Usa tokens CSRF o asegura que
sameSiteesté en 'strict' o 'lax'. - Almacenar datos sensibles en la cookie: La cookie solo debe contener un ID de sesión, nunca información del usuario. Todo lo demás va en el servidor.
- Ignorar el manejo de estado global: Sin integrar
event.localscon stores de Svelte, el cliente no sabe el estado de autenticación. Usa un store derivado para sincronizarlo.
Checklist de dominio
- Implementé una Server Action que valida credenciales y crea una sesión en base de datos.
- Configuré cookies con flags de seguridad:
httpOnly,secure,sameSite. - Integré un hook en
hooks.server.jsque verifica la cookie y populaevent.locals. - Creé un store de Svelte que refleja el estado de autenticación desde
event.locals. - Implementé logout que elimina la sesión de la base de datos y borra la cookie.
- Protegí rutas sensibles verificando
event.locals.useren load functions. - Probé el flujo completo en diferentes navegadores y dispositivos.
Implementa autenticación completa con cookies y sesiones en una app SvelteKit
En este ejercicio, construirás un sistema de autenticación desde cero en una aplicación SvelteKit existente. Sigue estos pasos:
- Crea una tabla de sesiones en tu base de datos (si usas Prisma, añade un modelo Session con id, userId, expiresAt).
- En
src/routes/login/+page.svelte, crea un formulario con campos email y password que use una Server Action. - Implementa la Server Action en
+page.server.jsque:- Valide las credenciales contra la tabla de usuarios.
- Cree una nueva sesión en la tabla de sesiones.
- Establezca una cookie segura con el sessionId.
- Modifica
src/hooks.server.jspara que en cada request:- Lea la cookie 'session_id'.
- Busque la sesión correspondiente en la base de datos.
- Si es válida, asigne el usuario a
event.locals.user. - Si no, limpie la cookie.
- Crea un store en
src/stores/auth.jsque exponga el estado del usuario, sincronizado conevent.locals. - Implementa una ruta
/dashboardque solo sea accesible para usuarios autenticados, mostrando su email. - Añade un botón de logout que ejecute una Server Action para eliminar la sesión y borrar la cookie.
Entrega: Código funcional que permita login, mantenga sesión entre recargas, y restrinja acceso a rutas protegidas.
Pistas- Usa
cookies.setcon opciones de seguridad: httpOnly, secure, sameSite. - En el hook, verifica que la sesión no haya expirado comparando
expiresAtcon la fecha actual. - Para el store, considera usar
writabley actualizarlo en una load function que lea deevent.locals.
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.