https://d226lax1qjow5r.cloudfront.net/blog/blogposts/build-a-one-time-password-otp-service-using-the-dispatch-api/onetimepassword.png

Erstellen eines One-Time Password (OTP)-Dienstes mit der Dispatch API

Zuletzt aktualisiert am October 23, 2020

Lesedauer: 11 Minuten

Hinweis zur Produktverschlechterung

Ab dem 31. August 2025 wird die Vonage Dispatch API für neue Nutzer geschlossen, obwohl das Produkt für bestehende Nutzer weiterhin unterstützt wird. Wenn Sie eine Messaging-Anwendung mit Failover-Funktionalität erstellen möchten, wird Failover jetzt direkt in der Messages API unterstützt.

Allgemeine Informationen über die Funktion Messages Failover finden Sie in diesen Leitfaden. Eine Anleitung für die Migration von Dispatch API zu Messages API Failover finden Sie in diesen Leitfaden.

Wenn Sie weitere Fragen zu dieser Produktabkündigung haben, wenden Sie sich bitte kontaktieren Sie uns auf dem Vonage Community Slack.

One-Time-Passwörter (OTPs) sind in letzter Zeit recht bekannt geworden, vor allem aufgrund einer Sicherheitsanforderung, die herkömmliche Passwörter nicht gewährleisten. Während der Schutz des herkömmlichen Passworts in der Verantwortung des Benutzers liegt, der sich bekanntlich oft nicht genug darum kümmert, ist das OTP praktisch selbst geschützt, da es nach dem Zufallsprinzip generiert wird und seine Gültigkeit zeitlich begrenzt ist.

Sie können OTPs anstelle von herkömmlichen Passwörtern verwenden oder den herkömmlichen Authentifizierungsprozess durch einen Zwei-Faktor-Authentifizierungsansatz (2FA) verstärken. Eigentlich können Sie OTPs überall dort einsetzen, wo Sie einen Mechanismus benötigen, der die Identität eines Nutzers sicherstellt, indem er sich auf ein Kommunikationsmedium verlässt, das ihm gehört: eine Mailbox, ein Telefon, eine bestimmte App usw.

In diesem Artikel wird gezeigt, wie ein einfacher OTP-Dienst auf der Grundlage von zwei Web-APIs implementiert werden kann:

  • Die erste API ermöglicht es Ihnen, das OTP zu erstellen und es über Facebook Messenger als primäres Medium oder über SMS als Ausweichmedium an den Benutzer zu senden.

  • die zweite API ermöglicht es dem Benutzer, das erhaltene OTP zu verifizieren

Der OTP-Dienst hat keine Benutzeroberfläche. Er ist als Microservice konzipiert, den Sie aus Ihrer Anwendung heraus aufrufen können, um OTPs zu erzeugen und zu verifizieren.

Voraussetzungen

Um den in diesem Artikel vorgestellten OTP-Dienst nutzen zu können, benötigen Sie:

  • Node.js 8.11+ auf Ihrem Computer installiert

  • A Messenger Account und ein Telefon, das SMS empfangen kann

  • Eine Anwendung zum Senden von HTTP-Anfragen, wie z. B. curl oder Postman

Vonage API-Konto

Um dieses Tutorial durchzuführen, benötigen Sie ein Vonage API-Konto. Wenn Sie noch keines haben, können Sie sich noch heute anmelden und mit einem kostenlosen Guthaben beginnen. Sobald Sie ein Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf dem Vonage-API-Dashboard.

Einrichten des Projekts

In einem ersten Schritt müssen Sie das Projekt aus dem GitHub-Repository klonen oder herunterladen.

Sobald Sie den Code des Projekts auf Ihrem Computer haben, müssen Sie seine Abhängigkeiten installieren, indem Sie in den Projektordner wechseln und den folgenden Befehl eingeben:

npm install

Wie wir später sehen werden, verwendet die Anwendung Express als Web-Framework und die Vonage-Client-Bibliothek für Node.js, um das OTP an den Benutzer zu senden.

Konfigurieren Sie die Anwendung

Bevor Sie den OTP-Dienst nutzen können, müssen Sie einige Konfigurationen auf dem Vonage API-Dashboard vornehmen, um die Zustellung von Nachrichten über die Vonage Dispatch API.

Mit dieser API können Sie Nachrichten an Ihre Benutzer über mehrere Kanäle mit Priorisierung senden. In unserem Fall senden wir beispielsweise das OTP als ersten Versuch über das Messenger-Konto des Nutzers. Wenn der Nutzer es nicht innerhalb einer bestimmten Zeitspanne liest, wird die Nachricht per SMS an seine Telefonnummer gesendet.

