Zwei-Faktor-Authentifizierung mit sipgate.io implementieren

Julia
26.07.2022 0 8:58 min

Was ist sipgate.io?

SMS oder Faxe versenden und empfangen, den Anrufverlauf abrufen,  Anrufe initiieren und manipulieren – das alles kann sipgate.io! Mit unseren APIs können Sie unsere Telekommunikationsfunktionen flexibel in Ihren Projekten integrieren. Unsere Library und Tutorials unterstützen Sie dabei, Ihre Telefonie möglichst bequem zu gestalten.

Warum ist die Zwei-Faktor-Authentifizierung wichtig?

Traditionell erfolgt die Authentifizierung einfach durch die Eingabe von Benutzername/E-Mail und einem Passwort. Allerdings könnte es zu einem Missbrauch kommen, wenn eine dritte Person Zugang zu diesen Anmeldedaten erhält.

Um die Sicherheit ihres Kontos zu erhöhen, lässt sich die Zwei-Faktor-Authentifizierung verwenden. Dabei wird ein temporärer Code über einen zweiten Kanal gesendet. Beide, die Anmeldedaten und der temporäre Code, sind für eine erfolgreiche Anmeldung erforderlich.

Falls Sie mehr über das Thema erfahren möchten, was ein 2. Faktor ist und welche Möglichkeiten er bietet, lesen Sie den folgenden Artikel dazu. Außerdem schauen Sie sich gerne auf unserer Website an, wie wir die 2-FA bei sipgate.io Produkten einsetzen.

❗Anmerkung ❗ Im Vergleich zu anderen 2-FA-Methoden bietet SMS einen geringeren Sicherheitsstandard. Dies sollte bei der Wahl Ihres zweiten Authentifizierungsfaktors berücksichtigt werden. 

Allerdings bietet SMS-Faktor einen einfachen Einstieg in die 2-FA, sodass Sie sich in unserem Tutorial die Grundprinzipien der Verwendung des zweiten Faktors anschauen können. 

In diesem Tutorial

  • In dieser Schritt-für-Schritt-Anleitung schauen wir uns an, wie wir den Versand von Authentifizierungs-Tokens per SMS einrichten können. Für einen vollständigen 2FA-Dienst integrieren wir den Tutorial-Code in Ihren eigenen Dienst, der für die Benutzer:innen-Authentifizierung sowie die Generierung, Speicherung und Verifizierung temporärer Token zuständig ist.
  • Mit unserer SMS-Demo können Sie das Versenden von SMS über sipgate.io in Ihrem Browser testen.
  • Das vollständige Projekt finden Sie in unserer GitHub-Repository .

Voraussetzungen

In diesem Projekt möchten wir einen Webservice einrichten, der in der Lage ist, ein Authentifizierungs-Token und die Telefonnummern zu authentifizieren und daraufhin eine 2FA-SMS zu senden.

Die sipgate.io Node.js-Library bietet bereits eine Möglichkeit, SMS zu versenden. Um den Server zu implementieren, verwenden wir das express-Framework.

Step 1: das Projekt aufsetzen

Zunächst erstellen wir ein neues Node.js -Project und legen eine server.js-Datei an, die unseren Code enthält.

npm init -y
touch server.js

Danach installieren wir unsere express und sipgateio-Abhängigkeiten wie folgt:

npm install express
npm install sipgateio

Step 2: Server und Basisfunktionen aufbauen

Öffnen Sie die server.js-Datei in einem beliebigen Editor. Zuerst importieren wir die soeben installierten Abhängigkeiten.

const { createSMSModule, sipgateIO } = require("sipgateio");
const express = require("express");

Jetzt können wir den Server erstellen und ihn so konfigurieren, dass er JSON akzeptiert und auf Port 3000 läuft. Außerdem richten wir ihn so ein, dass er den Inhalt des Ordners /html mit express.static() liefert.

const port = 3000;
const app = express();
app.use(express.json());
app.use("/", express.static(__dirname + "/html", { extensions: ["html"] }));

app.listen(port, () => {
  console.log(`Example app listening at http://localhost:${port}`);
});

Um die sipgate.io-Library zu verwenden, erstellen wir den Client mit den Anmeldedaten, die wir zum Senden der SMS verwenden möchten:

