Concepto clave
En aplicaciones web modernas, la autenticación de usuarios es como el sistema de seguridad de un edificio inteligente. No solo verifica quién entra (login), sino que también mantiene un perfil personalizado para cada residente (sesión de usuario) y controla a qué áreas puede acceder (autorización). En SvelteKit, implementamos esto combinando Server Actions para operaciones seguras en el backend con un estado global en el frontend que sincroniza la experiencia del usuario en tiempo real.
Imagina que construyes una aplicación de gestión de proyectos. Sin autenticación, cualquiera podría ver y modificar todos los proyectos. Con un sistema de login, cada usuario solo accede a sus proyectos asignados, y su perfil muestra información personalizada como avatar, preferencias y actividad reciente. La magia está en cómo SvelteKit maneja esta transición entre cliente y servidor de forma eficiente, manteniendo la seguridad sin sacrificar performance.
Cómo funciona en la práctica
Vamos a implementar un flujo completo de autenticación en 4 pasos:
- Formulario de login: Creamos una interfaz en el cliente que captura email y contraseña
- Server Action de validación: Enviamos las credenciales al servidor para verificación segura
- Gestión de sesión: Almacenamos el token o datos de usuario en cookies seguras
- Perfil de usuario: Mostramos información personalizada basada en la sesión activa
La clave está en que las operaciones sensibles (verificación de contraseña, acceso a base de datos) ocurren exclusivamente en el servidor mediante Server Actions, mientras que la UI reacciona inmediatamente a los cambios de estado.
Codigo en accion
Primero, creamos un store para el estado global del usuario:
// src/stores/userStore.js
import { writable } from 'svelte/store';
export const user = writable(null);
export async function loginUser(email, password) {
const response = await fetch('/api/auth/login', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ email, password })
});
if (response.ok) {
const userData = await response.json();
user.set(userData);
return { success: true };
} else {
return { success: false, error: 'Credenciales incorrectas' };
}
}
export function logoutUser() {
user.set(null);
// También limpiar cookie de sesión
}Ahora implementamos la Server Action para el login:
// src/routes/api/auth/login/+server.js
import { json } from '@sveltejs/kit';
import bcrypt from 'bcryptjs';
import { db } from '$lib/database';
export async function POST({ request, cookies }) {
try {
const { email, password } = await request.json();
// Buscar usuario en la base de datos
const user = await db.user.findUnique({
where: { email },
select: { id: true, name: true, email: true, passwordHash: true }
});
if (!user) {
return json({ error: 'Usuario no encontrado' }, { status: 401 });
}
// Verificar contraseña
const passwordValid = await bcrypt.compare(password, user.passwordHash);
if (!passwordValid) {
return json({ error: 'Contraseña incorrecta' }, { status: 401 });
}
// Crear sesión (simplificado - en producción usar JWT o similar)
const sessionId = crypto.randomUUID();
await db.session.create({
data: {
id: sessionId,
userId: user.id,
expiresAt: new Date(Date.now() + 7 * 24 * 60 * 60 * 1000) // 7 días
}
});
// Establecer cookie segura
cookies.set('session_id', sessionId, {
path: '/',
httpOnly: true,
sameSite: 'strict',
secure: process.env.NODE_ENV === 'production',
maxAge: 60 * 60 * 24 * 7 // 7 días
});
// Devolver datos de usuario (sin información sensible)
return json({
id: user.id,
name: user.name,
email: user.email
});
} catch (error) {
console.error('Login error:', error);
return json({ error: 'Error interno del servidor' }, { status: 500 });
}
}Errores comunes
- Almacenar datos sensibles en el cliente: Nunca guardes tokens JWT completos o información personal en localStorage. Usa cookies httpOnly para datos sensibles y stores solo para información de UI.
- Falta de validación en Server Actions: Siempre valida y sanitiza los inputs en el servidor, incluso si ya lo hiciste en el cliente. Un atacante puede saltarse la validación del frontend.
- No manejar estados de carga y error: Los usuarios necesitan feedback visual cuando el login está procesándose o falla. Implementa estados de loading y mensajes de error claros.
- Olvidar la persistencia de sesión: Al recargar la página, el store se reinicia. Usa hooks como +layout.server.js para rehidratar el estado del usuario desde la sesión del servidor.
- Exponer información de error detallada: Mensajes como "Email no existe" o "Contraseña incorrecta" dan pistas a atacantes. Usa mensajes genéricos como "Credenciales inválidas".
Checklist de dominio
- ✅ Implementé un formulario de login que se comunica con una Server Action
- ✅ Validé credenciales en el servidor usando hash seguro (bcrypt)
- ✅ Establecí cookies httpOnly para manejo seguro de sesiones
- ✅ Creé un store de Svelte para el estado global del usuario
- ✅ Implementé una página de perfil que muestra datos del usuario autenticado
- ✅ Agregué manejo de estados (loading, error, success) en la UI
- ✅ Protegí rutas privadas redirigiendo usuarios no autenticados
Implementa un sistema completo de autenticación con perfil de usuario
En tu aplicación SvelteKit existente, implementa un sistema de autenticación completo que incluya:
- Crea una página de login en
/logincon formulario que capture email y contraseña - Implementa la Server Action correspondiente en
/api/auth/loginque:- Valide las credenciales contra una base de datos (usa bcrypt para comparar hashes)
- Genere una sesión segura y establezca una cookie httpOnly
- Devuelva los datos básicos del usuario (id, nombre, email)
- Crea un store global (
userStore.js) que:- Mantenga el estado del usuario autenticado
- Provea funciones
loginUserylogoutUser - Sincronice con la sesión del servidor al cargar la app
- Desarrolla una página de perfil en
/profileque:- Muestre los datos del usuario actual
- Solo sea accesible para usuarios autenticados
- Permita cerrar sesión
- Agrega un componente de navegación que cambie dinámicamente:
- Muestre "Login" cuando no hay usuario autenticado
- Muestre "Perfil" y "Cerrar sesión" cuando hay usuario autenticado
Requisitos técnicos:
- Usa bcryptjs para hashing de contraseñas
- Implementa cookies con flags httpOnly y secure
- Maneja estados de loading y error en la UI
- Protege rutas privadas con redirecciones
- Usa el hook +layout.server.js para verificar la sesión en cada carga de página y pasar el usuario al cliente
- Para la base de datos, puedes empezar con un array en memoria, pero documenta cómo se conectaría a PostgreSQL o MySQL
- Implementa un middleware de autenticación en src/hooks.server.js para proteger rutas de forma centralizada
Evalua tu comprension
Completa el quiz interactivo de arriba para ganar XP.