Best Practices für SMS-OTP-Formulare

Es ist üblich, Nutzer aufzufordern, ein Einmalpasswort (OTP) anzugeben, um ihre Identität per SMS zu bestätigen. Hier sind einige Anwendungsfälle für SMS-OTP:

  • 2‑Faktor-Authentifizierung: Zusätzlich zu Nutzername und Passwort kann ein SMS-OTP als starkes Signal dafür verwendet werden, dass das Konto der Person gehört, die das SMS-OTP erhalten hat.
  • Bestätigung der Telefonnummer: Einige Dienste verwenden eine Telefonnummer als primäre Kennung des Nutzers. In solchen Diensten können Nutzer ihre Telefonnummer und das per SMS erhaltene OTP eingeben, um ihre Identität nachzuweisen. Manchmal wird sie mit einer PIN kombiniert, um eine 2-Faktor-Authentifizierung zu ermöglichen.
  • Kontowiederherstellung: Wenn ein Nutzer den Zugriff auf sein Konto verliert, muss es eine Möglichkeit geben, es wiederherzustellen. Das Senden einer E-Mail an die registrierte E-Mail-Adresse oder eines Einmalpassworts per SMS an die Telefonnummer sind gängige Methoden zur Kontowiederherstellung.
  • Zahlungsbestätigung: In Zahlungssystemen verlangen einige Banken oder Kreditkartenaussteller aus Sicherheitsgründen eine zusätzliche Authentifizierung des Zahlers. Zu diesem Zweck wird häufig ein SMS-OTP verwendet.

Im Folgenden finden Sie Best Practices zum Erstellen von SMS-OTP-Formularen für diese Anwendungsfälle.

Checkliste

So erreichen Sie die bestmögliche Nutzerfreundlichkeit mit dem SMS-OTP:

  • Verwenden Sie das <input>-Element mit:
    • type="text"
    • inputmode="numeric"
    • autocomplete="one-time-code"
  • Verwenden Sie @BOUND_DOMAIN #OTP_CODE als letzte Zeile der OTP-SMS.
  • Verwenden Sie die WebOTP API.

<input>-Element verwenden

Die Verwendung eines Formulars mit einem <input>-Element ist die wichtigste Best Practice, die du befolgen kannst, da sie in allen Browsern funktioniert. Auch wenn andere Vorschläge aus diesem Beitrag in einigen Browsern nicht funktionieren, kann der Nutzer das OTP trotzdem manuell eingeben und senden.

<form action="/verify-otp" method="POST">
  <input type="text"
      inputmode="numeric"
      autocomplete="one-time-code"
      pattern="\d{6}"
      required>
</form>

Im Folgenden finden Sie einige Ideen, wie Sie die Browserfunktionen für ein Eingabefeld optimal nutzen können.

type="text"

Da OTPs in der Regel fünf- oder sechsstellige Zahlen sind, mag die Verwendung von type="number" für ein Eingabefeld intuitiv erscheinen, da dadurch die mobile Tastatur nur auf Zahlen umgestellt wird. Dies wird nicht empfohlen, da der Browser erwartet, dass ein Eingabefeld eine zählbare Zahl und keine Sequenz mehrerer Zahlen ist. Dies kann zu unerwartetem Verhalten führen. Wenn Sie type="number" verwenden, werden neben dem Eingabefeld Aufwärts- und Abwärtsschaltflächen angezeigt. Durch Drücken dieser Schaltflächen wird die Zahl erhöht oder verringert und vorangestellte Nullen werden möglicherweise entfernt.

Verwenden Sie stattdessen type="text". Dadurch wird die mobile Tastatur nicht nur auf Zahlen umgestellt, aber das ist in Ordnung, da der nächste Tipp für die Verwendung von inputmode="numeric" diese Aufgabe übernimmt.

inputmode="numeric"

Verwenden Sie inputmode="numeric", um die Tastatur auf Mobilgeräten auf Zahlen umzustellen.

