Ir al contenido

Sistema de Autenticación

El sistema de autenticación de Te Afirmo utiliza sesiones basadas en cookies, verificación de email con TOTP, y hash seguro de contraseñas.

Endpoint: POST /api/auth/register

Flujo:

  1. Usuario proporciona email, contraseña, nombre y rol
  2. Se valida con Zod schema
  3. Se sanitiza el email
  4. Se crea hash de la contraseña con bcrypt
  5. Se genera código TOTP para verificación
  6. Se guarda en base de datos (Turso)
  7. Se envía email con código de verificación

Roles disponibles:

  • natural - Persona Natural
  • juridica - Persona Jurídica

Endpoint: POST /api/auth/verify

Flujo:

  1. Usuario proporciona email y código TOTP
  2. Se valida el código contra el secret almacenado
  3. Se actualiza email_verified = 1 en la base de datos
  4. Se envía email de bienvenida

Endpoint: POST /api/auth/login

Flujo:

  1. Usuario proporciona email, contraseña y selecciona el perfil con el que desea entrar
  2. Se busca usuario en la base de datos y se valida que el perfil esté permitido
  3. Se verifica que el email esté verificado
  4. Se compara la contraseña con bcrypt
  5. Se crea una sesión guardando el rol efectivo
  6. Se devuelve cookie de sesión

Archivo: src/utils/auth.ts

Funciones principales:

  • createSession(userId, role) - Crea una nueva sesión con el rol efectivo
  • destroySession(token) - Elimina una sesión
  • getSessionFromRequest(request) - Obtiene sesión desde request
  • requireSession(request, options) - Requiere sesión válida

Cookie de sesión:

  • Nombre: teafirmo_session
  • HttpOnly: Sí (seguridad)
  • SameSite: Strict
  • Duración: 24 horas
import bcrypt from 'bcryptjs';
// Crear hash
const hash = await bcrypt.hash(password, 10);
// Verificar
const matches = await bcrypt.compare(password, hash);

Todos los inputs se sanitizan antes de procesarse:

import { sanitizeEmail, sanitizeString } from '../utils/sanitize';
const email = sanitizeEmail(rawEmail);
const name = sanitizeString(rawName);

Se usa Zod para validación de esquemas:

import { registerSchema, validateWithSchema } from '../types/validation';
const validation = validateWithSchema(registerSchema, data);
if (!validation.success) {
// Manejar errores
}
CREATE TABLE users (
id INTEGER PRIMARY KEY AUTOINCREMENT,
email TEXT NOT NULL UNIQUE,
password_hash TEXT NOT NULL,
role TEXT NOT NULL CHECK(role IN ('natural', 'juridica', 'admin')),
available_roles TEXT,
email_verified INTEGER NOT NULL DEFAULT 0,
totp_secret TEXT,
totp_issued_at INTEGER,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now'))
);
CREATE TABLE sessions (
id INTEGER PRIMARY KEY AUTOINCREMENT,
user_id INTEGER NOT NULL,
token TEXT NOT NULL UNIQUE,
role TEXT,
expires_at INTEGER NOT NULL,
created_at INTEGER NOT NULL DEFAULT (strftime('%s', 'now')),
FOREIGN KEY (user_id) REFERENCES users(id)
);
  • users.available_roles almacena (en JSON) los perfiles extra habilitados para una cuenta específica.
  • El rol principal sigue definido por users.role y siempre se incluye automáticamente.
  • Al iniciar sesión se valida que el perfil solicitado pertenezca a {rol principal} ∪ available_roles.
  • El rol efectivo se persiste en sessions.role, por lo que cada sesión recuerda con qué perfil se autenticó el usuario.
  • Los endpoints protegidos leen ese rol efectivo mediante requireSession para determinar si el acceso corresponde a Persona Natural, Persona Jurídica o Administración.
  1. Registro → Usuario se registra → Recibe código TOTP
  2. Verificación → Usuario verifica email → Cuenta activada
  3. Login → Usuario inicia sesión → Recibe cookie de sesión
  4. Acceso → Usuario accede a recursos protegidos → Sesión validada