Volver al blog Operaciones SMS

Concurrencia en el envío de OTP por SMS: estados, bloqueos y reenvíos

Una guía práctica para coordinar solicitudes de OTP concurrentes, reducir duplicados y tratar con prudencia los timeouts cuyo resultado de envío es incierto.

Diagrama de solicitudes de OTP por SMS coordinadas mediante estados y un bloqueo temporal

El problema: varias solicitudes para una sola verificación

Una pulsación repetida, dos pestañas abiertas o varias peticiones que llegan casi a la vez pueden iniciar más de un flujo de OTP. También puede ocurrir que dos trabajadores procesen simultáneamente solicitudes equivalentes. El resultado posible es generar códigos distintos, crear varios intentos de envío o dejar al usuario con un código que ya no coincide con el estado de la aplicación.

La limitación de solicitudes y el bloqueo temporal pueden ayudar a controlar la frecuencia, pero por sí solos no garantizan que dos procesos concurrentes no reclamen la misma operación. La coordinación debe resolverse también en el almacenamiento o mecanismo compartido que utiliza la aplicación.

  • Comprueba si las solicitudes concurrentes pertenecen a la misma verificación, no solo si llegaron desde el mismo número.
  • Separa la protección contra exceso de solicitudes de la prevención de procesamiento duplicado.
  • No interpretes la ausencia de una respuesta inmediata como prueba de que no se inició el envío.
El problema: varias solicitudes para una sola verificación

Separa la verificación, el código y el intento de envío

Modela por separado tres elementos: la solicitud de autenticación, el OTP asociado a esa solicitud y cada intento de enviar el mensaje. Esta separación facilita distinguir una nueva verificación de un reenvío y conservar el historial aunque cambie el estado del código.

Antes de definir reglas, establece qué significa «la misma operación» en tu producto. Una petición repetida para una verificación activa puede requerir un tratamiento distinto de una nueva verificación iniciada después de completar, cancelar o dejar caducar la anterior.

  • Asocia cada verificación a un identificador interno estable.
  • Registra cada intento de envío con su propio identificador y referencia a la verificación.
  • No uses el número de teléfono como única señal para deduplicar: un mismo usuario puede iniciar verificaciones legítimas con propósitos o sesiones distintos.
Separa la verificación, el código y el intento de envío

Define una clave de operación con un ámbito claro

Una clave de operación permite que las solicitudes equivalentes converjan en el mismo registro de control. Puede componerse de un identificador de usuario o sesión, el propósito de la autenticación y una ventana temporal definida por la política del servicio. La elección concreta depende de cómo se delimitan las sesiones y las verificaciones en la aplicación; no existe una combinación universal.

La ventana temporal debe servir para agrupar repeticiones de una operación, no para bloquear indefinidamente nuevas verificaciones. Mantén separadas la clave de deduplicación y las reglas de frecuencia o bloqueo temporal: cumplen objetivos relacionados, pero distintos.

  • Define qué elementos hacen que dos solicitudes sean equivalentes.
  • Usa una ventana explícita y documenta cuándo se considera iniciada una operación nueva.
  • Evita incluir datos sensibles innecesarios en una clave visible o en registros operativos.

Usa estados explícitos y reclamación atómica

Representa el ciclo con estados comprensibles, por ejemplo: solicitada, en procesamiento, envío aceptado por el proveedor, resultado incierto, completada, fallida o caducada. Adapta los nombres y transiciones a la información que realmente devuelve tu sistema y el proveedor; no marques como aceptado un envío solo porque se creó una tarea interna.

La transición que reclama una operación debe ser atómica. En términos prácticos, un trabajador intenta cambiar el registro de un estado reclamable a «en procesamiento» y solo continúa si esa transición se realizó. Si otro trabajador ya lo reclamó, el segundo debe consultar el estado existente y seguir la política definida, no iniciar otro envío por defecto.

La atomicidad depende del mecanismo de coordinación empleado. Un bloqueo en memoria de un único proceso no coordina por sí solo varias instancias de una aplicación. Comprueba que el control elegido funciona en todos los trabajadores que pueden recibir la misma operación.

  • Define los estados y transiciones permitidas antes de implementar los reintentos.
  • Haz que la reclamación y el registro del estado se realicen como una operación indivisible en el mecanismo compartido.
  • Decide qué respuesta recibe una petición repetida cuando la operación ya está en curso: esperar, consultar el estado o devolver una respuesta de «en curso».
  • Añade una forma controlada de recuperar operaciones abandonadas, sin convertir automáticamente toda operación antigua en un nuevo envío.

Define cuándo reutilizar el código y cuándo iniciar otro

Una solicitud duplicada durante una verificación todavía vigente puede tratarse como una consulta o repetición de la misma operación. Si la política de seguridad y experiencia de usuario lo permite, la aplicación puede conservar el OTP vigente y evitar generar otro código. Esta decisión debe coordinarse con la caducidad y con las reglas de invalidación que utilice el sistema.

