Node.js

Ajouter l'API Verify de Vonage au backend

Utilisation du SDK de Vonage Server

Vonage expose une API HTTP standard sous le capot. Cela signifie qu'en théorie, vous pouvez intégrer Verify en envoyant les requêtes HTTP brutes (par exemple avec fetch, axiosetc.)

Alors pourquoi utiliser le SDK Node de Vonage ?

L'utilisation du SDK est utile parce que :

  • L'authentification est plus simple et plus sûre : Verify utilise une authentification basée sur JWT avec une clé privée. Le SDK gère correctement le flux de signature, ce qui réduit les risques d'erreur.

  • Un code plus propre : au lieu de construire manuellement des URL, des en-têtes et d'analyser les formats de réponse, vous appelez des méthodes telles que newRequest() et checkCode().

  • Meilleure maintenance : lorsque Vonage met à jour l'API ou ajoute des fonctionnalités, le SDK est généralement mis à jour en conséquence.

  • Moins de "gotchas" : des éléments tels que le formatage des demandes et les champs attendus sont traités de manière cohérente.

Ajoutons le SDK à notre app.js fichier :

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}`);
});

Que se passe-t-il ici ?

  • Le backend doit faire ses preuves auprès de Vonage : "J'ai le droit d'appeler cette API".
  • Vonage utilise des JWT (JSON Web Tokens) signés par votre clé privée pour cette preuve.
  • Le SDK génère et attache le JWT automatiquement chaque fois qu'il appelle Vonage.

Vérification de départ : POST /verification

Ce point final lance le processus de vérification. L'application mobile appelle votre backend avec un numéro de téléphone. Votre backend demande alors à Vonage de lancer une demande de vérification.

Que se passe-t-il dans ce point final ?

  1. L'utilisateur saisit son numéro de téléphone dans l'application mobile.
  2. L'application mobile envoie le numéro de téléphone à votre backend.
  3. Votre backend lance une requête Verify :
    • Premiers essais Authentification silencieuse
    • si cela n'est pas possible, il revient à l'option 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,
    });
  }
});

Comprendre request_id et check_url:

  • request_idun identifiant unique pour cette tentative de vérification. Il s'agit en quelque sorte d'un "numéro de reçu" pour la vérification.

  • check_url: utilisée pour l'authentification silencieuse. Votre backend renvoie cette URL à l'application mobile. L'application mobile l'appelle pour prouver que "cette demande provient du réseau mobile du numéro de téléphone".

Vérifier le code de vérification : POST /check-code

Si l'authentification silencieuse échoue ou n'est pas disponible, Vonage se rabattra sur le SMS et l'utilisateur recevra un code. L'application mobile envoie le code à votre système d'arrière-plan en même temps que le numéro d'identification de l'utilisateur. 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,
    });
  }
});

Rappels

Un rappel (également appelé webhook) est une URL dans votre backend qu'un service externe (Vonage) peut appeler pour vous notifier des événements.

Au lieu que votre backend demande constamment à Vonage : "L'authentification silencieuse est-elle terminée ? Et maintenant ? Maintenant ?"

Vonage peut vous envoyer le résultat : "L'authentification silencieuse est terminée. Voici l'état final."

Cette notification push est le rappel.

Pourquoi les rappels sont-ils utiles ici ? L'authentification silencieuse peut prendre du temps et se dérouler de manière asynchrone. L'utilisation d'un rappel signifie :

  • Votre backend n'a pas besoin d'interroger Vonage de façon répétée.
  • Vous obtenez un événement définitif lorsque la vérification change d'état
  • Il s'adapte mieux aux systèmes réels

Pour configurer l'URL de rappel dans le tableau de bord, ouvrez le tableau de bord de Vonage :

  1. Aller aux Applications
  2. Sélectionnez votre application → Modifier
  3. Trouver le registre du réseau
  4. Activer Verify (SA)
  5. Définissez l'URL de rappel (où votre serveur écoute), par exemple :

https://your-domain.com/callback

Note: Si vous travaillez localement, Vonage ne peut pas vous joindre. http://localhost:3000. Vous aurez besoin d'une URL publique (généralement un tunnel comme ngrok).

Pour mettre en œuvre le rappel, ajoutez une nouvelle méthode à votre application Express, comme suit :

app.post("/callback", (req, res) => {
  console.log("Callback received:", req.body);
  return res.status(200).json({ ok: true });
});

Pour l'instant, nous enregistrons l'événement pour que vous puissiez voir ce que Vonage envoie. Nous l'étendrons dans la section suivante pour stocker l'état et faire en sorte que l'application mobile réagisse à ces mises à jour.

Ajouter un état en mémoire

Un flux de vérification n'est pas "une demande et c'est fini". Il a un cycle de vie :

  • a commencé
  • en attente (authentification silencieuse / sms)
  • terminé ou échoué/expiré

Si vous ne stockez l'état nulle part, votre backend ne se souvient pas de ce qui s'est passé, et.. :

  • /callback ne peut que consigner des données (pas très utile)
  • L'application ne peut pas connaître de manière fiable le statut actuel
  • Le débogage devient pénible ("ça a marché une fois, puis ça n'a pas marché...")

Un magasin vous offre une source unique de vérité.

En production, vous utiliseriez une base de données (par exemple Postgres/Redis), mais pour le tutoriel, nous pouvons simplement utiliser une base de données de type Map.

Étape 1 : Créer le magasin avec un Map

Dans Node.js, un Map est un moyen simple de stocker des paires clé/valeur en mémoire.

Ajoutez ceci en haut de votre app.js:

// In-memory store for tutorial purposes:
// request_id -> verification state
const verificationStore = new Map();

Ajouter une fonction d'aide pour valider les champs obligatoires du corps de la demande. Ajoutez-la juste en dessous de la fonction verificationStore déclaration :

function requireFields(obj, fields) {
  for (const f of fields) {
    if (!obj || obj[f] == null || obj[f] === "") return f;
  }
  return null;
}

Ceci renvoie le nom du premier champ manquant, ou null si tous les champs sont présents.

Chaque entrée sera codée par request_id.

Une entrée typique peut ressembler à ceci :

Étape 2 : Sauvegarder l'état initial lors de la création d'une vérification

Lorsque vous appelez verifyClient.newRequest(...) en /verification, vous recevez un request_id.

C'est la clé idéale pour stocker l'état initial.

À l'intérieur de votre /verification juste après avoir obtenu result:

verificationStore.set(result.requestId, {
  phone,
  status: "started",
  createdAt: new Date().toISOString(),
  updatedAt: new Date().toISOString(),
});

Le backend se "souvient" désormais qu'une vérification a commencé.

Étape 3 : Mise à jour de l'état lorsque le callback reçoit des événements

Un rappel (webhook) est une communication de Vonage à votre backend : "Quelque chose a changé. Voici le nouveau statut."

Au lieu d'enregistrer uniquement la charge utile, nous mettons à jour l'état stocké :

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 });
});

Les livraisons de Webhook peuvent être retentées, ce qui signifie que vous pouvez recevoir le même événement plusieurs fois. La mise à jour du magasin de cette manière est naturellement idempotente : définir à nouveau le même statut ne casse rien.

Étape 4 : Ajouter un point de terminaison d'état pour l'application mobile

Nous pouvons maintenant fournir un point de terminaison simple que l'application peut appeler pour vérifier l'état actuel :

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,
  });
});

Ceci est particulièrement utile pour l'authentification silencieuse car l'application peut interroger toutes les 1-2 secondes pendant une courte période au lieu d'attendre aveuglément.

Étape 5 : Ajouter POST /next

Les /next indique à Vonage de sauter le canal de flux de travail actuel et de passer au suivant. Dans notre cas, cela signifie qu'il faut ignorer l'authentification silencieuse et envoyer un SMS immédiatement.

Ceci est utile dans l'application Android lorsque la demande d'authentification silencieuse échoue (mauvais réseau, erreur SDK, etc.) - au lieu d'attendre ~20 secondes que Vonage s'arrête naturellement, l'application appelle /next et l'utilisateur reçoit un SMS immédiatement.

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,
    });
  }
});

Note : Si /next échoue, ce n'est pas fatal. Vonage reviendra automatiquement au SMS après le délai d'attente de l'authentification silencieuse. L'application Android devrait afficher l'écran de saisie de SMS, que cet appel aboutisse ou non.