https://d226lax1qjow5r.cloudfront.net/blog/blogposts/how-to-add-two-factor-authentication-with-node-js-dr/Blog_Node-js_Verify_1200x600.png

Hinzufügen der Zwei-Faktor-Authentifizierung mit Node.js und Koa.js

Zuletzt aktualisiert am November 5, 2020

Lesedauer: 13 Minuten

Die Zwei-Faktor-Authentifizierung (2FA) verdankt ihren Namen der Tatsache, dass Sie zwei Dinge benötigen, um Ihre Identität zu verifizieren. Etwas, das Sie wissen, z. B. ein Passwort, und etwas, das Sie haben, z. B. den Verifizierungscode von Ihrem Mobilgerät oder einen physischen Token.

Das Hinzufügen von 2FA zu Ihrer Anwendung muss keine schwierige Aufgabe sein. In diesem Tutorial erfahren Sie, wie Sie mit Hilfe der Vonage Verify API 2FA für Ihre Web-Applikationen und -Services implementieren, um eine zusätzliche Sicherheitsebene zu schaffen. Wir werden eine einfache Koa.js-Anwendung erstellen, um zu verstehen, wie der zugrunde liegende Mechanismus funktioniert. Dadurch wird es einfacher zu sehen, wie dies in Ihre eigenen bestehenden Projekte passt, auch wenn Sie Koa.js nicht verwenden.

In diesem Tutorial wird gezeigt, wie man ein Verifizierungs-Token-System mit der Vonage Verify API und Koa.js implementiert. Wir haben ein ähnliches Node.js-Tutorial mit Express.js - Sie können es hier finden.

Sie beginnen mit einer Anmeldeseite, auf der Sie Ihre Benutzer nach einer Handynummer fragen. Nach der Eingabe wird er aufgefordert, einen Verifizierungscode einzugeben, der per SMS an seine Mobiltelefonnummer geschickt wird. Sobald das erledigt ist, kann der Nutzer auf die Anwendung zugreifen.

Voraussetzungen

  • Ein grundlegendes Verständnis von Javascript

  • Node.js auf Ihrem Rechner installiert

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.

In diesem Lernprogramm wird auch eine virtuelle Telefonnummer verwendet. Um eine zu erwerben, gehen Sie zu Rufnummern > Rufnummern kaufen und suchen Sie nach einer Nummer, die Ihren Anforderungen entspricht.

Sobald Sie ein Vonage API-Konto haben, finden Sie Ihren API-Schlüssel und Ihr API-Geheimnis oben auf der Seite Vonage-API-Dashboard.

Dieses Tutorial führt Sie von Anfang an durch den Prozess. Wenn Sie den fertigen Code sehen möchten, können Sie das Git-Repository für dieses Projekt klonen. Wir haben auch eine Glitch-Version, die ein noch ausgefalleneres Design hat, und Sie können remixen remixen. Beachten Sie, dass es bei der Glitch-Implementierung leichte Unterschiede gibt, um der Art und Weise, wie Projekte auf der Plattform gehostet werden, Rechnung zu tragen.

Glitch version of demoGlitch version of demo

Ein Koa.js-Projekt von Grund auf neu beginnen

Erstellen Sie einen Projektordner auf Ihrem lokalen Rechner und führen Sie dann den folgenden Befehl aus, um ein neues Node.js-Projekt einzurichten.

npm init

Dies löst eine Reihe von Eingabeaufforderungen aus, die Ihre package.json Datei erstellt. Wenn Sie möchten, können Sie die Antworten leer lassen und die Standardwerte verwenden.

Configuring package.jsonConfiguring package.json

Als nächstes installieren Sie Koa.js. Beachten Sie, dass Koa für ES2015 und die Unterstützung von asynchronen Funktionen Node 7.6.0 oder höher benötigt.

npm install koa --save

Erstellen Sie eine server.js Datei in Ihrem Projektordner.

touch server.js

Fügen Sie den folgenden Code in Ihre neu erstellte Datei ein.

const Koa = require('koa')
const port = process.env.PORT || 3000
const app = new Koa()

app.use(async ctx => {
  ctx.body = 'Hello Unicorn 🦄'
})

const listener = app.listen(port, function() {
  console.log('Your app is listening on port ' + listener.address().port)
})

