Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion example/example/templates/logout.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
<link href="{% static 'vendor/fontawesome-free/css/all.min.css'%}" rel="stylesheet" type="text/css">

<!-- Custom styles for this template-->
<link href="{% static 'css/sb-admin.css'%}" rel="stylesheet">
<link href="{% static 'css/sb-admin.min.css'%}" rel="stylesheet">

</head>

Expand Down
126 changes: 126 additions & 0 deletions mfa/static/mfa/css/mfa.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,126 @@
.text-center {
text-align: center;
}

.text-right {
text-align: right;
}

.padding-10 {
padding: 10px;
}

.padding-left-0 {
padding-left: 0;
}

.padding-left-15 {
padding-left: 15px;
}

.padding-left-25 {
padding-left: 25px;
}

.padding-top-10 {
padding-top: 10px;
}

.padding-right-30 {
padding-right: 30px;
}

#popUpModal {
top: 40px;

& .modal-dialog {
height: 80%;
width: 80%;
}
}

.img-circle {
padding: 3px;
height: 50px;
}

.success-message {
color: green;
}
.error-message {
color: red;
}

.font-10 {
font-size: 10px;
}

.color-gray {
color: #333333;
}

.bg-white {
background-color: #f0f0f0;
}

.institution-code {
font-size: 10pt;
font-family: Calibri;
height: 34px;width: 230px
}

.td-digits {
font-size: 16px;
font-weight: bold;
margin-left: 50px;
}

#two-factor-steps {
border: 1px solid #ccc;
border-radius: 3px;
padding: 15px;

& .row {
margin: 0;
align-items: center;
}

& .toolbtn {
border-radius: 7px;
cursor: pointer;

& :hover {
background-color: gray;
transition: 0.2s;
}

& :active {
background-color: green;
transition: 0.2s;
}
}

& #answer {
display: inline;
width: 95%
}

& #second_step {
display: none;
text-align: center;
align-content: center
}

& .tokenrow {
margin-top: 10px;
margin-left: 5px;
}
}

.row.margin-3 {
margin: 3px;
}

.row.margin-left-15, .auth-card .row {
margin-left: 15px;
}
6 changes: 6 additions & 0 deletions mfa/static/mfa/js/Email/recheck.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
$(document).ready(function () {
const mode = JSON.parse(document.getElementById('mode').textContent);
if (mode == "recheck") {
$("#send_totp").click(function () {send_totp()});
}
})
64 changes: 64 additions & 0 deletions mfa/static/mfa/js/FIDO2/add.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
var MakeCredReq = (makeCredReq) => {
makeCredReq.publicKey.challenge = base64url.decode(makeCredReq.publicKey.challenge);
makeCredReq.publicKey.user.id = base64url.decode(makeCredReq.publicKey.user.id);

for (let excludeCred of makeCredReq.publicKey.excludeCredentials) {
excludeCred.id = base64url.decode(excludeCred.id);
}

return makeCredReq
}

