Ir al contenido principal

Devlog · Fase 0 · 4/7

La confirmación por correo en la lista de espera de aubia.dev

aubia.dev registra la solicitud antes de confirmarla por correo. Cómo el formulario detecta erratas, limita los abusos y trata las direcciones que siguen pendientes.

Publicado el 3 de agosto de 2026Actualizado el 13 de septiembre de 20268 min de lectura
  • waitlist
  • react
  • security
  • gdpr

Usted puede introducir la dirección de correo de otra persona en un formulario. Que esa dirección supere la validación no permite saber quién la ha enviado ni si su destinatario quiere recibir una invitación.

Por eso, en aubia.dev pido una confirmación por correo. El formulario crea una inscripción pendiente en aubia.cloud y un enlace enviado por correo permite al destinatario confirmarla. Este double opt-in añade un paso al proceso, con la posibilidad de que el mensaje nunca llegue a abrirse.

El sitio transmite la solicitud sin guardar las inscripciones en una base de datos local. El artículo sobre la ausencia de base de datos explica esta separación entre el sitio y el cloud.

El proceso de inscripción animado: envío del formulario a aubia.dev, solicitud firmada con HMAC a api.aubia.cloud, correo con un token y confirmación en un plazo de 48 horas.

Detectar una errata antes del envío

Una dirección mal escrita puede ser válida y no recibir nunca el mensaje previsto. El formulario propone una corrección cuando el dominio se parece al de un servicio de correo conocido: gmial.com podría convertirse en gmail.com.

La comparación utiliza la distancia de Levenshtein, que cuenta las modificaciones necesarias para transformar una cadena de caracteres en otra. Comprueba el dominio y, si no encuentra una corrección, su sufijo, a partir de listas limitadas. No consulta el buzón ni corrige la parte anterior a la @.

La sugerencia aparece al salir del campo. También se vuelve a calcular al enviar, porque un envío desde el teclado no siempre desplaza el foco fuera del campo. Si se detecta una posible errata, se detiene el primer envío para que usted pueda revisar la dirección.

Puede aplicar la corrección o conservar lo que ha escrito. Ambos botones devuelven el foco al campo sin enviar el formulario. También puede enviar la misma dirección una segunda vez sin haber elegido ninguna de las dos opciones: el parecido con un dominio conocido no basta para bloquear una dirección que usted sabe que es correcta.

La etiqueta está asociada al campo, los errores utilizan aria-invalid y role="alert", y aria-describedby vincula la sugerencia con el campo. La corrección debe poder leerse sin depender de un cambio de color.

Una respuesta inmediata, todavía provisional

El componente React envía el formulario mediante Inertia, sin recargar toda la página. En cuanto comienza el envío, activa una notificación global gestionada con useOptimistic. La respuesta visual aparece antes de que conteste el servidor.

Esta indicación anticipa un resultado satisfactorio. Antes de recibir la respuesta del servidor, el navegador aún no sabe si el cloud ha registrado la solicitud ni si se podrá entregar un correo. La respuesta real la sustituye por el mensaje correspondiente o revierte el estado provisional si se produce un error.

El cloud distingue cuatro resultados para una solicitud aceptada:

Resultado Tratamiento de la solicitud
confirmation_sent Se crea una inscripción pendiente y se añade el envío del correo de confirmación a la cola.
confirmation_resent Se prepara un nuevo enlace para una inscripción pendiente una vez transcurrido el plazo de espera para el reenvío.
already_pending Ya existe una solicitud reciente; no se inicia el envío de otro correo.
already_confirmed La dirección ya está confirmada; no hay que repetir la inscripción.

Cada resultado tiene un mensaje traducido a los seis idiomas del sitio. Prefiero indicar que una dirección ya está confirmada a dejar que su destinatario espere un correo que no se enviará.

El proxy también comprueba el código de estado HTTP. Los errores de validación, de límite de solicitudes y de servicio tienen prioridad sobre un posible estado de éxito en el cuerpo de la respuesta. El código HTTP 409, acompañado específicamente de already_confirmed, se reconoce como una inscripción ya confirmada. Los demás conflictos no se tratan como éxitos.

Una dirección rechazada y un límite de solicitudes superado tienen cada uno su propio mensaje. Una firma rechazada o un servicio cloud no disponible producen un mensaje de indisponibilidad temporal; los detalles técnicos sirven para el diagnóstico en el servidor.

Ni siquiera confirmation_sent demuestra que el correo se haya entregado. Un job en cola se encarga del envío: el servidor ha solicitado su ejecución, pero el envío aún puede fallar o el servicio de correo del destinatario puede filtrar el mensaje.

Limitar los abusos sin captcha

No añado un captcha al formulario. Exigiría una comprobación adicional al visitante sin impedir por completo los envíos automatizados. El sitio combina controles que no requieren ninguna interacción más.

