Proceso de Log In

Sábado 26 11:00 PM - Domingo 27 12:20 PM

Esta entrada del blog documentará una sola sesión intensa que duró desde el sábado por la noche hasta el domingo al mediodía. La meta fue implementar un sistema de login con lógica para bloquear IPs después de múltiples intentos fallidos, registrar eventos en la bitácora de la base de datos, y crear toda la integración backend/frontend. Aunque parecía una tarea relativamente sencilla, nos tomó más de 8 horas de tiempo programando debido a varios desafíos que surgieron.

Concepto inicial
Cada intento fallido de login se registrara en una tabla BitacoraEvento.
Después de 5 intentos fallidos en 30 minutos, la IP quedara bloqueada por 10 minutos.
Estos eventos quedarían reflejados en una tabla relacionada con TipoEvento:
2 = Login no exitoso.
3 = Login bloqueado (lockout).

Obstáculos que surgieron:

1. La columna id en BitacoraEvento
Cuando intentamos registrar los eventos fallidos, recibimos un error:
Cannot insert the value NULL into column 'id'...
Esto fue porque la columna id no tenía configurado un IDENTITY(1,1) y esperaba que se le asignara manualmente un valor. No queríamos manejar manualmente las IDs, así que decidimos modificar la tabla para que el campo id se auto-generara. Esto nos ahorró muchos problemas.

2. El error de conexión tras limpiar intentos fallidos
Después de implementar la limpieza de intentos fallidos cuando el bloqueo expiraba, comenzamos a recibir errores 500 que mostraban “Error de conexión” en el frontend. Esto nos tuvo un buen rato perdidos hasta que revisamos bien los logs y descubrimos que:

const checkLockoutRequest = new sql.Request(connection);

pero nunca se había inicializado connection en esa parte del código. Un pequeño problema creado al tratar de reutilizar código ya creado en otras partes. La solución fue abrir la conexión una sola vez al principio del controlador y reutilizarla para todas las consultas.

3. Lógica para limpiar intentos fallidos tras el bloqueo

Descubrimos que, aunque el bloqueo (evento tipo 3) expiraba después de 10 minutos, los intentos fallidos (eventos tipo 2) seguían contando, porque no se limpiaban automáticamente. Esto provocaba que al siguiente intento el usuario se bloqueara de nuevo instantáneamente.

La solución fue limpiar los intentos fallidos cuando el bloqueo expiraba, asegurándonos de dar un "borrón y cuenta nueva" después de cada periodo de bloqueo.

Finalmente, mejoramos el frontend para:

  1. Consultar si la IP estaba bloqueada al cargar la página.

  2. Deshabilitar los campos de usuario y contraseña si estaba bloqueada.

  3. Mostrar un contador de minutos restantes.

Además, aplicamos algunos cambios visuales para que el login se viera más amigable.

Github commit

Comentarios

Entradas más populares de este blog

CRUD empleado completo

Creación de tablas y código del backend

Correcciones, primeras llamadas a BD y llenar las tablas