Greifen Sie also auf Ihr Vonage API-Dashboard zu und wählen Sie im Menü den Punkt Nachrichten und Versand aus, und wählen Sie dann Eine Anwendung erstellenwie in der Abbildung unten gezeigt:

Vonage DashboardVonage Dashboard

Im Vonage-API-Jargon ist eine Applikation ein Bündel von Daten, mit denen Sie die Messages API und die Dispatch API nutzen können. Wie wir aus dem obigen Formular ersehen können, sind die minimalen Daten:

  • den Namen der Anwendung

  • die URL eines öffentlichen Webhooks, der für den Empfang des Zustellungsstatus der Nachrichten aktiviert ist

  • die URL eines öffentlichen Webhooks, der für den Empfang eingehender Nachrichten aktiviert ist

  • den öffentlichen Schlüssel, mit dem Sie die an die API gesendete Anfrage signieren (Sie können ein Paar öffentlicher und privater Schlüssel generieren, indem Sie auf den Link unter dem Textbereich klicken)

In den Schritten zwei und drei des Erstellungsprozesses der Nachrichtenanwendung können Sie Telefonnummern und/oder Links zu externen Accounts für Dienste wie Messenger, WhatsApp oder Viber zuweisen, damit Sie SMS oder Nachrichten über die Nachrichten- und Dispatch API senden können.

Siehe insbesondere dieses Dokument um zu verstehen, wie Sie Ihre Facebook-Seite mit Ihrem Vonage API Account verknüpfen können.

Beachten Sie, dass der Status und die eingehenden URLs, die einer Vonage-API-Anwendung zur Verfügung gestellt werden, öffentlich zugänglich sein müssen. Wenn Sie keinen öffentlichen Webserver haben oder dies nur auf Ihrem Computer ausprobieren möchten, verwenden Sie ngrok, ein Tool, mit dem Sie Ihren lokalen Webserver öffentlich zugänglich machen können.

Weitere Details zur Arbeit mit Ngrok finden Sie in unserer Dokumentation. Beachten Sie, dass bei der Nutzung des kostenlosen Plans von ngrok jedes Mal, wenn Sie das Tool ausführen, eine temporäre URL generiert wird. Sie müssen also die Anwendungs-URLs in Ihren Dashboard-Einstellungen entsprechend aktualisieren.

Nachdem Sie Ihre Vonage API-Anwendung erstellt haben, wird ihr ein Application ID zugewiesen werden. Notieren Sie sich diese.

Konfigurieren Sie den OTP-Dienst

Sobald Sie die Vonage-Seite konfiguriert haben, müssen Sie die OTP-Service-Seite konfigurieren, damit beide miteinander kommunizieren können.

Öffnen Sie also die nexmo.json Datei unter dem src Ordner des Projekts und geben Sie die gewünschten Daten ein:

{
   "apiKey": "YOUR_API_KEY",
   "apiSecret": "YOUR_API_SECRET",
   "applicationId": "YOUR_APPLICATION_ID"
}

Sie können die apiKey und die apiSecret Werte aus dem Einstellungsbereich des Vonage API Dashboards abrufen, während der applicationId der Wert ist, den Sie im vorherigen Abschnitt notiert haben.

Nehmen Sie dann den privaten Schlüssel, der mit dem öffentlichen Schlüssel verbunden ist, den Sie der Anwendung zugewiesen haben, und speichern Sie ihn in der Datei private.key Datei im Ordner src.

Ausführen des OTP-Dienstes

Es ist an der Zeit, Ihren OTP-Dienst zu starten. Geben Sie den folgenden Befehl in den Stammordner des Projekts ein:

npm start

Nach einigen Augenblicken sollten Sie die Meldung erhalten, dass der Server auf Port 3000 läuft. Sie können verifizieren, ob er funktioniert, indem Sie einen Browser auf die http://localhost:3000 Adresse zeigen. Wenn alles in Ordnung ist, sollten Sie die Meldung "Dies ist der OTP-Dienst" sehen.

Ein OTP anfordern

Stellen Sie sich nun vor, dass Ihre Anwendung ein OTP generieren muss, das an einen Benutzer gesendet wird, um dessen Identität zu verifizieren. Sie muss eine POST Anfrage an den OTP-Dienst stellen, indem sie eine Zeichenkette angibt, die als Bezeichner für Ihre Anfrage dient, sowie die Kontakte des Benutzers, an den das OTP gesendet werden soll.

Sie können dies tun, indem Sie eine HTTP-Anfrage wie die folgende an den laufenden OTP-Dienst senden:

POST /otp/123456789 HTTP/1.1
Host: localhost:3000
Content-Type: application/json
cache-control: no-cache
{"messengerId": "8192836451", "phoneNumber": "393331234567"}

Die Zeichenkette 123456789 die an den API-URI angehängt ist, ist die Kennung Ihrer Anfrage. Wir nennen sie tokenund es liegt an Ihnen, sie anzugeben. Der Hauptteil der Anfrage enthält ein JSON-Objekt mit der Messenger-Kennung und der Telefonnummer des Benutzers, der das OTP erhalten soll.

Sie können den Antrag über Postman einreichen, wie in der folgenden Abbildung dargestellt:

PostmanPostman

Der OTP-Dienst generiert ein OTP, das aus 5 Ziffern besteht, und sendet es an die angegebene Messenger-Kennung. Wie wir später sehen werden, wird das OTP per SMS an die Telefonnummer gesendet, wenn der Nutzer es nicht innerhalb einer bestimmten Zeitspanne liest.

Nach einer erfolgreichen OTP-Erstellung sollten Sie den HTTP-Statuscode 201 Created erhalten.

Verify eines OTPs

Unabhängig von dem Medium, über das die Nachricht empfangen wurde, sollte der Benutzer das OTP verifizieren, indem er eine GET-Anfrage an die zweite API sendet, wie im folgenden Beispiel:

GET /otp/123456789/63731 HTTP/1.1
Host: localhost:3000
cache-control: no-cache

Der URI der API setzt sich zusammen aus dem otp Präfix, dem Anfrage-Token (d. h. der Anfragekennung, die bei der Erstellung eines OTPs angegeben wurde) und dem OTP selbst zusammen.

In Postman erscheint sie wie folgt:

PostmanPostman

Wenn Sie eine solche Anfrage senden, können Sie einen der folgenden HTTP-Statuscodes als Antwort erhalten:

  • 200 OK - Sie erhalten diese Antwort, wenn Ihr OTP gültig ist

  • 404 Nicht gefunden - Sie erhalten diese Antwort, wenn Ihr OTP falsch ist, d. h. nicht vom OTP-Dienst generiert wurde

  • 409 Der Code wurde bereits verifiziert - Diese Antwort bedeutet, dass Sie oder jemand anderes das OTP bereits verifiziert hat.

  • 410 Der Code ist abgelaufen - Sie erhalten diese Antwort, wenn Sie versuchen, ein OTP nach Ablauf seiner Gültigkeitsdauer zu verifizieren

Sie können auch die folgende Meldung erhalten 404 Der Code ist aus unbekanntem Grund ungültig HTTP-Statuscode, wenn der OTP-Dienst Ihren Code aus einem anderen Grund nicht verifizieren kann.

Wie es funktioniert

Werfen wir nun einen Blick auf den Code, der unseren OTP-Dienst implementiert. In der folgenden Abbildung sind die zum Projekt gehörenden Ordner und Dateien zusammengefasst:

Project structureProject structure

Die index.js Datei unter dem Ordner src Ordner enthält den Startcode der Anwendung und die Definition der Web-APIs. Die Erstellungs- und Prüf-APIs werden durch den folgenden Code implementiert:

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

Wie Sie sehen können, verlassen sich beide APIs auf die otpManager um das OTP tatsächlich zu erstellen und zu verifizieren, und von der otpSender um es an den Benutzer zu senden. Ihre Initialisierung erfolgt ein paar Zeilen weiter oben, in derselben index.js Datei:

const OtpManager = require("./OtpManager");
const otpRepository = require("./otpRepository");
const otpSender = require("./otpSender")

const otpManager = new OtpManager(otpRepository, {otpLength: 5, validityTime: 5});

Hier können Sie sehen, dass der gesamte Dienst aus drei Komponenten besteht:

  • otpManager Verantwortlich für die Erstellung und Verifizierung des OTP

  • otpRepository Verantwortlich für das Persistieren des OTP

  • otpSender Verantwortlich für den Versand des OTP an den Benutzer

Das Vorhandensein dieser drei Komponenten ermöglicht es Ihnen, die Implementierung der Erstellung und Überprüfung, der Speicherung und der Zustellung unabhängig voneinander zu halten.

Wenn Sie die Instanz von otpManagererstellen, übergeben Sie die otpRepository und ein options Objekt, das angibt, wie lang das OTP sein soll (fünf Zeichen) und wie lange es als gültig gilt (fünf Minuten).

Der otpManager

Die otpManager ist eine Instanz der OtpManager Klasse, die in der OtpManager.js Datei implementiert ist. Ihre Hauptmethoden sind create() und verify().

Die Methode create() Methode erzeugt ein neues OTP und ist wie folgt implementiert:

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