Un honeypot, un campo oculto a la vista y excluido de la navegación con teclado, debe permanecer vacío. Permite rechazar los envíos de bots que rellenan los campos indiscriminadamente. Una trampa temporal compara una marca de tiempo facilitada por el navegador con el momento del envío y rechaza los envíos demasiado rápidos.

El servidor comprueba estos valores, pero proceden del cliente. Un programa puede dejar vacío el honeypot y proporcionar una marca de tiempo aceptable. Estas trampas filtran comportamientos simples; no demuestran que una persona haya rellenado el formulario.

Un rate limiter actúa en paralelo por dirección IP, por dirección de correo y sobre el volumen total de solicitudes. El límite por correo reduce las solicitudes repetidas dirigidas a un mismo destinatario. El tope global limita las solicitudes incluso cuando proceden de varias IP, con el riesgo de rechazar también inscripciones legítimas durante un aumento de tráfico.

La validación de la dirección comprueba el formato y, en producción, el DNS del dominio. El DNS puede ayudar a descartar un dominio que no pueda recibir correo, pero no permite determinar si existe ese buzón concreto.

Una vez que el sitio acepta la solicitud, la firma con HMAC antes de transmitirla al cloud. Los dos servidores comparten un secreto que permite verificar la firma del cuerpo recibido. Esta protección se aplica a la solicitud de inscripción entre servidores: impide sustituir el proxy por una llamada sin firmar a ese endpoint. No todas las rutas de la API exigen esta firma, en particular las que utilizan tokens de confirmación o de baja.

El enlace recibido por correo

El cloud registra la dirección con el estado PENDING antes de preparar el correo. El enlace contiene un token aleatorio del que solo se guarda el hash en el registro de inscripción. Es válido durante 48 horas a partir de la fecha de envío registrada por la aplicación, antes de que se ejecute el job de correo.

El botón de confirmación abre una página de aubia.dev en el idioma de la inscripción. Tras cargarse, la página lee el token de la URL y consulta directamente la API cloud. Muestra el resultado: confirmado, ya confirmado, enlace inválido, no encontrado o caducado.

La confirmación actualiza el estado y su fecha una sola vez. Dos solicitudes simultáneas no deben provocar dos confirmaciones. Otro clic en el mismo enlace reconocido devuelve "ya confirmado", incluso después del plazo de validez original, porque la inscripción ya se ha completado.

Para una dirección todavía pendiente, el reenvío sustituye el hash del token e inicia un nuevo plazo de validez. El enlace anterior ya no permite encontrar la inscripción. El reenvío invalida el enlace anterior desde el momento en que se prepara, aunque el nuevo correo aún no haya llegado.

Las páginas de confirmación y de baja declaran noindex. Esta directiva pide a los buscadores que no las indexen; no restringe el acceso. Los enlaces del selector de idioma se reconstruyen sin la cadena de consulta para evitar copiar el token en otras cinco URL. Por tanto, al cambiar de idioma no se continúa la confirmación con ese token.

Un programa capaz de seguir el enlace y ejecutar este proceso también puede confirmar. El double opt-in comprueba el uso de un enlace enviado a la dirección facilitada, sin certificar la identidad ni la presencia humana de quien lo utiliza. La marca de tiempo de la confirmación ayuda a seguir la inscripción; por sí sola, no basta para demostrar que todo el tratamiento cumple el RGPD.

Las direcciones que siguen pendientes

La caducidad del token no elimina el registro. Una tarea diaria elimina las inscripciones que aún tienen el estado PENDING y cuya fecha de inscripción original es de hace más de 30 días. Reenviar una confirmación no reinicia este plazo de conservación.

La baja sigue otro proceso. La página dedicada envía el token al cloud, que anonimiza el registro: se sustituye la dirección de correo, se borran los tokens y los datos de atribución, y el estado pasa a PURGED. La fila permanece, junto con sus fechas.

El botón de confirmación del cuerpo del correo es independiente de las cabeceras List-Unsubscribe y List-Unsubscribe-Post. Estas cabeceras proporcionan instrucciones a los clientes de correo para ofrecer una opción de baja. Su presencia no garantiza que todos los clientes muestren esa opción o la procesen de la misma manera.

Qué mide el contador

El contador de la página de inicio consulta al cloud el número de inscripciones con el estado CONFIRMED. Excluye las direcciones pendientes. También cuenta las invitaciones administrativas registradas directamente como confirmadas, por lo que el total no corresponde exclusivamente a clics en los enlaces del formulario público.

El valor llega después del renderizado inicial, mediante una solicitud diferida, con una caché en el servidor. Puede reflejar una confirmación con retraso y solo se muestra por encima de un umbral. El artículo sobre Octane e Inertia SSR explica cómo se carga.

Para medir cuántas solicitudes del formulario llegan a confirmarse, tengo que distinguirlas de las invitaciones administrativas.

Esta obra se cuenta aquí, artículo a artículo. Lo que viene después depende de lo que usted diga de ella.

Únase a la lista de espera