function begin_reg() {
const fido2_begin_reg = JSON.parse(document.getElementById('fido2_begin_reg').textContent);
const fido2_complete_reg = JSON.parse(document.getElementById('fido2_complete_reg').textContent);
const redirect_html = JSON.parse(document.getElementById('redirect_html').textContent);
const reg_success_msg = JSON.parse(document.getElementById('reg_success_msg').textContent);
const manage_recovery_codes = JSON.parse(document.getElementById('manage_recovery_codes').textContent);
const RECOVERY_METHOD = JSON.parse(document.getElementById('RECOVERY_METHOD').textContent);
const mfa_home = JSON.parse(document.getElementById('mfa_home').textContent);
fetch(fido2_begin_reg, {}).then(function (response) {
if (response.ok) {
return response.json().then(function (req) {
return MakeCredReq(req)
});
}
throw new Error('Error getting registration data!');
}).then(function (options) {
//options.publicKey.attestation="direct"
console.log(options)
return navigator.credentials.create(options);
}).then(function (attestation) {
return fetch(fido2_complete_reg, {
method: 'POST',
body: JSON.stringify(publicKeyCredentialToJSON(attestation))
});
}).then(function (response) {
var stat = response.ok ? 'successful' : 'unsuccessful';
return response.json()
}).then(function (res) {
if (res["status"] == 'OK')
$("#res").html("<div class='alert alert-success'>Registered Successfully, <a href='"+redirect_html+"'>"+reg_success_msg+"</a></div>")
else if (res['status'] = "RECOVERY") {
setTimeout(function () {
location.href = manage_recovery_codes
}, 2500)
$("#res").html("<div class='alert alert-success'>Registered Successfully, but <a href='"+manage_recovery_codes+"'>redirecting to "+RECOVERY_METHOD+" method</a></div>")
} else
$("#res").html("<div class='alert alert-danger'>Registration Failed as " + res["message"] + ", <a href='#' id='failed_begin_reg'> try again or <a href='"+mfa_home+"'> Go to Security Home</a></div>")
$("#failed_begin_reg").click(function() {begin_reg()});
}, function (reason) {
$("#res").html("<div class='alert alert-danger'>Registration Failed as " + reason + ", <a href='#' id='failed_begin_reg'> try again </a> or <a href='"+mfa_home+"'> Go to Security Home</a></div>")
$("#failed_begin_reg").click(function() {begin_reg()});
})
}

$(document).ready(function () {
ua = new UAParser().getResult()
if (ua.browser.name == "Safari" || ua.browser.name == "Mobile Safari") {
$("#res").html("<button class='btn btn-success' id='ua_begin_reg'>Start...</button>")
$("#ua_begin_reg").click(function() { begin_reg(); });
} else {
setTimeout(begin_reg, 500)
}
})
96 changes: 96 additions & 0 deletions mfa/static/mfa/js/FIDO2/auth_js.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
window.conditionalUI = false;
window.conditionUIAbortController = new AbortController();
window.conditionUIAbortSignal = conditionUIAbortController.signal;

function checkConditionalUI(form) {
if (window.PublicKeyCredential && PublicKeyCredential.isConditionalMediationAvailable) {
// Check if conditional mediation is available.
PublicKeyCredential.isConditionalMediationAvailable().then((result) => {
window.conditionalUI = result;
if (window.conditionalUI) {
authen(true)
}
});
}
}

var GetAssertReq = (getAssert) => {
getAssert.publicKey.challenge = base64url.decode(getAssert.publicKey.challenge);
for (let allowCred of getAssert.publicKey.allowCredentials) {
allowCred.id = base64url.decode(allowCred.id);
}
return getAssert
}

function authen(conditionalUI = false) {
const fido2_begin_auth = JSON.parse(document.getElementById('fido2_begin_auth').textContent);
const fido2_complete_auth = JSON.parse(document.getElementById('fido2_complete_auth').textContent);
const mode = JSON.parse(document.getElementById('mode').textContent);
fetch(fido2_begin_auth, {
method: 'GET',
}).then(function (response) {
if (response.ok) {
return response.json().then(function (req) {
return GetAssertReq(req)
});
}
throw new Error('No credential available to authenticate!');
}).then(function (options) {
if (conditionalUI) {
options.mediation = 'conditional';
options.signal = window.conditionUIAbortSignal;
} else {
window.conditionUIAbortController.abort()
}
console.log(options)
return navigator.credentials.get(options);
}).then(function (assertion) {
return fetch(fido2_complete_auth, {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify(publicKeyCredentialToJSON(assertion)),
}
).then(function (response) {
if (response.ok) return res = response.json()
}).then(function (res) {
if (res.status == "OK") {
$("#msgdiv").addClass("alert alert-success").removeClass("alert-danger")
$("#msgdiv").html("Verified....please wait")
if (mode === "auth" || !mode) {
window.location.href = res.redirect;
}
else if (mode === "recheck") {
mfa_success_function();
}
} else {
$("#msgdiv").addClass("alert alert-danger").removeClass("alert-success")
$("#msgdiv").html("Verification Failed as " + res.message + ", <a href='#' id='failed_authen'> try again</a> or <a href='#' id='failed_history_back'> Go Back</a>")
$("#failed_history_back").click(function() { history.back() });
$("#failed_authen").click(function() { authen(); });

if (mode === "auth") {
// do nothing
}
else if (mode === "recheck") {
mfa_failed_function();
}
}
})
})
}