Generar un código nuevo puede ser legítimo cuando comienza una verificación nueva, cuando el código anterior ya no es válido o cuando una política documentada exige reemplazarlo. En ese caso, determina qué ocurre con los códigos anteriores y asegúrate de que la validación aplique esa regla de forma coherente. No hay una política única adecuada para todas las aplicaciones.

  • Documenta qué evento constituye una nueva verificación y cuál es solo un reintento de la solicitud.
  • Establece cómo se comporta un OTP vigente ante una petición repetida.
  • Si se emite un código nuevo, define expresamente si el anterior sigue siendo válido o queda invalidado.
  • Alinea la respuesta de la interfaz con el estado real: no indiques que se envió un código nuevo si la aplicación no ha iniciado un nuevo intento.

Trata los timeouts como un resultado incierto

Si la conexión se interrumpe o vence el tiempo de espera después de iniciar una solicitud al proveedor, puede no ser posible determinar si el proveedor la recibió o la aceptó. El timeout describe lo que observó tu aplicación; no demuestra por sí mismo que el SMS no se haya enviado.

Registra el intento como incierto cuando no puedas confirmar el resultado. Antes de repetir, comprueba si la API o conexión de envío ofrece una clave de idempotencia, una consulta de estado u otro mecanismo documentado para reconocer la misma solicitud. Si no existe una forma verificable de resolverla, aplica una política explícita de riesgo y comunica la incertidumbre al sistema solicitante; evita el reintento automático y ciego.

  • Conserva el identificador interno del intento y la referencia devuelta por el proveedor, si existe.
  • Distingue «no se obtuvo respuesta» de «el proveedor rechazó la solicitud».
  • Si se autoriza un nuevo intento, regístralo como tal y conserva el vínculo con el intento incierto.
  • No prometas que el usuario recibirá un único SMS cuando el resultado del primer intento no puede confirmarse.

Deduplicación interna e idempotencia del proveedor no son lo mismo

La deduplicación interna evita que tu propia plataforma procese varias veces una operación que considera equivalente. La idempotencia de una API de envío, cuando está documentada y disponible, permite que el proveedor reconozca solicitudes repetidas según las reglas de esa API. Una no implica la otra.

Antes de depender de una garantía externa, consulta la documentación técnica aplicable a la API o conexión utilizada. Verifica el ámbito de la clave, cuánto tiempo se conserva, qué respuesta se devuelve ante una repetición y si cubre realmente el caso de timeout que necesitas resolver. No asumas que HTTP o SMPP, por sí solos, ofrecen deduplicación de negocio para un OTP.

  • Mantén un control interno aunque el proveedor ofrezca idempotencia.
  • No reenvíes la misma clave a ciegas sin conocer su ámbito y comportamiento documentados.
  • Si no hay una garantía documentada, trata los envíos repetidos como potencialmente duplicados.

Conserva trazabilidad y límites de interpretación

Registra el identificador de operación, el identificador de cada intento, las transiciones de estado, el motivo de un reenvío y el resultado informado por el proveedor. Limita los datos personales registrados y aplica controles de acceso y conservación acordes con tus obligaciones. Estos registros permiten reconstruir qué decidió cada componente cuando llegaron solicitudes concurrentes.

Un estado informado por el proveedor debe describirse como tal. No lo conviertas en una afirmación de recepción en el teléfono si no existe evidencia que lo demuestre. Los registros de envío sirven para operar y diagnosticar; no garantizan que el usuario haya recibido el SMS ni que haya completado la verificación.

  • Separa el resultado de la petición a tu API, el resultado comunicado por el proveedor y el resultado de la verificación del OTP.
  • Anota el motivo del reenvío y quién o qué componente lo autorizó.
  • Usa identificadores correlacionables en registros internos sin exponer el código OTP en telemetría.
  • Prueba solicitudes repetidas y paralelas, fallos entre transiciones y timeouts con resultados no confirmados.
FAQ

Preguntas frecuentes

¿Una clave de operación garantiza que solo se envíe un SMS?

No por sí sola. Puede ayudar a impedir que tu aplicación procese varias veces una operación equivalente, si la reclamación se coordina de forma atómica. No garantiza que un proveedor haya recibido una sola solicitud ni que el mensaje llegue una sola vez al terminal.

¿Debo generar un OTP nuevo cada vez que el usuario pulsa «reenviar»?

No hay una regla universal. Define si la acción repite una verificación vigente o inicia una nueva, y especifica cómo se trata el código anterior. La política debe ser coherente entre generación, caducidad, invalidación y validación.

¿Qué hacer cuando el envío termina en timeout?

Trátalo como incierto si no puedes saber si el proveedor recibió la solicitud. Consulta los mecanismos documentados disponibles y evita asumir que el mensaje no fue aceptado. Si se permite otro intento, registra la decisión y su vínculo con el intento anterior.

¿Un estado de entrega confirma que el SMS llegó al teléfono?

No hagas esa equivalencia sin evidencia específica. Registra el estado como resultado informado por el proveedor y distingue esa señal de la recepción comprobada en el terminal y de la finalización de la autenticación.

Fuentes consultadas

  1. NIST Digital Identity GuidelinesNIST
  2. OWASP Authentication Cheat SheetOWASP
  3. ClickSend: Envíos de OTP por SMSClickSend