Führen Sie die server.js Datei aus.

node server.js

Wenn Sie zu http://localhost:3000 in Ihrem Browser aufrufen, sollten Sie eine leere Seite mit dem Text "Hello Unicorn 🦄" sehen.

Check that server is runningCheck that server is running

Sie sollten auch Folgendes installieren dotenvinstallieren, das Ihnen erlaubt, Umgebungsvariablen, die in einer .env Datei gespeicherten Umgebungsvariablen in process.env.

npm install dotenv --save

Und nun können Sie die Datei .env Datei erstellen, die mindestens die folgenden Variablen enthalten sollte:

NEXMO_API_KEY='' NEXMO_API_SECRET=''

Um auf Umgebungsvariablen zugreifen zu können, müssen Sie sie anfordern, idealerweise am Anfang Ihrer server.js Datei.

require('dotenv').config()

Wenn Sie sich noch nicht für einen Nexmo Account angemeldet haben haben, ist jetzt ein guter Zeitpunkt, dies zu tun. Sobald Sie sich im Dashboard angemeldet haben, sollten Ihre API-Anmeldedaten das erste sein, was Sie sehen. Achten Sie darauf, sowohl Ihren Schlüssel als auch Ihr Geheimnis in Anführungszeichen zu setzen.

Projektstruktur

Im Moment würde Ihr Projekt wahrscheinlich nur eine package.json, a server.js Datei und eine .env Datei. Lassen Sie uns die Projektstruktur so einrichten, dass Sie ein grundlegendes Frontend haben, mit dem die Benutzer interagieren können.

