Allow to re-auth by using the U2F token

This commit is contained in:
Raphael Michel
2018-04-26 20:24:03 +02:00
parent 30f8afca85
commit 97bf958b74
4 changed files with 62 additions and 4 deletions

View File

@@ -1,9 +1,10 @@
{% extends "pretixcontrol/auth/base.html" %}
{% load i18n %}
{% load bootstrap3 %}
{% load compress %}
{% load staticfiles %}
{% block content %}
<form class="form-signin" action="" method="post">
<form action="" method="post">
<form class="form-signin" id="u2f-form" action="" method="post">
{% csrf_token %}
<h3>{% trans "Welcome back!" %}</h3>
<p>
@@ -17,6 +18,15 @@
<input class="form-control" id="id_password" name="password" placeholder="{% trans "Password" %}"
title="" type="password" required="" autofocus>
</div>
{% if jsondata %}
<div class="sr-only alert alert-danger" id="u2f-error">
{% trans "U2F failed. Check that the correct authentication device is correctly plugged in." %}
</div>
<p><small>
<span class="fa fa-usb"></span>
{% trans "Alternatively, you can use your U2F device." %}
</small></p>
{% endif %}
<div class="form-group text-right">
<button type="submit" class="btn btn-primary btn-block">
{% trans "Continue" %}
@@ -26,5 +36,15 @@
</a>
</div>
{% if jsondata %}
<script type="text/json" id="u2f-login">
{{ jsondata|safe }}
</script>
{% endif %}
{% compress js %}
<script type="text/javascript" src="{% static "jquery/js/jquery-2.1.1.min.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/u2f-api.js" %}"></script>
<script type="text/javascript" src="{% static "pretixcontrol/js/ui/u2f.js" %}"></script>
{% endcompress %}
</form>
{% endblock %}

View File

@@ -20,6 +20,7 @@ from django_otp.plugins.otp_static.models import StaticDevice
from django_otp.plugins.otp_totp.models import TOTPDevice
from u2flib_server import u2f
from u2flib_server.jsapi import DeviceRegistration
from u2flib_server.utils import rand_bytes
from pretix.base.forms.user import User2FADeviceAddForm, UserSettingsForm
from pretix.base.models import Event, NotificationSetting, U2FDevice, User
@@ -48,9 +49,27 @@ class RecentAuthenticationRequiredMixin:
class ReauthView(TemplateView):
template_name = 'pretixcontrol/user/reauth.html'
@property
def app_id(self):
return get_u2f_appid(self.request)
def post(self, request, *args, **kwargs):
password = request.POST.get("password", "")
if request.user.check_password(password):
valid = False
if '_u2f_challenge' in self.request.session and password.startswith('{'):
devices = [DeviceRegistration.wrap(device.json_data)
for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)]
challenge = self.request.session.pop('_u2f_challenge')
try:
u2f.verify_authenticate(devices, challenge, password, [self.app_id])
valid = True
except Exception:
logger.exception('U2F login failed')
valid = valid or request.user.check_password(password)
if valid:
t = int(time.time())
request.session['pretix_auth_login_time'] = t
request.session['pretix_auth_last_used'] = t
@@ -61,6 +80,22 @@ class ReauthView(TemplateView):
messages.error(request, _('The password you entered was invalid, please try again.'))
return self.get(request, *args, **kwargs)
def get_context_data(self, **kwargs):
ctx = super().get_context_data()
devices = [DeviceRegistration.wrap(device.json_data)
for device in U2FDevice.objects.filter(confirmed=True, user=self.request.user)]
if devices:
challenge = u2f.start_authenticate(devices, challenge=rand_bytes(32))
self.request.session['_u2f_challenge'] = challenge.json
ctx['jsondata'] = challenge.json
else:
if '_u2f_challenge' in self.request.session:
del self.request.session['_u2f_challenge']
ctx['jsondata'] = None
return ctx
class UserSettings(UpdateView):
model = User