Einige Websites verwenden type="tel" für OTP-Eingabefelder, da dadurch auch die mobile Tastatur auf nur Zahlen (einschließlich * und #) umgestellt wird, wenn der Fokus darauf liegt. Dieser Hack wurde in der Vergangenheit verwendet, als inputmode="numeric" noch nicht weit verbreitet war. Seit Firefox inputmode="numeric" unterstützt, ist es nicht mehr erforderlich, den semantisch falschen type="tel"-Hack zu verwenden.

autocomplete="one-time-code"

Mit dem Attribut autocomplete können Entwickler angeben, welche Berechtigung der Browser hat, um Unterstützung beim automatischen Vervollständigen zu bieten. Außerdem wird der Browser über die Art der Informationen informiert, die im Feld erwartet werden.

Wenn ein Nutzer mit autocomplete="one-time-code" eine SMS erhält, während ein Formular geöffnet ist, parst das Betriebssystem das OTP in der SMS heuristisch und die Tastatur schlägt das OTP zur Eingabe vor. Sie funktioniert nur in Safari 12 und höher unter iOS, iPadOS und macOS. Wir empfehlen jedoch dringend, sie zu verwenden, da sie die SMS-OTP-Nutzung auf diesen Plattformen verbessert.

autocomplete="one-time-code" in Aktion.

autocomplete="one-time-code" verbessert die Nutzerfreundlichkeit, aber Sie können noch mehr tun, indem Sie dafür sorgen, dass die SMS-Nachricht dem ursprungsgebundenen Nachrichtenformat entspricht.

Optionale Attribute

Optionale Attribute:

  • pattern gibt das Format an, dem das eingegebene OTP entsprechen muss. Verwenden Sie reguläre Ausdrücke, um das Übereinstimmungsmuster anzugeben. Beispiel: \d{6} beschränkt das OTP auf einen sechsstelligen String. Weitere Informationen zu pattern finden Sie unter JavaScript für komplexere Echtzeitvalidierung verwenden.
  • required gibt an, dass ein Nutzer das Feld ausfüllen muss.

Weitere Informationen finden Sie in unseren Best Practices für Anmeldeformulare.

SMS-Text formatieren

Verbessere die Nutzerfreundlichkeit bei der Eingabe eines OTP, indem du dich an die Spezifikation für ursprungsgebundene Einmalcodes, die per SMS gesendet werden, hältst.

Die Formatregel lautet im Wesentlichen: Beenden Sie die SMS-Nachricht mit der Empfängerdomain, der @ vorangestellt ist, und dem OTP, dem # vorangestellt ist.

Beispiel:

Your OTP is 123456

@web-otp.example.com #123456

Das Standardformat für OTP-Nachrichten erleichtert die Extraktion und macht sie zuverlässiger. Wenn OTP-Codes mit Websites verknüpft werden, ist es schwieriger, Nutzer dazu zu bringen, einen Code auf schädlichen Websites anzugeben.

Genaue Formatierungsregeln

Die genauen Regeln lauten:

  • Die Nachricht beginnt mit einem (optionalen) menschenlesbaren Text, der einen vier- bis zehnstelligen alphanumerischen String mit mindestens einer Zahl enthält. Die letzte Zeile ist für die URL und das OTP reserviert.
  • Dem Domainteil der URL der Website, die die API aufgerufen hat, muss @ vorangestellt werden.
  • Die URL muss ein # enthalten, gefolgt vom OTP. Die Anzahl der Zeichen darf höchstens 140 betragen.

Dieses Format bietet mehrere Vorteile:

  • Das OTP wird an die Domain gebunden. Wenn sich der Nutzer auf anderen Domains als der in der SMS angegebenen befindet, wird der OTP-Vorschlag nicht angezeigt. Außerdem wird so das Risiko von Phishing-Angriffen und potenziellem Kontodiebstahl verringert.
  • Der Browser kann das OTP jetzt zuverlässig extrahieren, ohne auf mysteriöse und unzuverlässige Heuristiken angewiesen zu sein.

Wenn eine Website autocomplete="one-time-code" verwendet, schlägt Safari mit iOS 14 oder höher das OTP gemäß diesen Regeln vor.

Dieses SMS-Nachrichtenformat bietet auch für andere Browser als Safari Vorteile. Chrome, Opera und Vivaldi unter Android unterstützen die Regel für ursprungsgebundene Einmalcodes ebenfalls mit der WebOTP API, allerdings nicht über autocomplete="one-time-code".

WebOTP API verwenden

Die WebOTP API bietet Zugriff auf das OTP, das in einer SMS empfangen wurde. Wenn du navigator.credentials.get() mit dem Typ otp (OTPCredential) aufrufst, wobei transport den Wert sms enthält, wartet die Website auf eine SMS, die den ursprungsgebundenen Einmalcodes entspricht, und gewährt dem Nutzer Zugriff. Sobald das OTP an JavaScript übergeben wurde, kann die Website es in einem Formular verwenden oder direkt an den Server senden.

navigator.credentials.get({
  otp: {transport:['sms']}
})
.then(otp => input.value = otp.code);
WebOTP API in Aktion.

Eine ausführliche Anleitung zur Verwendung der WebOTP API finden Sie unter Telefonnummern im Web mit der WebOTP API bestätigen. Sie können auch das folgende Snippet kopieren und einfügen. Achten Sie darauf, dass Sie in der <form> ein action- und ein method-Attribut festlegen.

// Feature detection
if ('OTPCredential' in window) {
  window.addEventListener('DOMContentLoaded', e => {
    const input = document.querySelector('input[autocomplete="one-time-code"]');
    if (!input) return;
    // Cancel the WebOTP API if the form is submitted manually.
    const ac = new AbortController();
    const form = input.closest('form');
    if (form) {
      form.addEventListener('submit', e => {
        // Cancel the WebOTP API.
        ac.abort();
      });
    }
    // Invoke the WebOTP API
    navigator.credentials.get({
      otp: { transport:['sms'] },
      signal: ac.signal
    }).then(otp => {
      input.value = otp.code;
      // Automatically submit the form when an OTP is obtained.
      if (form) form.submit();
    }).catch(err => {
      console.log(err);
    });
  });
}