backup codes
- add hotp_secret to user model - view to create backup codes in user prefs - check backup code if otp doesn't work - increment hotp count if used - show correct errors if code wrong
This commit is contained in:
parent
9616abb6bd
commit
9b74c26742
7 changed files with 89 additions and 17 deletions
|
@ -36,6 +36,7 @@ from .preferences.two_factor_auth import (
|
|||
Edit2FA,
|
||||
Confirm2FA,
|
||||
Disable2FA,
|
||||
GenerateBackupCodes,
|
||||
LoginWith2FA,
|
||||
Prompt2FA,
|
||||
)
|
||||
|
|
|
@ -59,6 +59,7 @@ class Edit2FA(View):
|
|||
return img.to_string()
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class Confirm2FA(View):
|
||||
"""confirm user's 2FA settings"""
|
||||
|
||||
|
@ -81,6 +82,7 @@ class Confirm2FA(View):
|
|||
return TemplateResponse(request, "preferences/2fa.html", data)
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class Disable2FA(View):
|
||||
"""Turn off 2FA on this user account"""
|
||||
|
||||
|
@ -118,7 +120,7 @@ class LoginWith2FA(View):
|
|||
if not form.is_valid():
|
||||
# make life harder for bots
|
||||
# humans are unlikely to get it wrong more than twice
|
||||
if not request.session["2fa_attempts"]:
|
||||
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"])
|
||||
|
@ -134,6 +136,36 @@ class LoginWith2FA(View):
|
|||
return set_language(user, redirect("/"))
|
||||
|
||||
|
||||
@method_decorator(login_required, name="dispatch")
|
||||
class GenerateBackupCodes(View):
|
||||
"""Generate and display backup 2FA codes"""
|
||||
|
||||
def get(self, request):
|
||||
"""Generate and display backup 2FA codes"""
|
||||
data = {"backup_codes": self.generate_backup_codes(request.user)}
|
||||
return TemplateResponse(request, "preferences/2fa.html", data)
|
||||
|
||||
def generate_backup_codes(self, user):
|
||||
"""generate fresh backup codes for 2FA"""
|
||||
|
||||
# create fresh hotp secrets and count
|
||||
hotp_secret = pyotp.random_base32()
|
||||
user.hotp_count = 0
|
||||
# save the secret to the user record
|
||||
user.hotp_secret = hotp_secret
|
||||
user.save(broadcast=False, update_fields=["hotp_count", "hotp_secret"])
|
||||
|
||||
# generate codes
|
||||
hotp = pyotp.HOTP(hotp_secret)
|
||||
counter = 0
|
||||
codes = []
|
||||
while counter < 10:
|
||||
codes.append(hotp.at(counter))
|
||||
counter = counter + 1
|
||||
|
||||
return codes
|
||||
|
||||
|
||||
class Prompt2FA(View):
|
||||
"""Alert user to the existence of 2FA"""
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue