
Compartir:
Andrea Chiarelli has over 20 years of experience as a software engineer and technical writer. Throughout his career, he has used various technologies for the projects he was involved in. Currently, he is a software architect at the Italian office of Apparound and contributes to a few online magazines and blogs.
Creación de un servicio de contraseña de un solo uso (OTP) mediante Dispatch API
Tiempo de lectura: 12 minutos
Aviso de caducidad del producto A partir del 31 de agosto de 2025, la Dispatch API de Vonage estará cerrada a nuevos usuarios, aunque el producto seguirá siendo compatible para los usuarios existentes. Si deseas crear una aplicación de mensajería con funcionalidad de conmutación por error, ahora la API de Messages API admite directamente la conmutación por error.
Para obtener información general sobre la función de conmutación por error de mensajes, consulte esta guía. Para obtener información sobre la migración de Dispatch API a Messages API Failover, consulte esta guía.
Si tiene más preguntas sobre esta eliminación de productos, póngase en contacto con nosotros en la dirección comunícate con nosotros en Slack de la comunidad de Vonage.
Las contraseñas de un solo uso (OTP) se han hecho bastante familiares en los últimos tiempos, principalmente debido a un requisito de seguridad que las contraseñas tradicionales no garantizan. Mientras que la protección de la contraseña tradicional es responsabilidad del usuario, que como bien sabemos a menudo no se preocupa lo suficiente, la OTP está prácticamente autoprotegida porque se genera aleatoriamente, y su validez está limitada en el tiempo.
Puedes utilizar OTPs en lugar de las contraseñas tradicionales, o para reforzar el proceso de autenticación tradicional con un enfoque de autenticación de dos factores (2FA). En realidad, puedes utilizar las OTP siempre que necesites un mecanismo que garantice la identidad de un usuario basándose en un medio de comunicación de su propiedad: un buzón de correo, un teléfono, una app específica, etc.
En este artículo veremos cómo implementar un servicio OTP básico basado en dos Web APIs:
la primera API permite crear la OTP y enviarla al usuario a través de Facebook Messenger como medio principal, o a través de SMS como medio alternativo
la segunda API permite al usuario verificar la OTP que ha recibido
El servicio OTP no tiene interfaz de usuario. Está concebido como un microservicio que puedes invocar desde tu aplicación para generar y verificar OTPs.
Requisitos previos
Para utilizar el servicio OTP mostrado en este artículo, necesitas:
Node.js 8.11+ instalado en su ordenador
A Messenger Account y un teléfono habilitado para recibir SMS
Una aplicación para enviar peticiones HTTP, como por ejemplo curl o Postman
Vonage API Account
To complete this tutorial, you will need a Vonage API account. If you don’t have one already, you can sign up today and start building with free credit. Once you have an account, you can find your API Key and API Secret at the top of the Vonage API Dashboard.
Configurar el proyecto
Como primer paso, debes clonar o descargar el proyecto del repositorio de GitHub.
Una vez que tenga el código del proyecto en su ordenador, deberá instalar sus dependencias accediendo a la carpeta del proyecto y escribiendo el siguiente comando:
Como veremos más adelante, la aplicación utiliza Express como framework web y la librería cliente de Vonage para Node.js, con el fin de enviar la OTP al usuario.
Configurar la aplicación
Antes de utilizar el servicio OTP, debes realizar algunas configuraciones en el panel de control de la API de Vonage para habilitar el envío de mensajes a través de la Dispatch API de Vonage.
Esta API te permite enviar mensajes a tus usuarios utilizando múltiples canales con priorización. Por ejemplo, en nuestro caso, enviaremos la OTP al usuario a través de su Account de Messenger, como primer intento. Si el usuario no lo lee en un tiempo determinado, el mensaje se enviará por SMS a su número de teléfono.
Por lo tanto, accede a tu panel de control de la API de Vonage y selecciona el elemento Messages and Dispatch en el menú y luego selecciona Crear una aplicacióncomo se muestra en la siguiente imagen:
Vonage Dashboard
En la jerga de la API de Vonage, una aplicación es un grupo de datos que te permiten usar la Messages y la Dispatch API. Como podemos ver en el formulario anterior, los datos mínimos son:
el nombre de la aplicación
la URL de un webhook público habilitado para recibir el estado de entrega de los mensajes
la URL de un webhook público habilitado para recibir mensajes entrantes
la clave pública utilizada para firmar la solicitud que envíe a la API (puede generar un par de claves pública y privada haciendo clic en el enlace situado bajo el área de texto)
Los pasos dos y tres del proceso de creación de aplicaciones de mensajes te permiten asignar Numbers de teléfono y/o enlaces a cuentas externas de servicios como Messenger, WhatsApp o Viber para que puedas enviar SMS o mensajes a través de la API de Mensajes y Dispatch.
En particular, véase este documento para saber cómo vincular tu página de Facebook con tu cuenta API de Vonage.
Ten en cuenta que el estado y las URL de entrada que se proporcionarán a una aplicación API de Vonage deben ser de acceso público. Si no tienes un servidor web público o sólo quieres probar esto en tu computadora, utiliza ngrokuna herramienta que te permite exponer públicamente tu servidor web local.
Puede encontrar más detalles sobre cómo trabajar con Ngrok en nuestra documentación. Tenga en cuenta que, si utiliza el plan gratuito de ngrok, se generará una URL temporal cada vez que ejecute la herramienta. Por lo tanto, es necesario actualizar las direcciones URL de la aplicación en la configuración de su tablero de instrumentos en consecuencia.
Luego de crear tu aplicación API de Vonage, se le asignará un Application ID se le asignará. Toma nota de ello.
Configurar el servicio OTP
Una vez que hayas configurado el lado de Vonage, debes configurar el lado del servicio OTP para que se comuniquen entre sí.
Por lo tanto, abra el archivo nexmo.json en la carpeta src carpeta del proyecto y proporcione los datos solicitados:
{
"apiKey": "YOUR_API_KEY",
"apiSecret": "YOUR_API_SECRET",
"applicationId": "YOUR_APPLICATION_ID"
}Puede recuperar el apiKey y el apiSecret de la sección de configuración del panel de control de la API de Vonage, mientras que el valor applicationId es el valor del que tomaste nota en la sección anterior.
A continuación, toma la clave privada asociada a la clave pública que asignaste a la aplicación y guárdala en el archivo private.key en la carpeta src.
Ejecutar el servicio OTP
Es hora de ejecutar tu servicio OTP. Escribe el siguiente comando en la carpeta raíz del proyecto:
Después de unos instantes, debería aparecer un mensaje diciendo que el servidor está funcionando en el puerto 3000. Puede verificar si está funcionando apuntando un navegador a la dirección http://localhost:3000 dirección. Si todo está bien, deberías ver el mensaje "Este es el servicio OTP".
Solicitar un OTP
Ahora, imagine que su aplicación necesita generar una OTP para enviarla a un usuario con el fin de verificar su identidad. Necesita enviar una POST al servicio OTP proporcionando una cadena que actúa como identificador de la solicitud y los contactos del usuario al que se enviará la OTP.
Puedes hacerlo enviando una petición HTTP como la siguiente al servicio OTP en ejecución:
POST /otp/123456789 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
cache-control: no-cache
{"messengerId": "8192836451", "phoneNumber": "393331234567"}La cadena 123456789 adjunta al URI de la API es el identificador de su solicitud. Nosotros la llamamos tokeny depende de ti proporcionarlo. El cuerpo de la solicitud contiene un objeto JSON con el identificador de Messenger y el número de teléfono del usuario que recibirá la OTP.
Puede enviar la solicitud a través de Postman como se muestra en la siguiente imagen:
Postman
El servicio OTP generará una OTP compuesta por 5 dígitos y la enviará al identificador de Messenger especificado. Como veremos más adelante, si el usuario no la lee en un tiempo determinado, la OTP se enviará por SMS al número de teléfono.
Después de una creación exitosa de OTP, deberías recibir un código de estado HTTP 201 Creado.
Verificar un OTP
Independientemente del medio por el que se haya recibido el mensaje, el usuario debe verificar la OTP enviando una solicitud GET a la segunda API, como en el siguiente ejemplo:
GET /otp/123456789/63731 HTTP/1.1
Host: localhost:3000
cache-control: no-cacheEl URI de la API está compuesto por los caracteres otp por el token de solicitud (es decir, el identificador de solicitud proporcionado al solicitar la creación de una OTP), y por la propia OTP.
En Postman, aparece de la siguiente manera:
Postman
Al enviar una solicitud de este tipo, puede tener como respuesta uno de los siguientes códigos de estado HTTP:
200 OK - Obtendrá esta respuesta cuando su OTP sea válido
404 No encontrado - Obtendrá esta respuesta cuando su OTP sea incorrecta, es decir, no haya sido generada por el servicio OTP
409 El código ya ha sido verificado - Esta respuesta significa que usted u otra persona ya ha verificado el OTP
410 El código ha caducado - Obtendrás esta respuesta si intentas verificar una OTP después de su tiempo de validez
También puede recibir el mensaje 404 El código no es válido por motivo desconocido cuando el servicio OTP no pueda verificar tu código por cualquier otra razón.
Cómo funciona
Veamos ahora el código que implementa nuestro servicio OTP. La siguiente imagen resume las carpetas y los archivos pertenecientes al proyecto:
Project structure
El archivo index.js de la carpeta src contiene el código de inicio de la aplicación y la definición de las API web. Las API de creación y verificación se implementan mediante el siguiente código:
app.post("/otp/:token", (req, res) => {
const otp = otpManager.create(req.params.token);
otpSender.send(otp, req.body);
res.sendStatus(201);
});
app.get("/otp/:token/:code", (req, res) => {
const verificationResults = otpManager.VerificationResults;
const verificationResult = otpManager.verify(req.params.token, req.params.code);
let statusCode;
let bodyMessage;
switch (verificationResult) {
case verificationResults.valid:
statusCode = 200;
bodyMessage = "OK";
break;
case verificationResults.notValid:
statusCode = 404;
bodyMessage = "Not found"
break;
case verificationResults.checked:
statusCode = 409;
bodyMessage = "The code has already been verified";
break;
case verificationResults.expired:
statusCode = 410;
bodyMessage = "The code is expired";
break;
default:
statusCode = 404;
bodyMessage = "The code is invalid for unknown reason";
}
res.status(statusCode).send(bodyMessage);
});
Como puede ver, ambas API se basan en la API otpManager para crear y verificar la OTP, y en la API otpSender para enviarla al usuario. Su inicialización se produce unas líneas más arriba, en el mismo fichero index.js archivo:
const OtpManager = require("./OtpManager");
const otpRepository = require("./otpRepository");
const otpSender = require("./otpSender")
const otpManager = new OtpManager(otpRepository, {otpLength: 5, validityTime: 5});Aquí puede ver que todo el servicio consta de tres componentes:
otpManagerResponsable de crear y verificar la OTPotpRepositoryResponsable de la persistencia de la OTPotpSenderResponsable de enviar la OTP al usuario
La existencia de estos tres componentes permite mantener independientes entre sí la implementación de la creación y verificación, el almacenamiento y la entrega.
Al crear la instancia de otpManagerse pasa el otpRepository y un objeto options que indica la longitud de la OTP (cinco caracteres) y el tiempo que debe considerarse válida (cinco minutos).
El otpManager
El sitio otpManager es una instancia de la clase OtpManager implementada en el archivo OtpManager.js archivo. Sus métodos principales son create() y verify().
El método create() genera un nuevo OTP y se implementa de la siguiente manera:
create(token) {
const code = Math.floor(Math.random()*Math.pow(10, this.options.otpLength))
.toString()
.padStart(this.options.otpLength, "0");
let otp = new OtpItem(token, code);
this.otpRepository.add(otp);
return otp;
}Toma un token como entrada y genera un número aleatorio de cinco dígitos. Garantiza que el código resultante esté compuesto exactamente por cinco dígitos, incluso si el primer dígito es un cero, convirtiéndolo en una cadena y rellenándolo con caracteres "0".
Por supuesto, esta es una implementación muy simple de la creación de OTP. Puede que quieras implementar algoritmos más precisospero esto está fuera del alcance de este artículo.
Una vez generado el código, crea un objeto otp como instancia de la OtpItem y añade la nueva otp instancia al directorio otpRepository.
La clase OtpItem define la estructura para representar la información relevante para la OTP, y se implementa en el archivo OtpItem.js fichero:
class OtpItem {
constructor(token, code) {
this.token = token;
this.code = code;
this.creationDate = new Date();
this.isChecked = false;
this.checkDate = null;
}
}El método verify() comprueba si se ha generado el código pasado para un token dado y si sigue siendo válido. Esta es su implementación:
verify(token, code) {
const id = `${token}-${code}`;
const otp = this.otpRepository.getById(id);
let verificationResult = VerificationResults.notValid;
if (otp) {
switch (true) {
case otp.isChecked:
verificationResult = VerificationResults.checked;
break;
case isOtpExpired(otp, this.options.validityTime):
verificationResult = VerificationResults.expired;
break;
default:
otp.isChecked = true;
otp.checkDate = new Date();
this.otpRepository.update(otp);
verificationResult = VerificationResults.valid;
}
}
return verificationResult;
}
}El método construye un identificador OTP concatenando el token y el código. Este identificador se utiliza para obtener la otp instancia del otpRepository. Si dicha instancia existe, el método verifica si ya ha sido verificada y si no ha caducado. El valor devuelto es un valor enumerado que representa el estado de validez de la OTP.
El otpRepository
En otpRepository almacena la instancia de un OtpItem en el sistema de archivos como un archivo JSON plano en la carpeta otpItems carpeta. Esta es una solución muy simple que funciona para un caso de demostración. Es posible que desee implementar mediante el almacenamiento de datos en una base de datos.
Este es el código de implementación que puede encontrar en el archivo otpRepository.js archivo:
const fs = require("fs");
const path = require("path");
const baseRepositoryPath = "./otpItems";
function add(otpItem) {
checkBaseFolder();
fs.writeFileSync(path.join(baseRepositoryPath, `${otpItem.token}-${otpItem.code}`), JSON.stringify(otpItem));
}
function getById(id) {
const content = getFileContent(path.join(baseRepositoryPath, id));
let otpItem = null;
if (content) {
otpItem = JSON.parse(content);
}
return otpItem;
}
function update(otpItem) {
fs.writeFileSync(path.join(baseRepositoryPath, `${otpItem.token}-${otpItem.code}`), JSON.stringify(otpItem));
return otpItem;
}
function checkBaseFolder() {
if (!fs.existsSync(baseRepositoryPath)){
fs.mkdirSync(baseRepositoryPath);
}
}
function getFileContent(fileName) {
let content = null;
try {
content = fs.readFileSync(fileName);
} catch (error) {
console.log(error);
}
return content;
}
module.exports = {
getById,
add,
update
};Como puede ver, implementa el método getById() para recuperar una instancia OtpItem instancia, el método add() para almacenar una instancia OtpItem y un método update() para actualizarla.
El otpSender
El componente otpSender envía la OTP al usuario utilizando la Dispatch API de Vonage. Se implementa mediante el archivo otpSender.js de la siguiente manera:
const Nexmo = require('nexmo')
const nexmoConfig =require("./nexmo.json");
const path = require("path");
nexmoConfig.privateKey = path.join(__dirname, "private.key");
const nexmo = new Nexmo(nexmoConfig);
function send(otp, recipientAdresses) {
const message = `Insert the following code: ${otp.code}`;
nexmo.dispatch.create("failover", [
{
"from": { "type": "messenger", "id": "YOUR_MESSENGER_ID" },
"to": { "type": "messenger", "id": recipientAdresses.messengerId },
"message": {
"content": {
"type": "text",
"text": message
}
},
"failover":{
"expiry_time": 120,
"condition_status": "read"
}
},
{
"from": {"type": "sms", "number": "NEXMO"},
"to": { "type": "sms", "number": recipientAdresses.phoneNumber},
"message": {
"content": {
"type": "text",
"text": message
}
}
},
(err, data) => {
console.log(data.dispatch_uuid);
}
])
}
module.exports = {
send
};Compone una configuración fusionando los datos del nexmo.json y del archivo private.key archivo. Esta configuración se pasa al constructor de la biblioteca para obtener una nexmo instancia. Esta instancia se utilizará en la implementación de la función send() función. La función toma una OtpItem y un objeto recipientAddresses como argumentos y construye el mensaje que se enviará al usuario y la carga útil para la Dispatch API
Mediante el método nexmo.dispatch.create() se crea un flujo de trabajo de entrega con conmutación por error. El segundo argumento del método es una matriz que contiene tres elementos:
El primer elemento es un objeto que especifica el remitente, el destinatario y el texto del mensaje a enviar. El tipo del remitente y del destinatario indica que se trata de una comunicación Messenger. También tiene una propiedad
failoverque especifica cuándo la entrega debe considerarse fallida. En nuestro caso, se considera fallida si el mensaje no se lee en 120 segundosEl segundo elemento es otro objeto que especifica el remitente, la entrega y el texto del mensaje que se enviará cuando falle la entrega de Messenger. En este caso, el tipo del remitente y del destinatario indica que el mensaje se enviará por SMS.
El último elemento es una función de devolución de llamada que se ejecuta una vez que el flujo de trabajo ha sido enviado al servidor de la API de Vonage. En nuestro caso, simplemente escribimos en la consola el identificador del flujo de trabajo (dispatch_uuid) devuelto por Vonage.
Esto aumenta las posibilidades de que la OTP generada sea entregada al usuario independientemente del medio de comunicación utilizado.
Compartir:
Andrea Chiarelli has over 20 years of experience as a software engineer and technical writer. Throughout his career, he has used various technologies for the projects he was involved in. Currently, he is a software architect at the Italian office of Apparound and contributes to a few online magazines and blogs.
