|
@@ -0,0 +1,159 @@
|
|
|
|
|
+{% extends "passkeys/base.html" %}
|
|
|
|
|
+{% load static %}
|
|
|
|
|
+{% block head %}
|
|
|
|
|
+{{block.super}}
|
|
|
|
|
+ <script type="application/javascript" src="{% static 'passkeys/js/base64url.js'%}"></script>
|
|
|
|
|
+ <script type="application/javascript" src="{% static 'passkeys/js/helpers.js'%}"></script>
|
|
|
|
|
+ <script type="application/javascript">
|
|
|
|
|
+ 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(){
|
|
|
|
|
+ fetch('{% url 'passkeys:reg_begin' %}',{}).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) {
|
|
|
|
|
+ if (!navigator.credentials) {
|
|
|
|
|
+ throw new Error('WebAuthn is not available. Ensure the page is served over HTTPS (secure context).');
|
|
|
|
|
+ }
|
|
|
|
|
+ return navigator.credentials.create(options);
|
|
|
|
|
+ }).then(function(attestation) {
|
|
|
|
|
+ attestation["key_name"] = $("#key_name").val();
|
|
|
|
|
+ return fetch('{% url 'passkeys:reg_complete' %}', {
|
|
|
|
|
+ headers:{'X-CSRFToken': "{{csrf_token}}"},
|
|
|
|
|
+ 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='{% url "passkeys:home"%}'> Refresh</a></div>")
|
|
|
|
|
+ else
|
|
|
|
|
+ $("#res").html("<div class='alert alert-danger'>Registration Failed as " + res["message"] + ", <a href='javascript:void(0)' onclick='begin_reg()'> try again </a> </div>")
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ }, function(reason) {
|
|
|
|
|
+ $("#res").html("<div class='alert alert-danger'>Registration Failed as " +reason +", <a href='javascript:void(0)' onclick='begin_reg()'> try again </a> </div>")
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+
|
|
|
|
|
+ function confirmDel(id) {
|
|
|
|
|
+ $.ajax({
|
|
|
|
|
+ url:"{% url 'passkeys:delKey' %}",
|
|
|
|
|
+ method:"POST",
|
|
|
|
|
+ data:{"id":id,"csrfmiddlewaretoken":"{{ csrf_token }}"},
|
|
|
|
|
+ success:function (data) {
|
|
|
|
|
+ alert(data)
|
|
|
|
|
+ window.location= "{%url 'passkeys:home'%}";
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function start(){
|
|
|
|
|
+ if (!navigator.credentials) {
|
|
|
|
|
+ alert("WebAuthn is not available. This feature requires a secure context (HTTPS).");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ $("#modal-title").html("Enter a token name")
|
|
|
|
|
+ $("#modal-body").html(`<p>Please enter a name for your new token</p>
|
|
|
|
|
+ <input type="text" placeholder="e.g Laptop, PC" id="key_name" class="form-control"/><br/>
|
|
|
|
|
+ <div id="res"></div>
|
|
|
|
|
+ `)
|
|
|
|
|
+ $("#actionBtn").remove();
|
|
|
|
|
+ $("#modal-footer").prepend(`<button id='actionBtn' class='btn btn-success' onclick="begin_reg()">Start</button>`)
|
|
|
|
|
+ $("#popUpModal").modal('show')
|
|
|
|
|
+ }
|
|
|
|
|
+ function deleteKey(id,name)
|
|
|
|
|
+ {
|
|
|
|
|
+ $("#modal-title").html("Confirm Delete")
|
|
|
|
|
+ $("#modal-body").html(`Are you sure you want to delete '${name}'? you may lose access to your system if this your only 2FA.`);
|
|
|
|
|
+ $("#actionBtn").remove()
|
|
|
|
|
+ $("#modal-footer").prepend("<button id='actionBtn' class='btn btn-danger' onclick='confirmDel("+id+")'>Confirm Deletion</button>")
|
|
|
|
|
+ $("#popUpModal").modal('show')
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ function toggleKey(id) {
|
|
|
|
|
+ $.ajax({
|
|
|
|
|
+ url:"{% url 'passkeys:toggle' %}",
|
|
|
|
|
+ data:{"id":id,"csrfmiddlewaretoken":"{{ csrf_token }}"},
|
|
|
|
|
+ method:"POST",
|
|
|
|
|
+ success:function (data) {
|
|
|
|
|
+ if (data == "Error")
|
|
|
|
|
+ $("#toggle_"+id).toggle()
|
|
|
|
|
+
|
|
|
|
|
+ },
|
|
|
|
|
+ error:function (data) {
|
|
|
|
|
+ $("#toggle_"+id).toggle()
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ {%if enroll%}
|
|
|
|
|
+ $(document).ready(start)
|
|
|
|
|
+ {%endif%}
|
|
|
|
|
+
|
|
|
|
|
+ </script>
|
|
|
|
|
+ <link href="{% static 'passkeys/css/bootstrap-toggle.min.css' %}" rel="stylesheet">
|
|
|
|
|
+ <script src="{% static 'passkeys/js/bootstrap-toggle.min.js'%}"></script>
|
|
|
|
|
+{% endblock %}
|
|
|
|
|
+{% block content %}
|
|
|
|
|
+{{block.super}}
|
|
|
|
|
+ <br/>
|
|
|
|
|
+ <br/>
|
|
|
|
|
+ <div class="container">
|
|
|
|
|
+ <div class="row">
|
|
|
|
|
+ <div class="offset-5 col-2" style="text-align: center">
|
|
|
|
|
+ <div class="btn-group">
|
|
|
|
|
+ <button class="btn btn-success" onclick='start()'> Add Key</button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <br/>
|
|
|
|
|
+ <table class="table table-striped">
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <th>Name</th>
|
|
|
|
|
+ <th>Date Added</th>
|
|
|
|
|
+ <th>Platform</th>
|
|
|
|
|
+ <th>Last Used</th>
|
|
|
|
|
+ <th>Status</th>
|
|
|
|
|
+ <th>Delete</th>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% if keys %}
|
|
|
|
|
+ {% for key in keys %}
|
|
|
|
|
+ <tr>
|
|
|
|
|
+
|
|
|
|
|
+ <td>{{ key.name }}</td>
|
|
|
|
|
+ <td>{{ key.added_on }}</td>
|
|
|
|
|
+ <td>{{ key.platform }}</td>
|
|
|
|
|
+ <td>{% if key.last_used %}{{ key.last_used }}{% else %}Never{% endif %}</td>
|
|
|
|
|
+ <td><input type="checkbox" id="toggle_{{ key.id }}" {% if key.enabled %}checked{% endif %} data-onstyle="success" data-offstyle="danger" onchange="toggleKey({{ key.id }})" data-toggle="toggle" class="status_chk"></td>
|
|
|
|
|
+ <td><a href="javascript:void(0)" onclick="deleteKey({{ key.id }},'{{ key.name }}')"> <span class="fa fa-trash fa-solid fa-trash-can bi bi-trash-fill"></span></a></td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% endfor %}
|
|
|
|
|
+ {% else %}
|
|
|
|
|
+ <tr>
|
|
|
|
|
+ <td colspan="7" align="center">You didn't have any keys yet.</td>
|
|
|
|
|
+ </tr>
|
|
|
|
|
+ {% endif %}
|
|
|
|
|
+ </table>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ {% include "passkeys/modal.html" %}
|
|
|
|
|
+{% endblock %}
|