diff --git a/mfa/recovery.py b/mfa/recovery.py index 902603c..8534de1 100644 --- a/mfa/recovery.py +++ b/mfa/recovery.py @@ -2,7 +2,7 @@ from django.shortcuts import render from django.views.decorators.cache import never_cache from django.template.context_processors import csrf from django.contrib.auth.hashers import make_password, PBKDF2PasswordHasher -from django.http import HttpResponse +from django.http import HttpResponse,FileResponse,HttpResponseNotFound from .Common import get_redirect_url from .models import * import simplejson @@ -57,19 +57,26 @@ def genTokens(request): uk.key_type="RECOVERY" uk.enabled = False uk.save() + request.session["recovery_keys"]=clearKeys return HttpResponse(simplejson.dumps({"keys":clearKeys})) +def download_codes(request): + if not "recovery_keys" in request.session: + return HttpResponseNotFound("This page isn't valid anymore.") + response = HttpResponse('\n'.join(request.session["recovery_keys"]),content_type='text/text') + response['Content-Disposition'] = 'attachment; filename = Recovery Codes.txt' + return response def verify_login(request, username,token): - for key in User_Keys.objects.filter(username=username, key_type = "RECOVERY"): - secret_keys = key.properties["secret_keys"] - salt = key.properties["salt"] - hashedToken = make_password(token, salt, "pbkdf2_sha256_custom") - for i in range(len(secret_keys)): - if hashedToken == secret_keys[i] and key.properties["enabled"][i]: - key.properties["enabled"][i] = False - key.save() - return [True, key.id, token_left(None, username) == 0] + key = User_Keys.objects.filter(username=username, key_type = "RECOVERY") + secret_keys = key.properties["secret_keys"] + salt = key.properties["salt"] + hashedToken = make_password(token, salt, "pbkdf2_sha256_custom") + if hashedToken == secret_keys[0]: + secret_keys.pop(0) + key.properties["secret_keys"] = secret_keys + key.save() + return [True, key.id, len(secret_keys) == 0] return [False] def getTokenLeft(request): diff --git a/mfa/templates/RECOVERY/Add.html b/mfa/templates/RECOVERY/Add.html index 1b871e5..9f240f6 100644 --- a/mfa/templates/RECOVERY/Add.html +++ b/mfa/templates/RECOVERY/Add.html @@ -18,7 +18,43 @@ .crossed{ text-decoration: line-through; } + .tooltip { + position: relative; + display: inline-block; +} +.tooltip .tooltiptext { + visibility: hidden; + width: 140px; + background-color: #555; + color: #fff; + text-align: center; + border-radius: 6px; + padding: 5px; + position: absolute; + z-index: 1; + bottom: 150%; + left: 50%; + margin-left: -75px; + opacity: 0; + transition: opacity 0.3s; +} + +.tooltip .tooltiptext::after { + content: ""; + position: absolute; + top: 100%; + left: 50%; + margin-left: -5px; + border-width: 5px; + border-style: solid; + border-color: #555 transparent transparent transparent; +} + +.tooltip:hover .tooltiptext { + visibility: visible; + opacity: 1; +} .return{ margin: 1px; } @@ -41,20 +77,30 @@ }}) }); function confirmRegenerateTokens() { - htmlModal = "
Caution! you can only note these token now, else you will need to generate new ones.
" - $("#modal-title").html("Are you sure you want to regenerate your recovery tokens?") + htmlModal = "
Caution! you can only view these token now, else you will need to generate new ones.
" + $("#modal-title").html("Regenerate your recovery Codes?") $("#modal-body").html(htmlModal) $("#popUpModal").modal('show') } + function copy() { + navigator.clipboard.writeText($("#recovery_codes").text()); + } function regenerateTokens() { $.ajax({ "url":"{% url 'regen_recovery_tokens' %}", dataType:"JSON", success:function (data) { - let htmlkey=""; + let htmlkey=`

Here are the recovery codes, you have to save them now as you won't able to view them again.

+
+
+
+    + +
`;
                 for (let i = 0; i < data.keys.length; i++) {
-                        htmlkey +="
" +data.keys[i] + "
" + htmlkey +="- " +data.keys[i] + "\n" } - document.getElementById('tokens').innerHTML = htmlkey + document.getElementById('tokens').innerHTML = htmlkey+"
" $("#popUpModal").modal('hide') } }) @@ -69,21 +115,23 @@
-

Token List

+

Recovery Codes List

- +
+
-
+
- +
+
{{reg_success_msg}} diff --git a/mfa/templates/RECOVERY/Auth.html b/mfa/templates/RECOVERY/Auth.html new file mode 100644 index 0000000..77629dd --- /dev/null +++ b/mfa/templates/RECOVERY/Auth.html @@ -0,0 +1,94 @@ + +
+
+ +
+
+
+ Recovery Code +
+
+ +
+ + + {% csrf_token %} + {% if invalid %} +
+ Sorry, The provided token is not valid. +
+ {% endif %} +
+
+
+

Enter enter your next recovery code

+
+
+ +
+
+
+
+ + + + + +
+
+ +
+ + +
+
+
+
+
+
+
+ {% if request.session.mfa_methods|length > 1 %} + Select Another Method + {% endif %} +
+
+
+
+
+
+
+{% include "modal.html" %} \ No newline at end of file diff --git a/mfa/templates/RECOVERY/recheck.html b/mfa/templates/RECOVERY/recheck.html new file mode 100644 index 0000000..7261939 --- /dev/null +++ b/mfa/templates/RECOVERY/recheck.html @@ -0,0 +1,99 @@ + +
+
+ +
+
+
+ One Time Password +
+
+ +
+ + + {% csrf_token %} + {% if invalid %} +
+ Sorry, The provided token is not valid. +
+ {% endif %} + {% if quota %} +
+ {{ quota }} +
+ {% endif %} +
+
+
+

Enter the 6-digits on your authenticator. Or input a recovery code

+
+
+ +
+
+
+
+ + + + + +
+
+ +
+ + +
+
+
+
+
+
+
+ {% if request.session.mfa_methods|length > 1 %} + Select Another Method + {% endif %} +
+
+
+
+
+
+
+{% include "modal.html" %} \ No newline at end of file diff --git a/mfa/templates/select_mfa_method.html b/mfa/templates/select_mfa_method.html index 07a8815..d8cafe3 100644 --- a/mfa/templates/select_mfa_method.html +++ b/mfa/templates/select_mfa_method.html @@ -19,6 +19,7 @@ {% elif method == "Email" %}Send OTP by Email {% elif method == "U2F" %}Secure Key {% elif method == "FIDO2" %}FIDO2 Secure Key + {% elif method == "RECOVERY" %}Recovery Code {% endif %} {% endfor %} diff --git a/mfa/urls.py b/mfa/urls.py index 4f5b2db..beb13fd 100644 --- a/mfa/urls.py +++ b/mfa/urls.py @@ -16,6 +16,7 @@ urlpatterns = [ url(r'recovery/getTokenLeft', recovery.getTokenLeft, name="get_recovery_token_left"), url(r'recovery/genTokens', recovery.genTokens, name="regen_recovery_tokens"), url(r'recovery/auth', recovery.auth, name="recovery_auth"), + url(r'recovery/download_codes', recovery.download_codes, name="download_recovery"), url(r'email/start/', Email.start , name="start_email"), url(r'email/auth/', Email.auth , name="email_auth"),