PROJECT_NAME/               
    |-- public/             
    |   |-- client.js
    |   `-- style.css
    |-- views/
    |   `-- index.html
    |-- .env
    |-- package.json
    `-- server.js

In diesem Fall müssen Sie einige Änderungen an der server.js Datei vornehmen, um die index.html Datei und die zugehörigen Assets zu liefern, anstatt nur eine Textzeile. Koa.js ist ein recht einfaches Framework, so dass alle zusätzlichen Funktionen für das Routing oder die Bereitstellung statischer Elemente separat installiert werden müssen. Hier ist die Liste der zusätzlichen Module und ihrer Verwendungen:

  • koa-static für die Bereitstellung statischer Inhalte

  • koa-bodyparser für die Behandlung von Daten, die über POST-Anfragen gesendet werden

  • koa-router für Routing

  • koa-views zum Rendern von Vorlagen

Dieses Beispiel verwendet auch die Nunjucks um Vorlagendateien zu rendern. Die Vonage Verify API wird verwendet, um den Verifizierungscode per SMS auszulösen, daher müssen Sie auch die Vonage Node.js Client-Bibliothek installieren.

npm install koa-static koa-bodyparser koa-router koa-views nunjucks nexmo --save

Bereitstellen von statischen Assets und HTML-Dateien

Damit die Anwendung statische Elemente wie Stylesheets und clientseitiges JavaScript aus dem Verzeichnis /public zu ermöglichen, können Sie Folgendes in die server.js Datei hinzufügen:

const serve = require('koa-static')
app.use(serve('./public'))

Um HTML-Dateien aus dem Verzeichnis /views Ordner auszuliefern, können Sie die Funktion koa-viewsverwenden, die eine render() Funktion bietet. Die in diesem Beispiel verwendete Template-Engine ist Nunjucks, aber es steht Ihnen frei, die für Sie am besten geeignete Template-Engine zu wählen.

const views = require('koa-views')
app.use(views('./views', { map: { html: 'nunjucks' }}))

Als nächstes müssen Sie einige grundlegende Routen für die Bereitstellung Ihrer Anwendungsseiten einrichten.

const Router = require('koa-router')
const router = new Router()

router.get('/', (ctx, next) => {
  return ctx.render('./index')
})

app.use(router.routes()).use(router.allowedMethods())

Für dieses Beispiel werden Sie 3 Seiten benötigen, die index.html als Haupt-Landingpage, verify.html für die Eingabe des Verifizierungscodes durch die Benutzer und result.html um anzuzeigen, ob die Verifizierung erfolgreich war oder nicht.

Die Struktur des Webformulars ist recht einfach, und es steht Ihnen frei, es nach Belieben mit CSS zu verschönern.

<form method="post" action="verify">
  <input name="phone" type="tel" placeholder="+6588888888">
  <button>Get OTP</button>
</form>

Dieses Formular wird die Benutzereingaben an die /verify und Sie können die Telefonnummer in der Eingabe verwenden, um die Anforderung des Verifizierungscodes auszulösen. Ein ähnliches Formular kann für die anderen 2 Routen verwendet werden für /check und /cancel verwendet werden.

<form method="post" action="check">
  <input name="pin" placeholder="Enter PIN">
  <input name="reqId" type="hidden" value="{{ reqId }}">
  <button>Verify</button>
</form>
<form method="post" action="cancel">
  <input name="reqId" type="hidden" value="{{ reqId }}">
  <button class="inline">Cancel verification</button>
</form>

Behandlung von Benutzereingaben

Für die Bearbeitung von Benutzereingaben über Webformulare benötigen Sie dann einige Routen zur Bearbeitung von POST Anfragen zu bearbeiten. Stellen Sie sicher, dass Sie bodyparser() vor jeder der Routen zu deklarieren.

const bodyParser = require('koa-bodyparser')

/* This should appear before any routes */
app.use(bodyParser())

router.post('/verify/', async (ctx, next) => {
  const payload = await ctx.request.body
  /* Function to trigger verification code here */
})

router.post('/check/', async (ctx, next) => {
  const payload = await ctx.request.body
  /* Function to check verification code here */
})

router.post('/cancel/', async (ctx, next) => {
  const payload = await ctx.request.body
  /* Function to cancel verification code here */
})

Nachdem Sie nun die Telefonnummer Ihres Benutzers erhalten haben, müssen Sie die Verify API verwenden, um einen PIN-Code an diesen zu senden. Initialisieren Sie eine neue Nexmo-Instanz mit Ihren Vonage-API-Anmeldedaten.

const Nexmo = require('nexmo');
const nexmo = new Nexmo({
  apiKey: YOUR_API_KEY,
  apiSecret: YOUR_API_SECRET
});

Es gibt 3 Funktionen, um die wir uns kümmern müssen. Die erste ist die Auslösung des Verifizierungscodes mit der nexmo.verify.request() Funktion. Sie umfasst die Telefonnummer des Benutzers und eine Zeichenfolge für den Markennamen, der dem Benutzer als Absender angezeigt wird.

async function verify(number) {
  return new Promise(function(resolve, reject) {
    nexmo.verify.request({
      number: number,
      brand: process.env.NEXMO_BRAND_NAME
    }, (err, result) => {
      if (err) {
        console.error(err)
        reject(err)
      } else {
        resolve(result)
      }
    })
  })
}

Sobald Ihr Benutzer den PIN-Code per SMS erhalten hat, muss er ihn an die nexmo.verify.check() Funktion eingeben, damit er verifiziert werden kann. Sie werden einen request_id Parameter. Diesen Wert erhält man, wenn der PIN-Code erfolgreich ausgelöst wurde. Es gibt eine Reihe von Möglichkeiten, die Anfrage-ID an die Funktion nexmo.verify.check() Funktion zu übergeben, und in diesem Beispiel wird ein verstecktes Feld in der Prüfung Formular.

async function check(reqId, code) {
  return new Promise(function(resolve, reject) {
    nexmo.verify.check({
      request_id: reqId,
      code: code
    }, (err, result) => {
      if (err) {
        console.error(err)
        reject(err)
      } else {
        resolve(result)
      }
    })
  })
}

Die letzte Funktion gibt dem Benutzer die Möglichkeit, die Überprüfung abzubrechen, wenn er seine Meinung geändert hat. Sie verwendet die Funktion nexmo.verify.control() Funktion und erfordert wiederum die Anfrage-ID, die beim Auslösen des PIN-Codes generiert wird, sowie einen String-Wert von cancel.

async function cancel(reqId) {
  return new Promise(function(resolve, reject) {
    nexmo.verify.control({
      request_id: reqId,
      cmd: 'cancel'
    }, (err, result) => {
      if (err) {
        console.error(err)
        reject(err)
      } else {
        resolve(result)
      }
    })
  })
}

Landing page for demoLanding page for demo

Nun müssen Sie diese 3 Funktionen in den zuvor festgelegten Routen verwenden, wobei Sie zuerst mit der Funktion zum Auslösen des Verifizierungscodes beginnen.

router.post('/verify/', async (ctx, next) => {
  const payload = await ctx.request.body
  const phone = payload.phone

  const result = await verify(phone)
  const reqId = result.request_id 
  ctx.status = 200
  return ctx.render('./verify', { reqId: reqId })
})

Die ctx.request.body wird in etwa so aussehen:

{ 
  "phone": "+40987654321"
}

Sie können diese Telefonnummer nehmen und sie an die verify() Funktion übergeben. Solange es sich um eine gültige Telefonnummer handelt, wird der Verifizierungscode abgefeuert und Sie erhalten eine Antwort, die ein request_id und status.

{ 
  "request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
  "status": "0"
}

Von dort aus können Sie die Anfrage-ID an das Frontend senden, wenn der Benutzer den Verifizierungscode eingibt.

The request_id is passed to the frontendThe request_id is passed to the frontend

Wenn Ihr Benutzer die richtige PIN eingibt, müssen Sie sowohl die PIN als auch die Anfrage-ID in die check() Funktion eingeben.

router.post('/check/', async (ctx, next) => {
  const payload = await ctx.request.body
  const code = payload.pin
  const reqId = payload.reqId
  
  const result = await check(reqId, code)
  const status = result.status
  ctx.status = 200
  return ctx.render('./result', { status: status })
})

Auch diese beiden Werte können über die Seite ctx.request.body und wenn die PIN als korrekt validiert wird, erhalten Sie eine Antwort, die wie folgt aussieht:

{ 
  "request_id": "1bf002ecd1e94d8aa81ba7463b19f583",
  "status": "0",
  "event_id": "150000001AC57AB2",
  "price": "0.10000000",
  "currency": "EUR" 
}

Anhand des Statuscodes können Sie dann bestimmen, welche Meldung Sie dem Benutzer anzeigen möchten. In diesem Beispiel wird Nunjucks verwendet, so dass das Markup auf der Ergebnisseite etwa so aussehen könnte:

{% if status == 0 %}
<p>Code verified successfully. ¯\_(ツ)_/¯</p>
{% else %}
<p>Something went wrong… ಠ_ಠ</p>
<p>Please contact the administrator for more information.</p>
{% endif %}

Verification messagesVerification messages

Dies war eine gründliche Aufschlüsselung der einzelnen Teile des Codes, aber um einen Blick darauf zu werfen, wie die Anwendung in ihrer Gesamtheit aussieht, sollten Sie sich den Quellcode auf GitHub.

Zusätzliche Dinge, die zu beachten sind

Dieses Tutorial ist eine abgespeckte Version, die nur die für die Implementierung der Zwei-Faktor-Authentifizierung notwendigen Teile hervorhebt. Es gibt jedoch zahlreiche Dinge, die in einer tatsächlichen Anwendung beachtet werden müssen. Einer der wichtigsten Punkte ist die Fehlerbehandlung. Die Verify API gibt einen Statuswert von 0 für erfolgreiche Abfragen zurück, aber jeder andere Wert zeigt einen Fehler an.

Diese Fehler sollten behandelt werden, und die Benutzeroberfläche des Frontends sollte alle potenziellen Fehler, die eine erfolgreiche Überprüfung verhindern, widerspiegeln. Es könnte auch eine gute Idee sein, eine Art von Frontend-Validierung zu implementieren, oder sogar die Vonage Number Insight API zu nutzen, um sicherzustellen, dass nur gültige Numbers an die Verify API weitergeleitet werden.

Wie geht es weiter?

Wenn Sie mehr mit diesen APIs machen wollen, finden Sie hier einige Links, die Ihnen helfen könnten:

Teilen Sie:

https://a.storyblok.com/f/270183/384x384/46621147f0/huijing.png
Hui Jing ChenVonage Ehemalige

Hui Jing ist Developer Advocate bei Nexmo. Sie hat eine übermäßige Liebe zu CSS und Typografie und ist allgemein leidenschaftlich über alle Dinge im Web.