Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
22b5d0884c | ||
|
|
9569b0cfcd | ||
|
|
1f3fca0f48 | ||
|
|
7c2958bebf | ||
|
|
d06f860611 | ||
|
|
ef3a5d4f40 | ||
|
|
9465042d7b |
@@ -7,9 +7,9 @@ Web Authencation API (WebAuthn) is state-of-the art techology that is expected t
|
|||||||
|
|
||||||

|

|
||||||
|
|
||||||
For FIDO2, **security keys**, **Apple's Touch ID (Chrome)** and **android-safetynet** are supported.
|
For FIDO2, **security keys**, **Windows Hello**, **Apple's Touch ID (Chrome)** and **android-safetynet** are supported.
|
||||||
|
|
||||||
In English :), It allows you to verify the user by security keys on PC, Laptops, Touch ID on Macboks (Chrome) and Fingerprint/PIN on Andriod Phones.
|
In English :), It allows you to verify the user by security keys on PC, Laptops, Windows Hello (Fingerprint, PIN) on Windows 10 Build 1903 (May 2019 Update) Touch ID on Macbooks (Chrome) and Fingerprint/PIN on Andriod Phones.
|
||||||
|
|
||||||
Trusted device is a mode for the user to add a device that doesn't support security keys like iOS and andriod without fingerprints or NFC.
|
Trusted device is a mode for the user to add a device that doesn't support security keys like iOS and andriod without fingerprints or NFC.
|
||||||
|
|
||||||
@@ -36,6 +36,8 @@ Depends on
|
|||||||
'mfa',
|
'mfa',
|
||||||
'......')
|
'......')
|
||||||
```
|
```
|
||||||
|
1. Collect Static Files
|
||||||
|
`python manage.py collectstatic`
|
||||||
1. Add the following settings to your file
|
1. Add the following settings to your file
|
||||||
|
|
||||||
```python
|
```python
|
||||||
@@ -45,6 +47,7 @@ Depends on
|
|||||||
MFA_RECHECK_MIN=10 # Minimum interval in seconds
|
MFA_RECHECK_MIN=10 # Minimum interval in seconds
|
||||||
MFA_RECHECK_MAX=30 # Maximum in seconds
|
MFA_RECHECK_MAX=30 # Maximum in seconds
|
||||||
MFA_QUICKLOGIN=True # Allow quick login for returning users by provide only their 2FA
|
MFA_QUICKLOGIN=True # Allow quick login for returning users by provide only their 2FA
|
||||||
|
MFA_HIDE_DISABLE=('FIDO2',) # Can the user disable his key (Added in 1.2.0).
|
||||||
|
|
||||||
TOKEN_ISSUER_NAME="PROJECT_NAME" #TOTP Issuer name
|
TOKEN_ISSUER_NAME="PROJECT_NAME" #TOTP Issuer name
|
||||||
|
|
||||||
|
|||||||
4
mfa/CHANGELOG.md
Normal file
4
mfa/CHANGELOG.md
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
# Change Log
|
||||||
|
|
||||||
|
## v1.2.0
|
||||||
|
* Added: MFA_HIDE_DISABLE setting option to disable users from deactivating their keys.
|
||||||
@@ -34,11 +34,11 @@ def begin_registeration(request):
|
|||||||
}, getUserCredentials(request.user.username))
|
}, getUserCredentials(request.user.username))
|
||||||
request.session['fido_state'] = state
|
request.session['fido_state'] = state
|
||||||
|
|
||||||
return HttpResponse(cbor.dumps(registration_data),content_type='application/octet-stream')
|
return HttpResponse(cbor.encode(registration_data),content_type='application/octet-stream')
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def complete_reg(request):
|
def complete_reg(request):
|
||||||
try:
|
try:
|
||||||
data = cbor.loads(request.body)[0]
|
data = cbor.decode(request.body)
|
||||||
|
|
||||||
client_data = ClientData(data['clientDataJSON'])
|
client_data = ClientData(data['clientDataJSON'])
|
||||||
att_obj = AttestationObject((data['attestationObject']))
|
att_obj = AttestationObject((data['attestationObject']))
|
||||||
@@ -79,7 +79,7 @@ def authenticate_begin(request):
|
|||||||
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)
|
auth_data, state = server.authenticate_begin(credentials)
|
||||||
request.session['fido_state'] = state
|
request.session['fido_state'] = state
|
||||||
return HttpResponse(cbor.dumps(auth_data),content_type="application/octet-stream")
|
return HttpResponse(cbor.encode(auth_data),content_type="application/octet-stream")
|
||||||
|
|
||||||
@csrf_exempt
|
@csrf_exempt
|
||||||
def authenticate_complete(request):
|
def authenticate_complete(request):
|
||||||
@@ -87,7 +87,7 @@ def authenticate_complete(request):
|
|||||||
username=request.session.get("base_username",request.user.username)
|
username=request.session.get("base_username",request.user.username)
|
||||||
server=getServer()
|
server=getServer()
|
||||||
credentials=getUserCredentials(username)
|
credentials=getUserCredentials(username)
|
||||||
data = cbor.loads(request.body)[0]
|
data = cbor.decode(request.body)
|
||||||
credential_id = data['credentialId']
|
credential_id = data['credentialId']
|
||||||
client_data = ClientData(data['clientDataJSON'])
|
client_data = ClientData(data['clientDataJSON'])
|
||||||
auth_data = AuthenticatorData(data['authenticatorData'])
|
auth_data = AuthenticatorData(data['authenticatorData'])
|
||||||
|
|||||||
@@ -20,6 +20,7 @@
|
|||||||
$("#modal-footer").prepend("<button id='actionBtn' class='btn btn-danger' onclick='confirmDel("+id+")'>Confirm Deletion</button>")
|
$("#modal-footer").prepend("<button id='actionBtn' class='btn btn-danger' onclick='confirmDel("+id+")'>Confirm Deletion</button>")
|
||||||
$("#popUpModal").modal()
|
$("#popUpModal").modal()
|
||||||
}
|
}
|
||||||
|
{% if not HIDE_DISABLE %}
|
||||||
function toggleKey(id) {
|
function toggleKey(id) {
|
||||||
$.ajax({
|
$.ajax({
|
||||||
url:"{% url 'toggle_key' %}?id="+id,
|
url:"{% url 'toggle_key' %}?id="+id,
|
||||||
@@ -33,6 +34,7 @@
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
{% endif %}
|
||||||
</script>
|
</script>
|
||||||
<link href="{% static 'mfa/css/bootstrap-toggle.min.css' %}" rel="stylesheet">
|
<link href="{% static 'mfa/css/bootstrap-toggle.min.css' %}" rel="stylesheet">
|
||||||
<script src="{% static 'mfa/js/bootstrap-toggle.min.js'%}"></script>
|
<script src="{% static 'mfa/js/bootstrap-toggle.min.js'%}"></script>
|
||||||
@@ -87,8 +89,16 @@
|
|||||||
<td>{{ key.expires }}</td>
|
<td>{{ key.expires }}</td>
|
||||||
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
<td>{% if key.device %}{{ key.device }}{% endif %}</td>
|
||||||
<td>{{ key.last_used }}</td>
|
<td>{{ key.last_used }}</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"></td>
|
{% if key.key_type in HIDE_DISABLE %}
|
||||||
<td><a href="javascript:void(0)" onclick="deleteKey({{ key.id }},'{{ key.key_type }}')"> <span class="fa fa-trash"></span></a></td>
|
<td>{% if key.enabled %}On{% else %} Off{% endif %}</td>
|
||||||
|
{% else %}
|
||||||
|
<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>
|
||||||
|
{% endif %}
|
||||||
|
<td>{% if key.key_type in HIDE_DISABLE %}
|
||||||
|
----
|
||||||
|
{% else %}
|
||||||
|
<a href="javascript:void(0)" onclick="deleteKey({{ key.id }},'{{ key.key_type }}')"> <span class="fa fa-trash"></span></a></td>
|
||||||
|
{% endif %}
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% empty %}
|
||||||
<tr><td colspan="7" align="center">You didn't have any keys yet.</td> </tr>
|
<tr><td colspan="7" align="center">You didn't have any keys yet.</td> </tr>
|
||||||
|
|||||||
@@ -62,7 +62,6 @@
|
|||||||
<fieldset>
|
<fieldset>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="col-sm-12 col-md-12">
|
<div class="col-sm-12 col-md-12">
|
||||||
{# <img class="profile-img" src="{{ STATIC_URL }}img/users.png" alt="">#}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="row">
|
<div class="row">
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
{% extends "base.html" %}
|
{% extends "base.html" %}
|
||||||
|
{% load static %}
|
||||||
{% block head %}
|
{% block head %}
|
||||||
<style>
|
<style>
|
||||||
#two-factor-steps {
|
#two-factor-steps {
|
||||||
@@ -10,7 +11,7 @@
|
|||||||
margin: 0px;
|
margin: 0px;
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
<script src="{{ STATIC_URL }}js/u2f-api.js" type="text/javascript"></script>
|
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function addToken() {
|
$(document).ready(function addToken() {
|
||||||
data=JSON.parse('{{ token|safe }}')
|
data=JSON.parse('{{ token|safe }}')
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
{% load static %}
|
||||||
<div class="row">
|
<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="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-8 col-lg-offset-2">
|
||||||
@@ -34,7 +35,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<script src="{{ STATIC_URL }}js/u2f-api.js" type="text/javascript"></script>
|
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
$(document).ready(function () {
|
$(document).ready(function () {
|
||||||
|
|||||||
@@ -12,7 +12,8 @@ from . import TrustedDevice
|
|||||||
from user_agents import parse
|
from user_agents import parse
|
||||||
def index(request):
|
def index(request):
|
||||||
keys=[]
|
keys=[]
|
||||||
context={"keys":User_Keys.objects.filter(username=request.user.username),"UNALLOWED_AUTHEN_METHODS":settings.MFA_UNALLOWED_METHODS}
|
context={"keys":User_Keys.objects.filter(username=request.user.username),"UNALLOWED_AUTHEN_METHODS":settings.MFA_UNALLOWED_METHODS
|
||||||
|
,"HIDE_DISABLE":getattr(settings,"MFA_HIDE_DISABLE",[])}
|
||||||
for k in context["keys"]:
|
for k in context["keys"]:
|
||||||
if k.key_type =="Trusted Device" :
|
if k.key_type =="Trusted Device" :
|
||||||
setattr(k,"device",parse(k.properties.get("user_agent","-----")))
|
setattr(k,"device",parse(k.properties.get("user_agent","-----")))
|
||||||
|
|||||||
4
setup.py
4
setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
|
|||||||
|
|
||||||
setup(
|
setup(
|
||||||
name='django-mfa2',
|
name='django-mfa2',
|
||||||
version='1.1.6',
|
version='1.3.0',
|
||||||
description='Allows user to add 2FA to their accounts',
|
description='Allows user to add 2FA to their accounts',
|
||||||
long_description=open("README.md").read(),
|
long_description=open("README.md").read(),
|
||||||
long_description_content_type="text/markdown",
|
long_description_content_type="text/markdown",
|
||||||
@@ -24,7 +24,7 @@ setup(
|
|||||||
'ua-parser',
|
'ua-parser',
|
||||||
'user-agents',
|
'user-agents',
|
||||||
'python-jose',
|
'python-jose',
|
||||||
'fido2 == 0.5',
|
'fido2 == 0.6',
|
||||||
'jsonLookup'
|
'jsonLookup'
|
||||||
],
|
],
|
||||||
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
|
python_requires=">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*",
|
||||||
|
|||||||
Reference in New Issue
Block a user