$(document).ready(function () {
if (location.protocol != 'https:') {
$("#main_paragraph").addClass("alert alert-danger")
$("#main_paragraph").html("FIDO2 must work under secure context")
} else {
ua = new UAParser().getResult()
if (ua.browser.name == "Safari" || ua.browser.name == "Mobile Safari" || ua.os.name == "iOS" || ua.os.name == "iPadOS") {
$("#res").html("<button class='btn btn-success' id='ua_authen'>Authenticate...</button>");
$("#ua_authen").click(function () { authen(); });
}
else {
authen()
}
}
});
85 changes: 85 additions & 0 deletions mfa/static/mfa/js/RECOVERY/add.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
var clearCodes;
$(document).ready(function() {
$("#confirmRegenerateTokens").click(function () { confirmRegenerateTokens() });

checkTokenLeft();
});

function checkTokenLeft() {
const get_recovery_token_left = JSON.parse(document.getElementById('get_recovery_token_left').textContent);
const mfa_redirect = JSON.parse(document.getElementById('mfa_redirect').textContent);
$.ajax({
"url": get_recovery_token_left, dataType: "JSON",
success: function (data) {
tokenLeft = data.left
html = ""
if (!!mfa_redirect) {
html += "<div class='alert alert-success'>You have enrolled successfully in "+mfa_redirect+" method, please generate recovery codes so that you can use in case you lost access to all your verification methods.</div>"
}
if (tokenLeft == 0) {
html += "<h6>You don't have any backup code linked to your account, please generate new ones !</h6>"
} else {
html += "<p>You still have " + tokenLeft + " backup code left."
}
document.getElementById('tokens').innerHTML = html
}
})
}

function confirmRegenerateTokens() {
htmlModal = "<h6>Caution! you can only view these token now, else you will need to generate new ones.</h6><div class='text-center'><button id='regenerateTokens' class='btn btn-success'>Regenerate</button></div>"
$("#modal-title").html("Regenerate your recovery Codes?")
$("#modal-body").html(htmlModal)
$("#regenerateTokens").click(function () { regenerateTokens(); })
$("#popUpModal").modal('show')
}

function copy() {
navigator.clipboard.writeText($("#recovery_codes").text());
}

function regenerateTokens() {
const regen_recovery_tokens = JSON.parse(document.getElementById('regen_recovery_tokens').textContent);
$.ajax({
"url": regen_recovery_tokens, dataType: "JSON",
success: function (data) {
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 bg-white padding-10'>
<div class='row'>
<div class="col-6 offset-6">
<span id='download_recovery' class='fa fa-download toolbtn' title="Download"></span>&nbsp;&nbsp;
<span class='fa fa-clipboard toolbtn' id='copy_clipboard' title="Copy"></span>
</div></div><div id='recovery_codes'><pre>`;
for (let i = 0; i < data.keys.length; i++) {
htmlkey += "- " + data.keys[i] + "\n"
}
document.getElementById('tokens').innerHTML = htmlkey + "</pre></div></div></div>"
$("#download_recovery").click(function () {
download_recovery();
})
$("#copy_clipboard").click(function () {
copy();
})
$("#popUpModal").modal('hide')
clearCodes = data.keys
}
})
}

function download_recovery() {
var element = document.createElement('a');
var text = "";
for (let i = 0; i < clearCodes.length; i++) {
text = text + clearCodes[i]
if (i < clearCodes.length - 1) {
text = text + "\n"
}
}
element.setAttribute('href', 'data:text/plain;charset=utf-8,' + encodeURIComponent(text));
element.setAttribute('download', 'Recovery Codes.txt');
element.hidden = true;
document.body.appendChild(element);
element.click();
console.log(element.innerHTML)
document.body.removeChild(element);
}
Loading