mirror of
https://github.com/bookwyrm-social/bookwyrm.git
synced 2025-01-11 01:35:28 +00:00
various 2fa improvements
- cleaner code - use TWO_FACTOR_LOGIN_MAX_SECONDS instead of hardcoded number - render qrcode properly - use nginx to rate limit login attempts - do not throw error if session user is undefined
This commit is contained in:
parent
aefc7a23bc
commit
79b04c2240
3 changed files with 11 additions and 14 deletions
|
@ -358,3 +358,5 @@ else:
|
||||||
OTEL_EXPORTER_OTLP_ENDPOINT = env("OTEL_EXPORTER_OTLP_ENDPOINT", None)
|
OTEL_EXPORTER_OTLP_ENDPOINT = env("OTEL_EXPORTER_OTLP_ENDPOINT", None)
|
||||||
OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None)
|
OTEL_EXPORTER_OTLP_HEADERS = env("OTEL_EXPORTER_OTLP_HEADERS", None)
|
||||||
OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None)
|
OTEL_SERVICE_NAME = env("OTEL_SERVICE_NAME", None)
|
||||||
|
|
||||||
|
TWO_FACTOR_LOGIN_MAX_SECONDS = 60
|
||||||
|
|
|
@ -55,7 +55,7 @@ class Login(View):
|
||||||
user = authenticate(request, username=username, password=password)
|
user = authenticate(request, username=username, password=password)
|
||||||
if user is not None:
|
if user is not None:
|
||||||
# if 2fa is set, don't log them in until they enter the right code
|
# if 2fa is set, don't log them in until they enter the right code
|
||||||
if user.two_factor_auth is True:
|
if user.two_factor_auth:
|
||||||
request.session["2fa_user"] = user.username
|
request.session["2fa_user"] = user.username
|
||||||
request.session["2fa_auth_time"] = time.time()
|
request.session["2fa_auth_time"] = time.time()
|
||||||
return redirect("login-with-2fa")
|
return redirect("login-with-2fa")
|
||||||
|
|
|
@ -14,7 +14,7 @@ from django.views import View
|
||||||
from django.views.decorators.debug import sensitive_post_parameters
|
from django.views.decorators.debug import sensitive_post_parameters
|
||||||
|
|
||||||
from bookwyrm import forms, models
|
from bookwyrm import forms, models
|
||||||
from bookwyrm.settings import DOMAIN
|
from bookwyrm.settings import DOMAIN, TWO_FACTOR_LOGIN_MAX_SECONDS
|
||||||
from bookwyrm.views.helpers import set_language
|
from bookwyrm.views.helpers import set_language
|
||||||
|
|
||||||
# pylint: disable= no-self-use
|
# pylint: disable= no-self-use
|
||||||
|
@ -56,7 +56,7 @@ class Edit2FA(View):
|
||||||
qr_code.add_data(provisioning_url)
|
qr_code.add_data(provisioning_url)
|
||||||
qr_code.make(fit=True)
|
qr_code.make(fit=True)
|
||||||
img = qr_code.make_image(attrib={"fill": "black"})
|
img = qr_code.make_image(attrib={"fill": "black"})
|
||||||
return img.to_string()
|
return str(img.to_string(), 'utf-8') # to_string() returns a byte string
|
||||||
|
|
||||||
|
|
||||||
@method_decorator(login_required, name="dispatch")
|
@method_decorator(login_required, name="dispatch")
|
||||||
|
@ -108,24 +108,19 @@ class LoginWith2FA(View):
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
"""Check 2FA code and allow/disallow login"""
|
"""Check 2FA code and allow/disallow login"""
|
||||||
|
if "2fa_user" not in request.session:
|
||||||
|
request.session["2fa_auth_time"] = 0
|
||||||
|
return redirect("/")
|
||||||
user = models.User.objects.get(username=request.session["2fa_user"])
|
user = models.User.objects.get(username=request.session["2fa_user"])
|
||||||
elapsed_time = datetime.now() - datetime.fromtimestamp(
|
session_time = int(request.session["2fa_auth_time"]) if request.session["2fa_auth_time"] else 0
|
||||||
int(request.session["2fa_auth_time"])
|
elapsed_time = datetime.now() - datetime.fromtimestamp(session_time)
|
||||||
)
|
|
||||||
form = forms.Confirm2FAForm(request.POST, instance=user)
|
form = forms.Confirm2FAForm(request.POST, instance=user)
|
||||||
# don't allow the login credentials to last too long before completing login
|
# don't allow the login credentials to last too long before completing login
|
||||||
if elapsed_time > timedelta(seconds=60):
|
if elapsed_time > timedelta(seconds=TWO_FACTOR_LOGIN_MAX_SECONDS):
|
||||||
request.session["2fa_user"] = None
|
request.session["2fa_user"] = None
|
||||||
request.session["2fa_auth_time"] = 0
|
request.session["2fa_auth_time"] = 0
|
||||||
return redirect("/")
|
return redirect("/")
|
||||||
if not form.is_valid():
|
if not form.is_valid():
|
||||||
# make life harder for bots
|
|
||||||
# humans are unlikely to get it wrong more than twice
|
|
||||||
if "2fa_attempts" not in request.session:
|
|
||||||
request.session["2fa_attempts"] = 0
|
|
||||||
request.session["2fa_attempts"] = request.session["2fa_attempts"] + 1
|
|
||||||
time.sleep(2 ** request.session["2fa_attempts"])
|
|
||||||
|
|
||||||
data = {"form": form, "2fa_user": user}
|
data = {"form": form, "2fa_user": user}
|
||||||
return TemplateResponse(
|
return TemplateResponse(
|
||||||
request, "two_factor_auth/two_factor_login.html", data
|
request, "two_factor_auth/two_factor_login.html", data
|
||||||
|
|
Loading…
Reference in a new issue