Es nimmt ein Token als Eingabe und generiert eine zufällige Zahl von fünf Ziffern. Er stellt sicher, dass der resultierende Code aus genau fünf Ziffern besteht, auch wenn die erste Ziffer eine Null ist, indem er ihn in eine Zeichenkette umwandelt und mit "0"-Zeichen auffüllt.

Natürlich ist dies eine sehr einfache Implementierung der OTP-Erstellung. Sie möchten vielleicht genauere Algorithmenimplementieren, aber das würde den Rahmen dieses Artikels sprengen.

Sobald der Code generiert ist, wird ein otp-Objekt als Instanz der OtpItem Klasse und fügt die neue otp Instanz zu der otpRepository.

Die Klasse OtpItem Klasse definiert die Struktur zur Darstellung der relevanten Informationen für das OTP und ist in der OtpItem.js Datei implementiert:

class OtpItem {
  constructor(token, code) {
    this.token = token;
    this.code = code;
    this.creationDate = new Date();
    this.isChecked = false;
    this.checkDate = null;
  }
}

Die Methode verify() Methode prüft, ob der übergebene Code für ein bestimmtes Token generiert wurde und ob er noch gültig ist. Hier ist ihre Implementierung:

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

Die Methode erstellt eine OTP-Kennung durch Verkettung des Tokens und des Codes. Dieser Bezeichner wird verwendet, um die otp Instanz aus der Datei otpRepository. Wenn eine solche Instanz existiert, prüft die Methode, ob sie bereits verifiziert wurde und ob sie nicht abgelaufen ist. Der zurückgegebene Wert ist ein Aufzählungswert, der den OTP-Gültigkeitsstatus angibt.

Das otpRepository

Die otpRepository speichert die Instanz eines OtpItem im Dateisystem als einfache JSON-Datei im otpItems Ordner. Dies ist eine sehr einfache Lösung, die für einen Demo-Fall funktioniert. Vielleicht möchten Sie sie durch Speicherung von Daten in einer Datenbank implementieren.

Hier ist der Implementierungscode, der sich in der otpRepository.js Datei finden:

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

Wie Sie sehen können, implementiert es die getById() Methode zum Abrufen einer OtpItem Instanz, die add() Methode zum Speichern einer OtpItem Instanz und eine update() Methode, um sie zu aktualisieren.

Der otpSender

Die Komponente otpSender Komponente sendet das OTP über die Vonage Dispatch API an den Benutzer. Sie ist in der Datei otpSender.js Datei wie folgt implementiert:

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

Es stellt eine Konfiguration zusammen, indem es die Daten aus der nexmo.json Datei und der private.key Datei. Diese Konfiguration wird an den Konstruktor der Bibliothek übergeben, um eine nexmo Instanz zu erhalten. Diese Instanz wird in der Implementierung der send() Funktion verwendet. Die Funktion nimmt eine OtpItem Instanz und ein recipientAddresses Objekt als Argumente und erstellt die Nachricht, die an den Benutzer gesendet werden soll, sowie die Nutzlast für die Dispatch API

Durch die nexmo.dispatch.create() Methode erstellen Sie einen Zustellungsworkflow mit Ausfallsicherung. Das zweite Argument der Methode ist ein Array mit drei Elementen:

  • Das erste Element ist ein Objekt, das den Absender, den Empfänger und den Text der zu sendenden Nachricht angibt. Der Typ des Absenders und des Empfängers gibt an, dass es sich um eine Messenger-Kommunikation handelt. Es hat auch eine failover Eigenschaft, die angibt, wann die Zustellung als fehlgeschlagen zu betrachten ist. In unserem Fall gilt sie als fehlgeschlagen, wenn die Nachricht nicht innerhalb von 120 Sekunden gelesen wird.

  • Das zweite Element ist ein weiteres Objekt, das den Absender, die Zustellung und den Text der Nachricht angibt, die gesendet werden soll, wenn die Zustellung durch den Messenger fehlschlägt. In diesem Fall gibt der Typ des Absenders und des Empfängers an, dass die Nachricht per SMS gesendet werden soll.

  • Der letzte Punkt ist eine Callback-Funktion, die ausgeführt wird, nachdem der Workflow an den Vonage-API-Server übermittelt wurde. In unserem Fall schreiben wir einfach die Workflow-Kennung in die Konsole (dispatch_uuid), die von Vonage zurückgegeben wird.

Dadurch erhöht sich die Wahrscheinlichkeit, dass das generierte OTP dem Benutzer unabhängig vom verwendeten Kommunikationsmedium zugestellt wird.

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/373925f138/andrea-chiarelli.png
Andrea Chiarelli

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.