const tokenId = YOUR_SIPGATE_TOKEN_ID;
const token = YOUR_SIPGATE_TOKEN;
const smsExtension = "s0";

const client = sipgateIO({ tokenId, token });

Wir definieren die Token-Credentials, um uns bei der REST-API zu authentifizieren. Die Konstante smsExtension enthält Ihren sipgate SMS-Identifier. Wie es im Code-Snippet gezeigt ist, hat die smsExtension den Wert “s0”.

Weitere Informationen zu persönlichen Zugangstokens finden Sie hier.

Im nächsten Schritt implementieren wir die Funktion sendAuthentificationSMS.

async function sendAuthentificationSMS(number, token) {
  const message = `Hello, your two-factor authentification token is: ${token}`;

  const shortMessage = {
    message,
    to: number,
    smsId: SMS_EXTENSION,
  };
  const sms = createSMSModule(client);

  await sms.send(shortMessage);
}

In der Funktion sendAuthentificationSMS erstellen wir ein SMS-Modul aus unserem Client-Objekt und verwenden es zum Versenden des Tokens.

Als Nächstes erstellen wir das Token. Die Funktion generateToken gibt eine 6-stellige Zufallszahl zurück, nachdem sie in unserem Token-Speicher abgelegt wurde.

function generateToken() {
  let token = "";
  for (let i = 0; i < TOKEN_DIGIT_COUNT; i++) {
    token += Math.floor(Math.random() * 10);
  }

  return token;
}

Wir erstellen auch eine einfache database.json-Benutzerdatenbank, um sich später anzumelden.

[{ "mail": "test@example.com", "phonenumber": "+4912345678901234" }]

In unserer server.js wollen wir nun das Laden von Dateien implementieren:

const fs = require("fs");

const jsonFile = fs.readFileSync("database.json");
const userDatabase = JSON.parse(jsonFile);

const tokenStorage = {};

Wir verwenden das fs-Modul und parsen die Rohdaten in ein userDatabase -Objekt. Um unsere generierten Tokens zu speichern, benutzen wir tokenStorage.

🗒Anmerkung: In einer echten Anwendung würden Sie Ihr eigenes Datenbanksystem anschließen.

Step 3: Webanwendung implementieren

Unsere Webanwendung besteht aus 3 HTML-Seiten: index.html, verify.html und success.html, die sich im /html-Ordner befinden.

Die Einstiegsseite ist die index.html:

<body>
  <h2>Login</h2>
  <form action="/login" method="POST">
    <input type="email" placeholder="Mail Address" name="mail" required />
    <input type="password" placeholder="Password" name="password" required />
    <input type="submit" name="submit" />
  </form>
  <p id="errorText"></p>
</body>
<script>
  const queryParams = new URLSearchParams(window.location.search);

  const errorTextElement = document.getElementById("errorText");
  const error = queryParams.get("error");
  if (error) {
    errorTextElement.innerText = error;
  }
</script>

Mit unserem Stylesheet könnte dies wie in der folgenden Abbildung aussehen:

Entry page

Wir erstellen zunächst ein einfaches Formular zur Eingabe der Mail-Adresse und des Passworts des Nutzers. Dieses Formular sendet die Daten an die URL /login . Der Einfachheit halber werden die Fehler behandelt, indem ein Fehlerstring über den Query-Parameter error an den entsprechenden Tag übergeben wird.

Im nächsten Schritt implementieren wir den /login-Pfad.

app.post("/login", async (request, response) => {
  const { mail } = request.body;
  if (!mail) {
    response.redirect("/?error=Please enter a mail address");
    return;
  }

  const entry = userDatabase.find((entry) => mail === entry.mail);
  if (!entry) {
    response.redirect("/?error=Mail address not found");
    return;
  }

  const generatedToken = generateToken();
  tokenStorage[mail] = {
    token: String(generatedToken),
    date: new Date(),
  };

  try {
    await sendAuthentificationSMS(entry.phonenumber, generatedToken);
    response.redirect("/verify?mail=" + mail);
  } catch (error) {
    response.redirect(`/?error=error: ${error.message}`);
  }
});

Zunächst definieren wir den POST-Pfad /login mithilfe der Express-Funktion app.post(). Innerhalb des Callbacks referenzieren wir die übermittelte Mail-Adresse durch Zugriff auf das Feld request.body.mail.

Danach überprüfen wir, ob die Mail in unserer Datenbank existiert. Der Einfachheit halber speichern oder überprüfen wir das Passwort nicht. In Ihrem eigenen Code sollten Sie jedoch Ihre Passwörter mit einer Datenbank abgleichen.

Sobald die grundlegende Authentifizierung erledigt ist, generieren wir im nächsten Schritt ein Token. Wir senden das von generateToken generierte Token per SMS und leiten zur Seite /verify mit dem Query-Parameter mail weiter.

Nun erstellen wir die verify.html-Seite:

<body>
  <h2>Enter your 2FA token</h2>
  <form action="/verify" method="POST">
    <input type="hidden" value="" id="mail" name="mail" />
    <input type="text" placeholder="2FA token" name="token" />
    <input type="submit" name="submit" />
  </form>
  <p id="errorText"></p>
</body>
<script>
  const queryParams = new URLSearchParams(window.location.search);

  const mailFieldElement = document.getElementById("mail");
  const mailAddress = queryParams.get("mail");
  mailFieldElement.setAttribute("value", mailAddress);

  const errorTextElement = document.getElementById("errorText");
  const error = queryParams.get("error");
  if (error) {
    errorTextElement.innerText = error;
  }
</script>

Wenn Sie unser Stylesheet verwenden, sieht das wie folgt aus:

Verify page

Diese Seite enthält das Formular für die Übermittlung des Tokens, den per SMS erhalten wird. Wir können nun die E-Mail-Adresse, die wir im vorherigen Schritt angehängt haben, aus der URL extrahieren und sie mit einem kurzen JavaScript-Block in ein verstecktes Formularfeld einfügen. Dies ist erforderlich, um die E-Mail-Adresse bei der Übermittlung des Formulars zu übergeben.

app.post("/verify", (request, response) => {
  let { mail, token } = request.body;
  if (!mail) {
    response.redirect("/verify?error=Mail address not set!");
    return;
  }

  if (!token) {
    response.redirect(`/verify?error=Token not set!&mail=${mail}`);
    return;
  }

  token = token.trim();
  const tokenPair = tokenStorage[mail];

  if (!tokenPair) {
    response.redirect(
      `/verify?error=No Token saved for given mail!&mail=${mail}`
    );
    return;
  }

  if (tokenPair.token != token) {
    response.redirect(`/verify?error=Token incorrect!&mail=${mail}`);
    return;
  }

  const expirationTime = new Date(tokenPair.date.getTime() + 5 * 60 * 1000);

  delete tokenStorage[mail];
  if (new Date() > expirationTime) {
    response.redirect(`/verify?error=Token expired!&mail=${mail}`);
    return;
  }

  response.redirect(`/success`);
});

Bei der Übermittlung des erhaltenen Tokens über das Formular an /verify prüfen wir zunächst, ob mail übergeben wurde und als Key zu einem tokenPair in unserer Datenbank existiert. Dann prüfen wir, ob das Token korrekt und noch gültig ist. Wenn alles stimmt, leiten wir auf eine einfache /success-Seite um. Andernfalls reagieren wir entsprechend auf einen Fehler.

An dieser Stelle können Sie auch dedizierte OAuth-Tokens für Ihre User-Sessions erstellen.

<body>
  <h1>Success!</h1>
  <p>Token validation was successful.</p>
</body>

Nach dem Speichern können Sie nun den Server mit npm run startstarten.

Zusätzliche Anmerkung

Beachten Sie, dass Sie nicht mehrere SMS auf einmal versenden sollten, da dies in der Verwendung des sipgate-Dienstes nicht erlaubt ist.

Fazit

In diesem Tutorial versenden wir mithilfe von sipgate.io ein Authentifizierungstoken per SMS. Für einen vollständigen 2FA-Service können Sie Ihre Benutzerdatenbank und einen Token-Generator integrieren, um das Benutzerkonto mit einem temporären Authentifizierungstoken zu verknüpfen.

Das vollständige Projekt finden Sie in unserem GitHub-Repository .

Wenn Sie mehr über die Möglichkeiten unserer sipgate.io-Library erfahren möchten, schauen Sie sich die anderen Tutorials in unserem Blog an.

Keine Kommentare


Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.