Not me @@ -21,9 +21,9 @@
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 916aa93..666747f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,13 +1,22 @@
# Change Log
## 3.0 (Beta)
+
+This is a major cleanup and CSS adjustments so please test before deployment.
+
* Updated to fido2==1.1.3
-* Removed: CBOR and exchange is done in JSON now
+* Removed: CBOR and exchange is done in JSON now.
+* Removed: `simplejson` package from dependencies.
+* Email OTP is always 6 numbers.
+* Better support for bootstrap 4 and 5.
* Added: the following settings
* `MFA_FIDO2_RESIDENT_KEY`: Defaults to `Discouraged` which was the old behaviour
* `MFA_FIDO2_AUTHENTICATOR_ATTACHMENT`: If you like to have a PLATFORM Authenticator, Defaults to NONE
* `MFA_FIDO2_USER_VERIFICATION`: If you need User Verification
* `MFA_FIDO2_ATTESTATION_PREFERENCE`: If you like to have an Attention
+ * `MFA_ENFORCE_EMAIL_TOKEN`: if you want the user to receive OTP by email without enrolling, if this the case, the system admins shall make sure that emails are valid.
+ * `MFA_SHOW_OTP_IN_EMAIL_SUBJECT`: If you like to show the OTP in the email subject
+ * `MFA_OTP_EMAIL_SUBJECT`: The subject of the email after the token allows placeholder '%s' for otp
## 2.9.0
* Add: Set black as code formatter
diff --git a/README.md b/README.md
index d4c8fce..5b5be2e 100644
--- a/README.md
+++ b/README.md
@@ -80,6 +80,7 @@ Depends on
```python
from django.conf.global_settings import PASSWORD_HASHERS as DEFAULT_PASSWORD_HASHERS #Preferably at the same place where you import your other modules
+
MFA_UNALLOWED_METHODS=() # Methods that shouldn't be allowed for the user e.g ('TOTP','U2F',)
MFA_LOGIN_CALLBACK="" # A function that should be called by username to login the user in session
MFA_RECHECK=True # Allow random rechecking of the user
@@ -91,7 +92,7 @@ Depends on
MFA_ALWAYS_GO_TO_LAST_METHOD = False # Always redirect the user to the last method used to save a click (Added in 2.6.0).
MFA_RENAME_METHODS={} #Rename the methods in a more user-friendly way e.g {"RECOVERY":"Backup Codes"} (Added in 2.6.0)
MFA_HIDE_DISABLE=('FIDO2',) # Can the user disable his key (Added in 1.2.0).
- MFA_OWNED_BY_ENTERPRISE = FALSE # Who owns security keys
+ MFA_OWNED_BY_ENTERPRISE = False # Who owns security keys
PASSWORD_HASHERS = DEFAULT_PASSWORD_HASHERS # Comment if PASSWORD_HASHER already set in your settings.py
PASSWORD_HASHERS += ['mfa.recovery.Hash']
RECOVERY_ITERATION = 350000 #Number of iteration for recovery code, higher is more secure, but uses more resources for generation and check...
@@ -101,10 +102,16 @@ Depends on
U2F_APPID="https://localhost" #URL For U2F
FIDO_SERVER_ID=u"localehost" # Server rp id for FIDO2, it is the full domain of your project
FIDO_SERVER_NAME=u"PROJECT_NAME"
+
+ import mfa
MFA_FIDO2_RESIDENT_KEY = mfa.ResidentKey.DISCOURAGED # Resident Key allows a special User Handle
MFA_FIDO2_AUTHENTICATOR_ATTACHMENT = None # Let the user choose
MFA_FIDO2_USER_VERIFICATION = None # Verify User Presence
MFA_FIDO2_ATTESTATION_PREFERENCE = mfa.AttestationPreference.NONE
+
+ MFA_ENFORCE_EMAIL_TOKEN = False # If you want the user to receive OTP by email without enrolling, if this the case, the system admins shall make sure that emails are valid.
+ MFA_SHOW_OTP_IN_EMAIL_SUBJECT = False #If you like to show the OTP in the email subject
+ MFA_OTP_EMAIL_SUBJECT= "OTP" # The subject of the email after the token
```
**Method Names**
* U2F
@@ -123,6 +130,7 @@ Depends on
* Added: `MFA_ALWAYS_GO_TO_LAST_METHOD`, `MFA_RENAME_METHODS`, `MFA_ENFORCE_RECOVERY_METHOD` & `RECOVERY_ITERATION`
* Starting version 3.0
* Added: `MFA_FIDO2_RESIDENT_KEY`, `MFA_FIDO2_AUTHENTICATOR_ATTACHMENT`, `MFA_FIDO2_USER_VERIFICATION`, `MFA_FIDO2_ATTESTATION_PREFERENCE`
+ * Added: `MFA_ENFORCE_EMAIL_TOKEN`, `MFA_SHOW_OTP_IN_EMAIL_SUBJECT`, `MFA_OTP_EMAIL_SUBJECT`
4. Break your login function
Usually your login function will check for username and password, log the user in if the username and password are correct and create the user session, to support mfa, this has to change
diff --git a/mfa/Common.py b/mfa/Common.py
index 2263ba9..6679e97 100644
--- a/mfa/Common.py
+++ b/mfa/Common.py
@@ -1,4 +1,8 @@
+import datetime
+from random import randint
+
from django.conf import settings
+from django.contrib.auth import get_user_model
from django.core.mail import EmailMessage
try:
@@ -24,3 +28,20 @@ def get_redirect_url():
),
"reg_success_msg": getattr(settings, "MFA_SUCCESS_REGISTRATION_MSG"),
}
+
+
+def get_username_field():
+ User = get_user_model()
+ USERNAME_FIELD = getattr(User, "USERNAME_FIELD", "username")
+ return User, USERNAME_FIELD
+
+
+def set_next_recheck():
+ if getattr(settings, "MFA_RECHECK", False):
+ delta = datetime.timedelta(
+ seconds=randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX)
+ )
+ return {
+ "next_check": datetime.datetime.timestamp(datetime.datetime.now() + delta)
+ }
+ return {}
diff --git a/mfa/Email.py b/mfa/Email.py
index 0b3fdb2..564518b 100644
--- a/mfa/Email.py
+++ b/mfa/Email.py
@@ -10,22 +10,26 @@
from .models import User_Keys
from .views import login
-from .Common import send
+from .Common import send, get_username_field, set_next_recheck
def sendEmail(request, username, secret):
"""Send Email to the user after rendering `mfa_email_token_template`"""
-
- User = get_user_model()
- key = getattr(User, "USERNAME_FIELD", "username")
- kwargs = {key: username}
+ User, UsernameField = get_username_field()
+ kwargs = {UsernameField: username}
user = User.objects.get(**kwargs)
res = render(
request,
"mfa_email_token_template.html",
{"request": request, "user": user, "otp": secret},
)
- return send([user.email], "OTP", res.content.decode())
+ subject = getattr(settings, "MFA_OTP_EMAIL_SUBJECT", "OTP")
+ if getattr(settings, "MFA_SHOW_OTP_IN_EMAIL_SUBJECT", False):
+ if "%s" in subject:
+ subject = subject % secret
+ else:
+ subject = secret + " " + subject
+ return send([user.email], subject, res.content.decode())
@never_cache
@@ -35,7 +39,8 @@ def start(request):
if request.method == "POST":
if request.session["email_secret"] == request.POST["otp"]: # if successful
uk = User_Keys()
- uk.username = request.user.username
+ User, USERNAME_FIELD = get_username_field()
+ uk.username = USERNAME_FIELD
uk.key_type = "Email"
uk.enabled = 1
uk.save()
@@ -64,8 +69,8 @@ def start(request):
)
context["invalid"] = True
else:
- request.session["email_secret"] = str(
- randint(0, 100000)
+ request.session["email_secret"] = str(randint(0, 1000000)).zfill(
+ 6
) # generate a random integer
if sendEmail(request, request.user.username, request.session["email_secret"]):
@@ -78,20 +83,23 @@ def auth(request):
"""Authenticating the user by email."""
context = csrf(request)
if request.method == "POST":
+ username = request.session["base_username"]
+
if request.session["email_secret"] == request.POST["otp"].strip():
- uk = User_Keys.objects.get(
- username=request.session["base_username"], key_type="Email"
- )
+ email_keys = User_Keys.objects.filter(username=username, key_type="Email")
+ if email_keys.exists():
+ uk = email_keys.first()
+ elif getattr(settings, "MFA_ENFORCE_EMAIL_TOKEN", False):
+ uk = User_Keys()
+ uk.username = username
+ uk.key_type = "Email"
+ uk.enabled = 1
+ uk.save()
+ else:
+ raise Exception("Email is not a valid method for this user")
+
mfa = {"verified": True, "method": "Email", "id": uk.id}
- if getattr(settings, "MFA_RECHECK", False):
- mfa["next_check"] = datetime.datetime.timestamp(
- datetime.datetime.now()
- + datetime.timedelta(
- seconds=random.randint(
- settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX
- )
- )
- )
+ mfa.update(set_next_recheck())
request.session["mfa"] = mfa
from django.utils import timezone
@@ -101,7 +109,7 @@ def auth(request):
return login(request)
context["invalid"] = True
else:
- request.session["email_secret"] = str(randint(0, 100000))
+ request.session["email_secret"] = str(randint(0, 1000000)).zfill(6)
if sendEmail(
request, request.session["base_username"], request.session["email_secret"]
):
diff --git a/mfa/FIDO2.py b/mfa/FIDO2.py
index 300f1a5..6b46991 100644
--- a/mfa/FIDO2.py
+++ b/mfa/FIDO2.py
@@ -1,11 +1,11 @@
+import json
+
from fido2.server import Fido2Server, PublicKeyCredentialRpEntity
-from fido2.webauthn import AttestationObject, AuthenticatorData, CollectedClientData, RegistrationResponse
+from fido2.webauthn import RegistrationResponse
from django.template.context_processors import csrf
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render
-import simplejson
-from fido2 import cbor
from django.http import HttpResponse
from django.conf import settings
from fido2.utils import websafe_decode, websafe_encode
@@ -13,16 +13,19 @@
from .views import login, reset_cookie
from .models import User_Keys
import datetime
-from .Common import get_redirect_url
+from .Common import get_redirect_url, set_next_recheck
from django.utils import timezone
import fido2.features
from django.http import JsonResponse
+
def enable_json_mapping():
try:
fido2.features.webauthn_json_mapping.enabled = True
except:
pass
+
+
def recheck(request):
"""Starts FIDO2 recheck"""
context = csrf(request)
@@ -34,9 +37,14 @@ def recheck(request):
def getServer():
"""Get Server Info from settings and returns a Fido2Server"""
from mfa import AttestationPreference
- rp = PublicKeyCredentialRpEntity(id=settings.FIDO_SERVER_ID, name=settings.FIDO_SERVER_NAME)
- attestation= getattr(settings,'MFA_FIDO2_ATTESTATION_PREFERENCE', AttestationPreference.NONE )
- return Fido2Server(rp,attestation=attestation)
+
+ rp = PublicKeyCredentialRpEntity(
+ id=settings.FIDO_SERVER_ID, name=settings.FIDO_SERVER_NAME
+ )
+ attestation = getattr(
+ settings, "MFA_FIDO2_ATTESTATION_PREFERENCE", AttestationPreference.NONE
+ )
+ return Fido2Server(rp, attestation=attestation)
def begin_registeration(request):
@@ -44,33 +52,46 @@ def begin_registeration(request):
enable_json_mapping()
server = getServer()
from mfa import ResidentKey
- resident_key = getattr(settings,'MFA_FIDO2_RESIDENT_KEY', ResidentKey.DISCOURAGED)
- auth_attachment = getattr(settings,'MFA_FIDO2_AUTHENTICATOR_ATTACHMENT', None)
- user_verification = getattr(settings,'MFA_FIDO2_USER_VERIFICATION', None)
- registration_data, state = server.register_begin({
- u'id': request.user.username.encode("utf8"),
- u'name': request.user.username,
- u'displayName': request.user.username,
- }, getUserCredentials(request.user.username),user_verification = user_verification,
- resident_key_requirement = resident_key, authenticator_attachment = auth_attachment)
- request.session['fido2_state'] = state
+
+ resident_key = getattr(settings, "MFA_FIDO2_RESIDENT_KEY", ResidentKey.DISCOURAGED)
+ auth_attachment = getattr(settings, "MFA_FIDO2_AUTHENTICATOR_ATTACHMENT", None)
+ user_verification = getattr(settings, "MFA_FIDO2_USER_VERIFICATION", None)
+ registration_data, state = server.register_begin(
+ {
+ "id": request.user.username.encode("utf8"),
+ "name": request.user.username,
+ "displayName": request.user.username,
+ },
+ getUserCredentials(request.user.username),
+ user_verification=user_verification,
+ resident_key_requirement=resident_key,
+ authenticator_attachment=auth_attachment,
+ )
+ request.session["fido2_state"] = state
return JsonResponse(dict(registration_data))
- #return HttpResponse(cbor.encode(registration_data), content_type = 'application/octet-stream')
+ # return HttpResponse(cbor.encode(registration_data), content_type = 'application/octet-stream')
@csrf_exempt
def complete_reg(request):
- """Completes the registeration, called by API"""
+ """Completes the registration, called by API"""
try:
if not "fido2_state" in request.session:
- return JsonResponse({'status': 'ERR', "message": "FIDO Status can't be found, please try again"})
+ return JsonResponse(
+ {
+ "status": "ERR",
+ "message": "FIDO Status can't be found, please try again",
+ }
+ )
enable_json_mapping()
- data = simplejson.loads(request.body)
+ data = json.loads(request.body)
server = getServer()
- auth_data = server.register_complete(request.session["fido2_state"], response = data)
+ auth_data = server.register_complete(
+ request.session["fido2_state"], response=data
+ )
registration = RegistrationResponse.from_dict(data)
attestation_object = registration.response.attestation_object
- #auth_data = attestation_object.auth_data
+ # auth_data = attestation_object.auth_data
att_obj = attestation_object
encoded = websafe_encode(auth_data.credential_data)
@@ -97,14 +118,16 @@ def complete_reg(request):
"FIDO2", "FIDO2"
),
}
- return HttpResponse(simplejson.dumps({"status": "RECOVERY"}))
+ return JsonResponse({"status": "RECOVERY"})
else:
- return HttpResponse(simplejson.dumps({"status": "OK"}))
+ return JsonResponse({"status": "OK"})
except Exception as exp:
import traceback
+
print(traceback.format_exc())
return JsonResponse(
- {"status": "ERR", "message": "Error on server, please try again later"}, status=500
+ {"status": "ERR", "message": "Error on server, please try again later"},
+ status=500,
)
@@ -124,7 +147,10 @@ def start(request):
def getUserCredentials(username):
- return [AttestedCredentialData(websafe_decode(uk.properties["device"])) for uk in User_Keys.objects.filter(username = username, key_type = "FIDO2")]
+ return [
+ AttestedCredentialData(websafe_decode(uk.properties["device"]))
+ for uk in User_Keys.objects.filter(username=username, key_type="FIDO2")
+ ]
def auth(request):
@@ -135,16 +161,18 @@ def auth(request):
def authenticate_begin(request):
enable_json_mapping()
server = getServer()
- credentials=[]
+ credentials = []
username = None
if "base_username" in request.session:
username = request.session["base_username"]
if request.user.is_authenticated:
username = request.user.username
if username:
- credentials = getUserCredentials(request.session.get("base_username", request.user.username))
+ credentials = getUserCredentials(
+ request.session.get("base_username", request.user.username)
+ )
auth_data, state = server.authenticate_begin(credentials)
- request.session['fido2_state'] = state
+ request.session["fido2_state"] = state
return JsonResponse(dict(auth_data))
@@ -160,53 +188,61 @@ def authenticate_complete(request):
if request.user.is_authenticated:
username = request.user.username
server = getServer()
- data = simplejson.loads(request.body)
- userHandle = data.get("response",{}).get('userHandle')
- credential_id = data['id']
+ data = json.loads(request.body)
+ userHandle = data.get("response", {}).get("userHandle")
+ credential_id = data["id"]
if userHandle:
if User_Keys.objects.filter(username=userHandle).exists():
credentials = getUserCredentials(userHandle)
- username=userHandle
+ username = userHandle
else:
- keys = User_Keys.objects.filter(user_handle = userHandle)
+ keys = User_Keys.objects.filter(user_handle=userHandle)
if keys.exists():
- credentials = [AttestedCredentialData(websafe_decode(keys[0].properties["device"]))]
+ credentials = [
+ AttestedCredentialData(
+ websafe_decode(keys[0].properties["device"])
+ )
+ ]
elif credential_id and username is None:
- keys = User_Keys.objects.filter(user_handle = credential_id)
+ keys = User_Keys.objects.filter(user_handle=credential_id)
if keys.exists():
- credentials=[AttestedCredentialData(websafe_decode(keys[0].properties["device"]))]
+ credentials = [
+ AttestedCredentialData(websafe_decode(keys[0].properties["device"]))
+ ]
else:
credentials = getUserCredentials(username)
try:
cred = server.authenticate_complete(
- request.session.pop('fido2_state'), credentials = credentials, response = data
+ request.session.pop("fido2_state"),
+ credentials=credentials,
+ response=data,
)
except ValueError:
- return JsonResponse(
+ return (
+ JsonResponse(
{
"status": "ERR",
"message": "Wrong challenge received, make sure that this is your security and try again.",
- }
- , status=400),
+ },
+ status=400,
+ ),
+ )
except Exception as excep:
- return JsonResponse({"status": "ERR", "message": str(excep)},
- status=500
- )
+ return JsonResponse({"status": "ERR", "message": str(excep)}, status=500)
if request.session.get("mfa_recheck", False):
- import time
-
request.session["mfa"]["rechecked_at"] = time.time()
- return HttpResponse(
- simplejson.dumps({"status": "OK"}), content_type="application/json"
- )
+ request.session["mfa"].update(set_next_recheck())
+ return JsonResponse({"status": "OK"})
+
else:
- import random
if keys is None:
- keys = User_Keys.objects.filter(username = username, key_type = "FIDO2", enabled = 1)
+ keys = User_Keys.objects.filter(
+ username=username, key_type="FIDO2", enabled=1
+ )
for k in keys:
if (
AttestedCredentialData(
@@ -217,29 +253,24 @@ def authenticate_complete(request):
k.last_used = timezone.now()
k.save()
mfa = {"verified": True, "method": "FIDO2", "id": k.id}
- if getattr(settings, "MFA_RECHECK", False):
- mfa["next_check"] = datetime.datetime.timestamp(
- (
- datetime.datetime.now()
- + datetime.timedelta(
- seconds=random.randint(
- settings.MFA_RECHECK_MIN,
- settings.MFA_RECHECK_MAX,
- )
- )
- )
- )
+ mfa.update(set_next_recheck())
request.session["mfa"] = mfa
try:
authenticated = request.user.is_authenticated
except:
authenticated = request.user.is_authenticated()
if not authenticated:
- res = login(request,k.username)
- if not "location" in res: return reset_cookie(request)
- return HttpResponse(simplejson.dumps({'status': "OK", "redirect": res["location"]}),
- content_type = "application/json")
- return JsonResponse({'status': "OK"})
+ res = login(request, k.username)
+ if not "location" in res:
+ return reset_cookie(request)
+ return JsonResponse(
+ {"status": "OK", "redirect": res["location"]}
+ )
+
+ return JsonResponse({"status": "OK"})
except Exception as exp:
+ import traceback
+
+ print(traceback.format_exc())
return JsonResponse({"status": "ERR", "message": str(exp)}, status=500)
diff --git a/mfa/U2F.py b/mfa/U2F.py
index 2bed454..533cf75 100644
--- a/mfa/U2F.py
+++ b/mfa/U2F.py
@@ -1,3 +1,5 @@
+import json
+
from u2flib_server.u2f import (
begin_registration,
begin_authentication,
@@ -8,10 +10,10 @@
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives.serialization import Encoding
from django.shortcuts import render
-import simplejson
+
from django.template.context_processors import csrf
-from django.http import HttpResponse
+from django.http import HttpResponse, JsonResponse
from django.conf import settings
from .models import User_Keys
from .views import login
@@ -31,13 +33,11 @@ def recheck(request):
def process_recheck(request):
x = validate(request, request.user.username)
- if x == True:
+ if x is True:
import time
request.session["mfa"]["rechecked_at"] = time.time()
- return HttpResponse(
- simplejson.dumps({"recheck": True}), content_type="application/json"
- )
+ return JsonResponse({"recheck": True})
return x
@@ -55,7 +55,7 @@ def check_errors(request, data):
def validate(request, username):
import datetime, random
- data = simplejson.loads(request.POST["response"])
+ data = json.loads(request.POST["response"])
res = check_errors(request, data)
if res != True:
@@ -105,7 +105,7 @@ def start(request):
enroll = begin_registration(settings.U2F_APPID, [])
request.session["_u2f_enroll_"] = enroll.json
context = csrf(request)
- context["token"] = simplejson.dumps(enroll.data_for_client)
+ context["token"] = json.dumps(enroll.data_for_client)
context.update(get_redirect_url())
context["method"] = {
"name": getattr(settings, "MFA_RENAME_METHODS", {}).get(
@@ -122,7 +122,7 @@ def bind(request):
import hashlib
enroll = request.session["_u2f_enroll_"]
- data = simplejson.loads(request.POST["response"])
+ data = json.loads(request.POST["response"])
device, cert = complete_registration(enroll, data, [settings.U2F_APPID])
cert = x509.load_der_x509_certificate(cert, default_backend())
cert_hash = hashlib.md5(cert.public_bytes(Encoding.PEM)).hexdigest()
@@ -135,7 +135,7 @@ def bind(request):
uk = User_Keys()
uk.username = request.user.username
uk.owned_by_enterprise = getattr(settings, "MFA_OWNED_BY_ENTERPRISE", False)
- uk.properties = {"device": simplejson.loads(device.json), "cert": cert_hash}
+ uk.properties = {"device": json.loads(device.json), "cert": cert_hash}
uk.key_type = "U2F"
uk.save()
if (
@@ -160,7 +160,7 @@ def sign(username):
for d in User_Keys.objects.filter(username=username, key_type="U2F")
]
challenge = begin_authentication(settings.U2F_APPID, u2f_devices)
- return [challenge.json, simplejson.dumps(challenge.data_for_client)]
+ return [challenge.json, json.dumps(challenge.data_for_client)]
def verify(request):
diff --git a/mfa/helpers.py b/mfa/helpers.py
index 39e85b7..96aa60b 100644
--- a/mfa/helpers.py
+++ b/mfa/helpers.py
@@ -1,12 +1,17 @@
-import simplejson
-from django.shortcuts import HttpResponse
+from django.http import JsonResponse
+from django.conf import settings
+
from mfa.views import verify
from . import TrustedDevice, U2F, FIDO2, totp
from .models import User_Keys
def has_mfa(request, username):
- if User_Keys.objects.filter(username=username, enabled=1).count() > 0:
+ FORCE_EMAIL = getattr(settings, "MFA_ENFORCE_EMAIL_TOKEN", False)
+ if (
+ User_Keys.objects.filter(username=username, enabled=1).count() > 0
+ or FORCE_EMAIL
+ ):
return verify(request, username)
return False
@@ -21,26 +26,15 @@ def is_mfa(request, ignore_methods=[]):
def recheck(request):
method = request.session.get("mfa", {}).get("method", None)
if not method:
- return HttpResponse(
- simplejson.dumps({"res": False}), content_type="application/json"
- )
+ return JsonResponse({"res": False})
if method == "Trusted Device":
- return HttpResponse(
- simplejson.dumps({"res": TrustedDevice.verify(request)}),
- content_type="application/json",
- )
+ return JsonResponse({"res": TrustedDevice.verify(request)})
+
elif method == "U2F":
- return HttpResponse(
- simplejson.dumps({"html": U2F.recheck(request).content}),
- content_type="application/json",
- )
+ return JsonResponse({"html": U2F.recheck(request).content})
+
elif method == "FIDO2":
- return HttpResponse(
- simplejson.dumps({"html": FIDO2.recheck(request).content}),
- content_type="application/json",
- )
+ return JsonResponse({"html": FIDO2.recheck(request).content})
+
elif method == "TOTP":
- return HttpResponse(
- simplejson.dumps({"html": totp.recheck(request).content}),
- content_type="application/json",
- )
+ return JsonResponse({"html": totp.recheck(request).content})
diff --git a/mfa/recovery.py b/mfa/recovery.py
index f7e4870..77ec058 100644
--- a/mfa/recovery.py
+++ b/mfa/recovery.py
@@ -1,14 +1,14 @@
import time
import random
import string
-import simplejson
+
from django.shortcuts import render
from django.views.decorators.cache import never_cache
from django.template.context_processors import csrf
from django.utils import timezone
from django.contrib.auth.hashers import make_password, PBKDF2PasswordHasher
-from django.http import HttpResponse
+from django.http import HttpResponse, JsonResponse
from django.conf import settings
from .Common import get_redirect_url
from .models import User_Keys
@@ -58,7 +58,7 @@ def genTokens(request):
uk.key_type = "RECOVERY"
uk.enabled = True
uk.save()
- return HttpResponse(simplejson.dumps({"keys": clearKeys}))
+ return JsonResponse({"keys": clearKeys})
def verify_login(request, username, token):
@@ -81,7 +81,7 @@ def getTokenLeft(request):
keyLeft = 0
for key in uk:
keyLeft += len(key.properties["secret_keys"])
- return HttpResponse(simplejson.dumps({"left": keyLeft}))
+ return JsonResponse({"left": keyLeft})
def recheck(request):
@@ -92,13 +92,11 @@ def recheck(request):
0
]:
request.session["mfa"]["rechecked_at"] = time.time()
- return HttpResponse(
- simplejson.dumps({"recheck": True}), content_type="application/json"
- )
+ return JsonResponse({"recheck": True})
+
else:
- return HttpResponse(
- simplejson.dumps({"recheck": False}), content_type="application/json"
- )
+ return JsonResponse({"recheck": False})
+
return render(request, "RECOVERY/recheck.html", context)
@@ -123,10 +121,6 @@ def auth(request):
"id": resBackup[1],
"lastBackup": resBackup[2],
}
- # if getattr(settings, "MFA_RECHECK", False):
- # mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now()
- # + datetime.timedelta(
- # seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
request.session["mfa"] = mfa
if resBackup[2]:
# If the last bakup code has just been used, we return a response insead of redirecting to login
diff --git a/mfa/templates/Email/Auth.html b/mfa/templates/Email/Auth.html
index bb58497..305ffea 100644
--- a/mfa/templates/Email/Auth.html
+++ b/mfa/templates/Email/Auth.html
@@ -1,14 +1,8 @@
{% extends "mfa_auth_base.html" %}
{% block head %}
-
{% endblock %}
{% block content %}
-
-
+
{% include "Email/recheck.html" with mode='auth' %}
{% endblock %}
diff --git a/mfa/templates/Email/recheck.html b/mfa/templates/Email/recheck.html
index be342f1..dd7da12 100644
--- a/mfa/templates/Email/recheck.html
+++ b/mfa/templates/Email/recheck.html
@@ -1,29 +1,12 @@
-
-