Agrega la API Vonage Verify al backend
Uso del SDK del servidor de Vonage
Vonage expone una API HTTP estándar bajo el capó. Esto significa que, en teoría, podrías integrar Verify enviando solicitudes HTTP sin procesar tú mismo (por ejemplo, con fetch, axiosetc.).
Entonces, ¿por qué utilizar el SDK de Vonage Node?
El uso del SDK ayuda porque:
-
La autenticación es más fácil y segura: Verify utiliza autenticación basada en JWT con una clave privada. El SDK gestiona el flujo de firma correctamente, por lo que es menos probable que cometas errores.
-
Código más limpio: en lugar de crear manualmente las URL y los encabezados, y analizar los formatos de respuesta, se invocan métodos como
newRequest()ycheckCode(). -
Mejor mantenimiento: cuando Vonage actualiza la API o agrega funciones, el SDK generalmente se actualiza para que coincida.
-
Menos «trampas»: aspectos como el formato de las solicitudes y los campos obligatorios se gestionan de forma coherente.
Añadamos el SDK a nuestro app.js archivo:
require("dotenv").config();
const fs = require("fs");
const express = require("express");
const cors = require("cors");
const { Auth } = require("@vonage/auth");
const { Verify2 } = require("@vonage/verify2");
const app = express();
const port = process.env.PORT || 3000;
app.use(cors());
app.use(express.json());
// Create Vonage credentials (JWT auth)
const credentials = new Auth({
applicationId: process.env.VONAGE_APPLICATION_ID,
privateKey: process.env.VONAGE_PRIVATE_KEY_PATH,
});
// Verify client (Verify API v2)
const verifyClient = new Verify2(credentials);
// Health check endpoint
app.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
// Run the server
app.listen(port, () => {
console.log(`Backend listening on port ${port}`);
});
¿Qué está pasando aquí?
- El backend necesita probar a Vonage: "Se me permite llamar a esta API".
- Vonage utiliza JWT (JSON Web Tokens) firmados con tu clave privada como prueba.
- El SDK genera y adjunta el JWT automáticamente cada vez que realiza una llamada a Vonage.
Comienza la verificación: POST /verification
Este punto final inicia el proceso de verificación. La aplicación móvil llama a tu backend con un número de teléfono. Tu backend luego le pide a Vonage que inicie una solicitud de verificación.
¿Qué ocurre en este punto final?
- El usuario introduce su número de teléfono en la aplicación móvil.
- La aplicación móvil envía el número de teléfono a tu servidor.
- Tu backend inicia una solicitud Verify:
- primeros intentos Autenticación silenciosa
- si no se puede completar, vuelve a SMS
app.post("/verification", async (req, res) => {
const { phone } = req.body || {};
if (!phone) {
return res.status(400).json({ error: "Phone number is required." });
}
try {
const result = await verifyClient.newRequest({
brand: "DemoApp",
workflow: [
{ channel: "silent_auth", to: phone },
{ channel: "sms", to: phone },
],
});
return res.json({
request_id: result.requestId,
check_url: result.checkUrl,
});
} catch (error) {
const status = error?.response?.status || 500;
const details = error?.response?.data || error?.message;
console.error("Vonage Verify newRequest failed:", details);
return res.status(status).json({
error: "Failed to start verification",
details: typeof details === "string" ? details : undefined,
});
}
});
Comprensión request_id y check_url:
-
request_idun identificador único para este intento de verificación. Es como un "número de recibo" de la verificación. -
check_url: se utiliza para la autenticación silenciosa. Tu servidor devuelve esta URL a la aplicación móvil. La aplicación móvil la invoca para demostrar que «esta solicitud procede de la red móvil de ese número de teléfono».
Compruebe el código de verificación: POST /check-code
Si la autenticación silenciosa falla o no está disponible, Vonage recurrirá a los SMS y el usuario recibirá un código. La aplicación móvil envía el código a tu sistema de fondo junto con el request_id.
app.post("/check-code", async (req, res) => {
const { request_id, code } = req.body || {};
if (!request_id || !code) {
return res.status(400).json({ error: "request_id and code are required." });
}
try {
const status = await verifyClient.checkCode(request_id, code);
return res.json({
verified: status === "completed",
status,
});
} catch (error) {
const status = error?.response?.status || 400;
const details = error?.response?.data || error?.message;
return res.status(status).json({
error: "Failed to check code",
details: typeof details === "string" ? details : undefined,
});
}
});
Devoluciones de llamada
Una devolución de llamada (también llamada webhook) es una URL de tu backend a la que un servicio externo (Vonage) puede acceder para notificarte sobre distintos eventos.
En lugar de que tu backend esté constantemente consultando a Vonage: "¿Ha terminado ya la Autentificación Silenciosa? ¿Y ahora? ¿Ahora?"
Vonage puede enviarte el resultado: «La autenticación silenciosa ha finalizado. Este es el estado final».
Esa notificación push es la devolución de llamada.
¿Por qué son útiles aquí las retrollamadas? La autenticación silenciosa puede llevar tiempo y completarse de forma asíncrona. Utilizar una devolución de llamada significa:
- Tu backend no tiene por qué realizar consultas repetidas a Vonage
- Obtendrá un evento definitivo cuando la verificación cambie de estado
- Se adapta mejor a los sistemas reales
Para configurar la URL de devolución de llamada en el panel de control, abre el panel de control de Vonage:
- Ir a Applications
- Seleccione su aplicación → Editar
- Buscar el Registro de red
- Habilitar «Verify» (SA)
- Configura la URL de devolución de llamada (donde tu servidor está a la escucha), por ejemplo:
https://your-domain.com/callback
Nota: Si estás ejecutando localmente, Vonage no puede alcanzar http://localhost:3000. Necesitarás una URL pública (normalmente un túnel como ngrok).
Para implementar la llamada de retorno, añade un nuevo método a tu aplicación Express de la siguiente manera:
app.post("/callback", (req, res) => {
console.log("Callback received:", req.body);
return res.status(200).json({ ok: true });
});
Por ahora, registramos el evento para que puedas ver lo que envía Vonage. En la siguiente sección ampliaremos esta funcionalidad para almacenar el estado y hacer que la aplicación móvil reaccione ante esas actualizaciones.
Añadir un estado en memoria
Un flujo de verificación no es "una solicitud y listo". Tiene un ciclo de vida:
- iniciado
- pendiente (silent auth / sms)
- completado o fallido/expirado
Si no almacena el estado en cualquier lugar, su backend no tiene memoria de lo que pasó, y:
/callbacksolo puede registrar datos (no es muy útil)- La aplicación no puede conocer de forma fiable el estado actual
- La depuración se vuelve dolorosa ("funcionó una vez, luego ya no...")
Una tienda le ofrece una única fuente de verdad.
En un entorno de producción, se utilizaría una base de datos (por ejemplo, Postgres o Redis), pero para este tutorial basta con utilizar un Map.
Paso 1: Crear la tienda con un Map
En Node.js, un Map es una forma sencilla de almacenar pares clave/valor en memoria.
Añade esto cerca de la parte superior de tu app.js:
// In-memory store for tutorial purposes:
// request_id -> verification state
const verificationStore = new Map();
Añade una función auxiliar para validar los campos obligatorios del cuerpo de la solicitud. Añádela justo debajo del verificationStore declaración:
function requireFields(obj, fields) {
for (const f of fields) {
if (!obj || obj[f] == null || obj[f] === "") return f;
}
return null;
}
Esto devuelve el nombre del primer campo que falta, o null si todos los campos están presentes.
Cada entrada se codificará por request_id.
Una entrada típica podría tener este aspecto:
{
"phone": "+34600111222",
"status": "started",
"createdAt": "2026-02-02T11:22:00.000Z",
"updatedAt": "2026-02-02T11:22:00.000Z"
}
Paso 2: Guardar el estado inicial al crear una verificación
Cuando llame verifyClient.newRequest(...) en /verificationrecibirá un request_id.
Esa es la clave perfecta para almacenar el estado inicial.
Dentro de tu /verification justo después de obtener result:
verificationStore.set(result.requestId, {
phone,
status: "started",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
});
Ahora el backend «recuerda» que se ha iniciado una verificación.
Paso 3: Actualizar el estado cuando la devolución de llamada reciba eventos
Una devolución de llamada (webhook) es Vonage diciéndoselo a tu backend: «Algo ha cambiado. Este es el nuevo estado».
En lugar de registrar únicamente la carga útil, actualizamos el estado almacenado:
app.post("/callback", (req, res) => {
const { request_id, status } = req.body || {};
if (!request_id) return res.status(400).json({ error: "Missing request_id" });
const current = verificationStore.get(request_id) || {
phone: null,
status: "unknown",
createdAt: new Date().toISOString(),
updatedAt: new Date().toISOString(),
lastEvent: null,
};
const updated = {
...current,
status: status || current.status,
updatedAt: new Date().toISOString(),
lastEvent: req.body,
};
verificationStore.set(request_id, updated);
return res.status(200).json({ ok: true });
});
Las entregas de webhooks pueden repetirse, lo que significa que es posible que recibas el mismo evento varias veces. Actualizar la tienda de esta forma es, por naturaleza, idempotente: volver a establecer el mismo estado no provoca ningún problema.
Paso 4: Añadir un punto final de estado para la aplicación móvil
Ahora podemos proporcionar un punto final simple que la aplicación puede llamar para comprobar el estado actual:
app.get("/status/:request_id", (req, res) => {
const { request_id } = req.params;
const entry = verificationStore.get(request_id);
if (!entry) return res.status(404).json({ error: "Unknown request_id" });
return res.json({
request_id,
status: entry.status,
updated_at: entry.updatedAt,
});
});
Esto es especialmente útil para la autenticación silenciosa porque la aplicación puede sondear cada 1-2 segundos durante un breve periodo de tiempo en lugar de esperar ciegamente.
Paso 5: Añadir POST /next
El /next El punto final indica a Vonage que omita el canal actual del flujo de trabajo y pase al siguiente. En nuestro caso, eso significa omitir la autenticación silenciosa y enviar un SMS de inmediato.
Esto resulta útil en la aplicación para Android cuando falla la solicitud de autenticación silenciosa (problemas de conexión, error del SDK, etc.): en lugar de esperar unos 20 segundos a que Vonage agote el tiempo de espera de forma natural, la aplicación llama a /next y el usuario recibe un SMS al instante.
app.post("/next", async (req, res) => {
try {
const missing = requireFields(req.body, ["requestId"]);
if (missing) {
return res.status(400).json({ error: `Field '${missing}' is required.` });
}
const { requestId } = req.body;
const entry = verificationStore.get(requestId);
if (!entry) {
return res.status(404).json({ error: "Unknown request_id" });
}
console.log("Moving to next workflow (SMS) for:", requestId);
// Call Vonage to move to next workflow
const result = await verifyClient.nextWorkflow(requestId);
console.log("Vonage nextWorkflow result:", result);
// Update last event
const updated = {
...entry,
updatedAt: new Date().toISOString(),
lastEvent: { source: "next_workflow", result },
};
verificationStore.set(requestId, updated);
return res.status(200).json({ ok: true });
} catch (error) {
const status = error?.response?.status || 500;
const details = error?.response?.data || error?.message;
console.error("Error /next:", details);
return res.status(status).json({
error: "Failed to move workflow",
details: typeof details === "string" ? details : undefined,
});
}
});
Nota: si /next falla, no es fatal. Vonage volverá automáticamente a SMS luego del tiempo de espera de la autenticación silenciosa. La aplicación para Android debería mostrar la pantalla de ingreso de SMS independientemente de si esta llamada tiene éxito o no.
Primeros pasos con la autenticación silenciosa
La autenticación silenciosa lleva bastante tiempo entenderla. Este tutorial te muestra cómo crear una integración desde cero con Node.js y Kotlin.