
Hinzufügen von Zwei-Faktor-Authentifizierung zu Ihrer Django-App mit Nexmo
Lesedauer: 12 Minuten
Ich habe meine Begeisterung für die Zwei-Faktor-Authentifizierung bereits auf dem Vonage-Blog mit einer Demo-Anwendung für mein "Kätzchen & Co" Geschäft. Interessanterweise ist nicht jeder gleichermaßen ein Fan von Katzen, einige von uns bevorzugen Hunde, einige von uns bevorzugen andere Tiere, aber wir alle lieben die Zwei-Faktor-Authentifizierung, oder?
Machen wir eine kleine Umfrage
In diesem Tutorial zeige ich Ihnen, wie Sie eine Zwei-Faktor-Authentifizierung zu Ihrer Django-Website hinzufügen können, indem Sie die Vonage Verify API. Zu diesem Zweck habe ich eine kleine App namens "Pollstr" - eine einfache Webanwendung für Umfragen. Ich weiß, dass sie wegen des fehlenden "e" im Namen über Nacht ein Erfolg werden wird. Ich möchte eine Zwei-Faktor-Authentifizierung hinzufügen, um sicherzustellen, dass die Leute tatsächlich die sind, für die sie sich ausgeben, und um Spam bei meinen Umfragen zu verhindern.
Pollstr screenshot
Sie können den Startpunkt der App herunterladen von Github herunterladen und lokal ausführen.
Dann besuchen Sie 127.0.0.1:8000 in Ihrem Browser und versuchen Sie, an einer Umfrage teilzunehmen. Sie können sich mit diesen Anmeldedaten anmelden:
Benutzernamen:
testKennwort:
test1234
Standardmäßig implementiert die App die Registrierung und Anmeldung mit dem in Django eingebauten Auth-Framework, aber der Großteil dieses Tutorials gilt auch für Apps, die andere Authentifizierungsmethoden verwenden. Zusätzlich haben wir Bootstrap hinzugefügt, um unsere App zu verschönern.
Der gesamte Code für diesen Ausgangspunkt befindet sich auf der Seite vor Zweig auf Github. Der gesamte Code, den wir im Folgenden hinzufügen werden, befindet sich im nach Zweig. Zu Ihrer Bequemlichkeit können Sie alle Änderungen zwischen unserem Start- und Endpunkt auch auf Github einsehen.
Vonage Verify für 2FA
Vonage Verify ist eine unkomplizierte und sichere Möglichkeit, die Telefonverifizierung mit nur 2 API-Aufrufen zu implementieren! Bei den meisten Zwei-Faktor-Authentifizierungssystemen müssen Sie Ihre eigenen Token, den Ablauf von Token, Wiederholungen und den SMS-Versand verwalten. Vonage Verify verwaltet all dies für Sie.
Um Vonage Verify zu unserer App hinzuzufügen, müssen wir die folgenden Änderungen vornehmen:
Hinzufügen einer
phone_numberzu unserem BenutzerHinzufügen eines
TwoFactorMixinzu unseren Ansichten hinzu, um sicherzustellen, dass der Benutzer angemeldet und verifiziert istAufzeichnung einer neuen Telefonnummer für neue Benutzer
Senden Sie dem Benutzer einen Verifizierungscode
Verify den an ihre Nummer gesendeten Code
Hinzufügen einer Rufnummer
Das Standard-Benutzermodell in Django verfügt nicht über eine Telefonnummer, also müssen wir selbst eine hinzufügen. Es gibt einige Möglichkeiten, wie wir dies tun können, aber in diesem Fall werden wir unseren gesamten neuen Code in einer neuen two_factor App.
Dies erzeugt eine Menge neuer Dateien im /two_factor Ordner. Öffnen wir den Ordner /two_factor/models.py und fügen ein neues Modell hinzu, das eine Eins-zu-Eins-Beziehung zu unserem Benutzer hat.
# two_factor/models.py
...
from django.contrib.auth.models import User
class TwoFactor(models.Model):
number = models.CharField(max_length=16)
user = models.OneToOneField(User)Als nächstes wollen wir die Migrationen für dieses Modell generieren, aber dazu müssen wir zuerst sicherstellen, dass wir two_factor.apps.TwoFactorConfig zu unserem INSTALLED_APPS.
# pollstr/settings.py
INSTALLED_APPS = [
'polls.apps.PollsConfig',
'two_factor.apps.TwoFactorConfig',
'django.contrib.admin',
...
]Damit können wir unsere Migrationen erstellen und unsere Datenbank migrieren:
Hinzufügen eines TwoFactorMixin
Unsere Django-Anwendung verwendet klassenbasierte Ansichten die es uns ermöglichen, benutzerdefinierte "Mixins" zu verwenden, um jeder Ansicht unser eigenes Verhalten hinzuzufügen. Derzeit verwenden wir das LoginRequiredMixin um sicherzustellen, dass wir eingeloggt sind, bevor wir an Umfragen teilnehmen können.
# polls/views.yml
class OptionsView(LoginRequiredMixin, DetailView):
...Wir werden eine neue TwoFactorMixin um eine TwoFactor-Schicht zu dieser Prüfung hinzuzufügen. Beginnen wir damit, unsere Ansichten so zu ändern, dass sie dieses neue Mixin verwenden, auch wenn wir es noch nicht geschrieben haben.
# polls/views.py
from two_factor.mixins import TwoFactorMixin
class OptionsView(TwoFactorMixin, DetailView):
...
class ResultsView(TwoFactorMixin, DetailView):
...
class VoteView(TwoFactorMixin, View):
...Fügen wir nun das Mixin in unsere two_factor App:
# two_factor/mixins.py
from django.contrib.auth.mixins import UserPassesTestMixin
from django.core.urlresolvers import reverse
class TwoFactorMixin(UserPassesTestMixin):
def test_func(self):
user = self.request.user
return (user.is_authenticated and "verified" in self.request.session)
def get_login_url(self):
if (self.request.user.is_authenticated()):
return reverse('two_factor:new')
else:
return reverse('login')Was wir hier getan haben, ist ein neues Mixin zu erstellen, das selbst die UserPassesTestMixin. Dieses Mixin ruft dann automatisch die test_func Funktion auf, in der wir prüfen, ob der Benutzer angemeldet ist und ob diese Sitzung verifiziert wurde. Letzteres tun wir, indem wir einfach prüfen, ob der Schlüssel verified in der Sitzung gesetzt wurde. Durch die Verwendung der Sitzung auf diese Weise kann eine Person auf mehreren Rechnern angemeldet sein und dennoch für jeden dieser Rechner eine Überprüfung verlangen.
Die Funktion get_login Funktion liefert der UserPassesTestMixin eine Route, zu der der Benutzer umgeleitet wird, wenn der Test fehlschlägt. In diesem Fall haben wir 2 Szenarien, eines, in dem der Benutzer überhaupt nicht angemeldet ist, und eines, in dem er angemeldet, aber nicht verifiziert ist.
Wenn Sie Ihren Server an dieser Stelle starten würden, würde er scheitern, weil wir noch keine der Routen oder Ansichten implementiert haben, an die der Benutzer weitergeleitet werden soll. Lassen Sie uns dies als nächstes tun.
Auswählen einer Rufnummer
Screen Capture of Number Verification Form
Wenn der Nutzer verifiziert werden muss, wird er weitergeleitet zu two_factor:new weitergeleitet, wo er aufgefordert wird, die Telefonnummer, an die wir einen Code senden werden, entweder festzulegen oder zu bestätigen.
# two_factor/urls.py
from django.conf.urls import url
from . import views
app_name = 'two_factor'
urlpatterns = [
url(r'^$', views.NewView.as_view(), name='new'),
url(r'^create/$', views.CreateView.as_view(), name='create'),
url(r'^verify/$', views.VerifyView.as_view(), name='verify'),
]Wir haben auch die URLs für unsere nächsten Schritte hinzugefügt. Als Nächstes müssen wir sicherstellen, dass wir diese URLs in unsere Hauptanwendung importieren.
# pollstr/urls.py
urlpatterns = [
...
url(r'^polls/', include('polls.urls')),
url(r'^2fa/', include('two_factor.urls')),
]Wenn die App auf /2fa/ weiterleitet, wird sie versuchen, die NewView Ansicht. Diese Ansicht wird das TwoFactor Modell für die Vorlage verfügbar machen, aber wir müssen die offensichtliche Ausnahme abfangen, wenn der Benutzer noch kein TwoFactor Objekt noch nicht hat, und stattdessen eines initialisieren.
# two_factor/views.py
from django.views.generic import DetailView
from django.contrib.auth.mixins import LoginRequiredMixin
from .models import TwoFactor
class NewView(LoginRequiredMixin, DetailView):
template_name = 'two_factor/new.html'
def get_object(self):
try:
return self.request.user.twofactor
except TwoFactor.DoesNotExist:
return TwoFactor.objects.create(user=self.request.user)Wir versuchen, den user.twofactor Datensatz zurückzugeben, aber wenn er nicht existiert, initialisieren wir stattdessen einen und geben diesen zurück.
Die Ansicht rendert die two_factor/new.html Vorlage, die es dem Benutzer ermöglicht, entweder seine Telefonnummer einzugeben, oder seine bereits eingegebene Nummer in einem deaktivierten Feld anzuzeigen. Wir werden die Nummer im deaktivierten Feld später ignorieren, wenn sie bereits eingegeben wurde, aber es ist eine schöne Erinnerung für den Benutzer, an welche Nummer der Code gesendet wird.
<!-- two_factor/templates/two_factor/new.html -->
{% extends 'polls/base.html' %}
{% block content %}
<form class='form-inline' action="{% url 'two_factor:create' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{ request.GET.next }}">
<p>
To continue we need to verify your phone number.
</p>
<div class="form-group">
<input type="text" name="number" value="{{ object.number }}"
{% if object.number %}disabled{% endif %} class='form-control'>
</div>
<div class="form-group">
<input type="submit" name="name" value="Verify" class="btn btn-primary">
</div>
</form>
{% endblock %}
Ohne den Bootstrap-Overhead ist unser Formular ein einfaches Formular mit ein paar Feldern:
Die
numberzur Übermittlung eines Codes anDie
nextSeite, auf die umgeleitet werden soll, nachdem wir mit der Verifizierung fertig sind, ist ein eingebautes Django-Feature, also lassen Sie uns schön damit spielen.
Wenn das Formular an /2fa/create gesendet wird, müssen wir den Code an den Benutzer mit Vonage senden.
Verwendung von Vonage Verify
Vonage Verify ist sehr einfach zu bedienen und besteht im Wesentlichen aus 2 API-Aufrufen. Der erste sendet den Verifizierungscode an die Telefonnummer des Benutzers. In unserem Fall geschieht dies in der CreateView wenn das Formular abgeschickt wird.
Um den Code zu senden, benötigen wir die vonage Python-Bibliothek. Wir haben diese bereits zu Ihrer requirements.txt zusammen mit der django-dotenv Bibliothek hinzugefügt, die es uns ermöglicht, unsere Anmeldedaten aus einer .env Datei zu laden. Wenn Sie eine andere Art der Verwaltung Ihrer App-Abhängigkeiten bevorzugen, können Sie sie direkt mit pip installieren.
Die vonage Bibliothek kann entweder mit einem API-Schlüssel und einem Geheimnis oder durch Setzen einiger Umgebungsvariablen instanziiert werden. Sie können Ihren Vonage-API-Schlüssel und -Geheimnis über das Entwickler Dashboard.
Mit diesen Umgebungsvariablen müssen wir unseren Vonage-Client nun nicht mehr initialisieren und können ihn direkt wie folgt verwenden.
# two_factor/views.py
from django.views.generic import DetailView, View
from django.contrib import messages
from django.contrib.auth.mixins import LoginRequiredMixin
from django.http import HttpResponseRedirect
from django.core.urlresolvers import reverse
from django.contrib.auth import logout
import nexmo
from .models import TwoFactor
class CreateView(LoginRequiredMixin, View):
def post(self, request):
number = self.find_or_set_number(request)
response = self.send_verification_request(request, number)
if (response['status'] == '0'):
request.session['verification_id'] = response['request_id']
return HttpResponseRedirect(reverse('two_factor:verify')+"?next="+request.POST['next'])
else:
logout(request)
messages.add_message(request, messages.INFO, 'Could not verify your number. Please contact support.')
return HttpResponseRedirect('/')
def find_or_set_number(self, request):
two_factor = request.user.twofactor
if (not two_factor.number):
two_factor.number = request.POST['number']
two_factor.save()
return two_factor.number
def send_verification_request(self, request, number):
client = nexmo.Client()
return client.start_verification(number=number, brand='Pollstr')Der Code hier tut einige Dinge. Zunächst einmal verwendet er find_or_set_number um zu prüfen, ob der Benutzer bereits eine Telefonnummer eingestellt hat, und nur wenn dies nicht der Fall ist wird die eingegebene Nummer gespeichert.
Es verwendet dann nexmo.Client().start_verification um den Verifizierungsprozess zu starten. Wir übergeben hier 2 Parameter: die number des Benutzers und einen benutzerfreundlichen brand Name, der in der gesendeten Textnachricht erscheinen wird.
Als nächstes prüfen wir, ob die status unseres API-Aufrufs gleich 0 ist, und wenn dies der Fall ist, speichern wir die request_id für diesen Überprüfungsversuch in der Sitzung. Wir tun dies, da wir diesen Code später id später benötigen, um den Code zu bestätigen, den der Benutzer erhalten hat.
Schließlich leiten wir den Benutzer zu unserer VerifyView die eine einfache Ansicht ist, die ein Formular zur Eingabe des Verifizierungscodes ausgibt.
# two_factor/views.py
from django.views.generic import DetailView, View, TemplateView
class VerifyView(LoginRequiredMixin, TemplateView):
template_name = 'two_factor/verify.html'Und die entsprechende Vorlage. Wie Sie sehen können, übergeben wir immer noch den next Wert weiter, so dass wir am Ende wieder zur richtigen Umfrage zurückkehren können.
<!-- two_factor/templates/two_factor/verify.html -->
{% extends 'polls/base.html' %}
{% block content %}
<form class="form-inline" action="{% url 'two_factor:confirm' %}" method="post">
{% csrf_token %}
<input type="hidden" name="next" value="{{request.GET.next}}">
<p>
We have sent a code to your number. Please type it in below.
</p>
<div class="form-group">
<input type="text" name="code" class="form-control">
</div>
<div class="form-group">
<input type="submit" name="name" value="Confirm" class="btn btn-primary">
</div>
</form>
{% endblock %} Überprüfung des Benutzercodes
Screengrab of 2 Factor Authentication Form
Der letzte Schritt in diesem Lernprogramm besteht darin, den vom Benutzer bereitgestellten Code zu bestätigen. Fügen wir zunächst die Route für diese Seite hinzu.
# two_factor/urls.py
urlpatterns = [
...
url(r'^confirm/$', views.ConfirmView.as_view(), name='confirm'),
]Im vorherigen Schritt wurde dem Benutzer ein Formular mit einem code Feld. Wenn er dieses an die two_factor:verify URL übermittelt, müssen wir die vonage Bibliothek erneut mit dem Code und den request_id die wir zuvor in der Sitzung gespeichert haben.
# two_factor/views.py
class ConfirmView(LoginRequiredMixin, View):
def post(self, request):
response = self.check_verification_request(request)
if (response['status'] == '0'):
request.session['verified'] = True
return HttpResponseRedirect(request.POST['next'])
else:
messages.add_message(request, messages.INFO, 'Could not verify code. Please try again.')
return HttpResponseRedirect(reverse('two_factor:verify')+"?next="+request.POST['next'])
def check_verification_request(self, request):
return nexmo.Client().check_verification(request.session['verification_id'], code=request.POST['code'])Wir verwenden die nexmo.Client().check_verification Funktion, um zu überprüfen, ob der Code für die request_id. Wenn sie erfolgreich war, lautet der Statuscode 0 und wir markieren die Sitzung als verifiziert. Wenn wir den Benutzer auf die Seite umleiten, auf der er begonnen hat, wird die TwoFactorMixin wird der Benutzer nun nicht mehr weggeleitet, sondern kann sich die Umfrage ansehen.
Using Vonage for 2 Authentication Factor
Nächste Schritte
Es gibt viele weitere Optionen in der Vonage Verify API als wir hier behandelt haben. Der Code, den wir hier gezeigt haben, ist ziemlich einfach und es gibt viele verschiedene Möglichkeiten, wie diese Benutzererfahrung implementiert werden kann. Das Vonage Verify System ist extrem belastbar, da es bei Bedarf auf Telefonanrufe zurückgreift, Token ablaufen lässt, ohne dass Sie etwas tun müssen, die Wiederverwendung von Token verhindert und die Verifizierungszeiten protokolliert.
Die Python-Bibliothek von Vonage ist sehr agnostisch, was ihre Verwendung angeht, was bedeutet, dass Sie Dinge ganz anders implementieren können, als ich es hier getan habe. Ich würde gerne wissen, was Sie als nächstes hinzufügen würden? Bitte schreiben Sie mir einen Tweet (ich bin @cbetta) mit Ihren Gedanken und Ideen.