Recovery Codes Work in Progress

This commit is contained in:
Mohamed ElKalioby
2022-08-31 16:43:44 +03:00
parent 91e44a78c1
commit 598968bc92
6 changed files with 269 additions and 19 deletions

View File

@@ -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):

View File

@@ -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 = "<h6>Caution! you can only note these token now, else you will need to generate new ones.</h6><button onclick='regenerateTokens()' class='btn btn-success'>Regenerate</button>"
$("#modal-title").html("Are you sure you want to regenerate your recovery tokens?")
htmlModal = "<h6>Caution! you can only view these token now, else you will need to generate new ones.</h6><div align='center'><button onclick='regenerateTokens()' class='btn btn-success'>Regenerate</button></div>"
$("#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=`<p>Here are the recovery codes, you have to save them now as you won't able to view them again.</p>
<div class='row'><div class='offset-4 col-md-4' style='background-color:#f0f0f0;padding: 10px'>
<div class='row'>
<div class="col-6 offset-6">
<span onclick='window.location.href="{% url 'download_recovery' %}"' class='fa fa-download' title="Download"
style='cursor:pointer'></span>&nbsp;&nbsp;
<span class='fa fa-clipboard' title="Copy" onclick="copy()" style='cursor:pointer'></span>
</div></div><div id='recovery_codes'><pre>`;
for (let i = 0; i < data.keys.length; i++) {
htmlkey +="<pre>" +data.keys[i] + "</pre>"
htmlkey +="- " +data.keys[i] + "\n"
}
document.getElementById('tokens').innerHTML = htmlkey
document.getElementById('tokens').innerHTML = htmlkey+"</pre></div></div></div>"
$("#popUpModal").modal('hide')
}
})
@@ -69,21 +115,23 @@
<div class="row">
<h4>Token List</h4>
<h4>Recovery Codes List</h4>
</div>
<div class="tokenrow" id="tokens">
</div>
<br/>
<br/>
<div class="row">
<div class="col-md-6" style="padding-left: 0px">
<div class="col-md-4 col-md-offset-4" style="padding-left: 0px" align="center">
<button onclick="confirmRegenerateTokens()" class="btn btn-success">Regenarate tokens</button>
<button onclick="confirmRegenerateTokens()" class="btn btn-success">Regenerate</button>
</div>
<div class="col-md-6" align="right" style="padding-right: 30px">
<a href="{{redirect_html}}" class="btn btn-default btn-secondary" role="button"> {{reg_success_msg}}</a>

View File

@@ -0,0 +1,94 @@
<script type="application/javascript">
$(document).ready(function showWarningLastBackup() {
{% if lastBackup %}
$("#modal-title").html("Last backup code used !")
$("#modal-body").html("Don't forget to regenerate new backup code after login !")
$('#modal-footer').html(`<FORM METHOD="GET" ACTION="{% url 'recovery_auth' %}" Id="confirmLogin" onSubmit="" name="recoveryLastBackupConfirm">
<input type='submit'class='btn btn-lg btn-success btn-block' value='Continue'>`)
$("#popUpModal").modal('show')
{% endif %}
return
});
function send_totp() {
$.ajax({"url":"{% url 'totp_recheck' %}", method:"POST",dataType:"JSON",
data:{"csrfmiddlewaretoken":"{{ csrf_token }}","otp":$("#otp").val()},
success:function (data) {
if (data["recheck"])
mfa_success_function();
else {
mfa_failed_function();
}
}
})
}
function tryToAuth() {
const otp_length = $("#otp").val().length
if (otp_length == 6) {
document.getElementById("formLogin").submit();
}
else if (otp_length == 11) {
const form = document.getElementById("formLogin");
form.setAttribute("ACTION", "{% url 'recovery_auth' %}")
form.submit();
}
}
</script>
<div class='container'>
<div class="row">
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
<div class="panel panel-default card">
<div class="panel-heading card-header">
<strong>Recovery Code</strong>
</div>
<div class="panel-body card-body">
<FORM METHOD="POST" ACTION="{% url 'recovery_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
{% csrf_token %}
{% if invalid %}
<div class="alert alert-danger">
Sorry, The provided token is not valid.
</div>
{% endif %}
<fieldset>
<div class="row">
<div class="col-sm-12 col-md-12">
<p>Enter enter your next recovery code</p>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<div class="input-group mb-3">
<span class="input-group-addon input-group-text">
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
</span>
<input class="form-control" size="11" value="" placeholder="e.g npXiX-7dZgK" name="otp" type="text" id="otp" autofocus>
</div>
</div>
<div class="form-group d-grid gap-2">
<input type="button" onclick="{% if mode == "recheck" %} send_totp() {% else %} tryToAuth() {% endif %}" class="btn btn-lg btn-success btn-block" value="Sign in">
</div>
</div>
</fieldset>
</FORM>
</div>
<div class="row">
<div class="col-md-12 mb-3" style="padding-left: 25px">
{% if request.session.mfa_methods|length > 1 %}
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include "modal.html" %}

View File

@@ -0,0 +1,99 @@
<script type="application/javascript">
$(document).ready(function showWarningLastBackup() {
{% if lastBackup %}
$("#modal-title").html("Last backup code used !")
$("#modal-body").html("Don't forget to regenerate new backup code after login !")
$('#modal-footer').html(`<FORM METHOD="GET" ACTION="{% url 'recovery_auth' %}" Id="confirmLogin" onSubmit="" name="recoveryLastBackupConfirm">
<input type='submit'class='btn btn-lg btn-success btn-block' value='Continue'>`)
$("#popUpModal").modal('show')
{% endif %}
return
});
function send_totp() {
$.ajax({"url":"{% url 'totp_recheck' %}", method:"POST",dataType:"JSON",
data:{"csrfmiddlewaretoken":"{{ csrf_token }}","otp":$("#otp").val()},
success:function (data) {
if (data["recheck"])
mfa_success_function();
else {
mfa_failed_function();
}
}
})
}
function tryToAuth() {
const otp_length = $("#otp").val().length
if (otp_length == 6) {
document.getElementById("formLogin").submit();
}
else if (otp_length == 11) {
const form = document.getElementById("formLogin");
form.setAttribute("ACTION", "{% url 'recovery_auth' %}")
form.submit();
}
}
</script>
<div class='container'>
<div class="row">
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
<div class="panel panel-default card">
<div class="panel-heading card-header">
<strong> One Time Password</strong>
</div>
<div class="panel-body card-body">
<FORM METHOD="POST" ACTION="{% url 'totp_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
{% csrf_token %}
{% if invalid %}
<div class="alert alert-danger">
Sorry, The provided token is not valid.
</div>
{% endif %}
{% if quota %}
<div class="alert alert-warning">
{{ quota }}
</div>
{% endif %}
<fieldset>
<div class="row">
<div class="col-sm-12 col-md-12">
<p>Enter the 6-digits on your authenticator. Or input a recovery code</p>
</div>
</div>
<div class="row">
<div class="col-sm-12 col-md-12">
<div class="form-group">
<div class="input-group mb-3">
<span class="input-group-addon input-group-text">
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
</span>
<input class="form-control" size="6" MaxLength="11" value="" placeholder="e.g 55552" name="otp" type="text" id="otp" autofocus>
</div>
</div>
<div class="form-group d-grid gap-2">
<input type="button" onclick="{% if mode == "recheck" %} send_totp() {% else %} tryToAuth() {% endif %}" class="btn btn-lg btn-success btn-block" value="Sign in">
</div>
</div>
</fieldset>
</FORM>
</div>
<div class="row">
<div class="col-md-12 mb-3" style="padding-left: 25px">
{% if request.session.mfa_methods|length > 1 %}
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
{% endif %}
</div>
</div>
</div>
</div>
</div>
</div>
</div>
{% include "modal.html" %}

View File

@@ -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 %}
</a> </li>
{% endfor %}

View File

@@ -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"),