Compare commits

...

9 Commits

Author SHA1 Message Date
Mohamed ElKalioby
19563fe2b8 Fixes #37 2021-02-26 13:55:49 +03:00
Mohamed ElKalioby
0e7c0911ca Merge branch 'master' of https://github.com/mkalioby/django-mfa2 2021-02-26 13:26:31 +03:00
Mohamed ElKalioby
ebfdaf7504 Merge branch 'TouchID' 2021-02-26 13:25:56 +03:00
Mohamed El-Kalioby
c78e600b37 Update CHANGELOG.md 2021-01-21 09:17:28 +03:00
Mohamed El-Kalioby
e1e931285f Update requirements.txt
Fix FIDO2 version in the requirements file
2021-01-21 09:13:52 +03:00
Mohamed El-Kalioby
2709e70f88 Removed Touch ID Beta statment 2021-01-20 17:11:08 +03:00
Mohamed El-Kalioby
25fe80d76d Merge branch 'master' of github.com:mkalioby/django-mfa2 2021-01-20 17:10:08 +03:00
Mohamed El-Kalioby
75cf1e6130 Update README.md 2021-01-18 19:57:54 +03:00
Mohamed El-Kalioby
911be9e106 Update README.md 2021-01-18 19:56:19 +03:00
8 changed files with 67 additions and 41 deletions

View File

@@ -1,5 +1,13 @@
# Change Log
## 2.1.2
* Fixed: Getting timestamp on Python 3.7 as ("%s") is raising an exception
* Upgraded to FIDO 0.9.1
## 2.1.1
* Fixed: FIDO2 version in requirments.txt file.
## 2.1.0
* Added Support for Touch ID for Mac OSx and iOS 14 on Safari

View File

@@ -46,8 +46,8 @@ def auth(request):
uk = User_Keys.objects.get(username=request.session["base_username"], key_type="Email")
mfa = {"verified": True, "method": "Email","id":uk.id}
if getattr(settings, "MFA_RECHECK", False):
mfa["next_check"] = int((datetime.datetime.now() + datetime.timedelta(
seconds = random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s"))
mfa["next_check"] = datetime.datetime.timestamp(datetime.datetime.now() + datetime.timedelta(
seconds = random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX)))
request.session["mfa"] = mfa
from django.utils import timezone

View File

@@ -4,28 +4,31 @@ from fido2.ctap2 import AttestationObject, AuthenticatorData
from django.template.context_processors import csrf
from django.views.decorators.csrf import csrf_exempt
from django.shortcuts import render
#from django.template.context import RequestContext
# from django.template.context import RequestContext
import simplejson
from fido2 import cbor
from django.http import HttpResponse
from django.conf import settings
from .models import *
from fido2.utils import websafe_decode,websafe_encode
from fido2.utils import websafe_decode, websafe_encode
from fido2.ctap2 import AttestedCredentialData
from .views import login,reset_cookie
from .views import login, reset_cookie
import datetime
from django.utils import timezone
def recheck(request):
context = csrf(request)
context["mode"]="recheck"
request.session["mfa_recheck"]=True
return render(request,"FIDO2/recheck.html", context)
context["mode"] = "recheck"
request.session["mfa_recheck"] = True
return render(request, "FIDO2/recheck.html", context)
def getServer():
rp = PublicKeyCredentialRpEntity(settings.FIDO_SERVER_ID, settings.FIDO_SERVER_NAME)
return Fido2Server(rp)
def begin_registeration(request):
server = getServer()
registration_data, state = server.register_begin({
@@ -35,7 +38,9 @@ def begin_registeration(request):
}, getUserCredentials(request.user.username))
request.session['fido_state'] = state
return HttpResponse(cbor.encode(registration_data),content_type='application/octet-stream')
return HttpResponse(cbor.encode(registration_data), content_type = 'application/octet-stream')
@csrf_exempt
def complete_reg(request):
try:
@@ -50,10 +55,10 @@ def complete_reg(request):
att_obj
)
encoded = websafe_encode(auth_data.credential_data)
uk=User_Keys()
uk = User_Keys()
uk.username = request.user.username
uk.properties = {"device":encoded,"type":att_obj.fmt,}
uk.owned_by_enterprise=getattr(settings,"MFA_OWNED_BY_ENTERPRISE",False)
uk.properties = {"device": encoded, "type": att_obj.fmt, }
uk.owned_by_enterprise = getattr(settings, "MFA_OWNED_BY_ENTERPRISE", False)
uk.key_type = "FIDO2"
uk.save()
return HttpResponse(simplejson.dumps({'status': 'OK'}))
@@ -63,10 +68,13 @@ def complete_reg(request):
client.captureException()
except:
pass
return HttpResponse(simplejson.dumps({'status': 'ERR',"message":"Error on server, please try again later"}))
return HttpResponse(simplejson.dumps({'status': 'ERR', "message": "Error on server, please try again later"}))
def start(request):
context = csrf(request)
return render(request,"FIDO2/Add.html", context)
return render(request, "FIDO2/Add.html", context)
def getUserCredentials(username):
credentials = []
@@ -74,24 +82,27 @@ def getUserCredentials(username):
credentials.append(AttestedCredentialData(websafe_decode(uk.properties["device"])))
return credentials
def auth(request):
context=csrf(request)
return render(request,"FIDO2/Auth.html",context)
context = csrf(request)
return render(request, "FIDO2/Auth.html", context)
def authenticate_begin(request):
server = getServer()
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)
request.session['fido_state'] = state
return HttpResponse(cbor.encode(auth_data),content_type="application/octet-stream")
return HttpResponse(cbor.encode(auth_data), content_type = "application/octet-stream")
@csrf_exempt
def authenticate_complete(request):
try:
credentials = []
username=request.session.get("base_username",request.user.username)
server=getServer()
credentials=getUserCredentials(username)
username = request.session.get("base_username", request.user.username)
server = getServer()
credentials = getUserCredentials(username)
data = cbor.decode(request.body)
credential_id = data['credentialId']
client_data = ClientData(data['clientDataJSON'])
@@ -107,7 +118,8 @@ def authenticate_complete(request):
signature
)
except ValueError:
return HttpResponse(simplejson.dumps({'status': "ERR", "message": "Wrong challenge received, make sure that this is your security and try again."}),
return HttpResponse(simplejson.dumps({'status': "ERR",
"message": "Wrong challenge received, make sure that this is your security and try again."}),
content_type = "application/json")
except Exception as excep:
try:
@@ -119,32 +131,34 @@ def authenticate_complete(request):
"message": excep.message}),
content_type = "application/json")
if request.session.get("mfa_recheck",False):
if request.session.get("mfa_recheck", False):
import time
request.session["mfa"]["rechecked_at"]=time.time()
request.session["mfa"]["rechecked_at"] = time.time()
return HttpResponse(simplejson.dumps({'status': "OK"}),
content_type="application/json")
content_type = "application/json")
else:
import random
keys = User_Keys.objects.filter(username=username, key_type="FIDO2", enabled=1)
keys = User_Keys.objects.filter(username = username, key_type = "FIDO2", enabled = 1)
for k in keys:
if AttestedCredentialData(websafe_decode(k.properties["device"])).credential_id == cred.credential_id:
k.last_used = timezone.now()
k.save()
mfa = {"verified": True, "method": "FIDO2",'id':k.id}
mfa = {"verified": True, "method": "FIDO2", 'id': k.id}
if getattr(settings, "MFA_RECHECK", False):
mfa["next_check"] = int((datetime.datetime.now()+ datetime.timedelta(
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s"))
mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now() + datetime.timedelta(
seconds = random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
request.session["mfa"] = mfa
try:
authenticated=request.user.is_authenticated
authenticated = request.user.is_authenticated
except:
authenticated = request.user.is_authenticated()
if not authenticated:
res=login(request)
res = login(request)
if not "location" in res: return reset_cookie(request)
return HttpResponse(simplejson.dumps({'status':"OK","redirect":res["location"]}),content_type="application/json")
return HttpResponse(simplejson.dumps({'status': "OK", "redirect": res["location"]}),
content_type = "application/json")
return HttpResponse(simplejson.dumps({'status': "OK"}),
content_type = "application/json")
except Exception as exp:
return HttpResponse(simplejson.dumps({'status': "ERR","message":exp.message}),content_type="application/json")
return HttpResponse(simplejson.dumps({'status': "ERR", "message": exp.message}),
content_type = "application/json")

View File

@@ -57,9 +57,9 @@ def validate(request,username):
key.save()
mfa = {"verified": True, "method": "U2F","id":key.id}
if getattr(settings, "MFA_RECHECK", False):
mfa["next_check"] = int((datetime.datetime.now()
mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now()
+ datetime.timedelta(
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s"))
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
request.session["mfa"] = mfa
return True

View File

@@ -6,6 +6,7 @@ from jsonLookup import shasLookup, hasLookup
JSONField.register_lookup(shasLookup)
JSONField.register_lookup(hasLookup)
class User_Keys(models.Model):
username=models.CharField(max_length = 50)
properties=JSONField(null = True)
@@ -20,9 +21,12 @@ class User_Keys(models.Model):
if self.key_type == "Trusted Device" and self.properties.get("signature","") == "":
self.properties["signature"]= jwt.encode({"username": self.username, "key": self.properties["key"]}, settings.SECRET_KEY)
super(User_Keys, self).save(force_insert=force_insert, force_update=force_update, using=using, update_fields=update_fields)
def __unicode__(self):
return "%s -- %s"%(self.username,self.key_type)
def __str__(self):
return self.__unicode__()
class Meta:
app_label='mfa'

View File

@@ -40,9 +40,9 @@ def auth(request):
if res[0]:
mfa = {"verified": True, "method": "TOTP","id":res[1]}
if getattr(settings, "MFA_RECHECK", False):
mfa["next_check"] = int((datetime.datetime.now()
mfa["next_check"] = datetime.datetime.timestamp((datetime.datetime.now()
+ datetime.timedelta(
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))).strftime("%s"))
seconds=random.randint(settings.MFA_RECHECK_MIN, settings.MFA_RECHECK_MAX))))
request.session["mfa"] = mfa
return login(request)
context["invalid"]=True

View File

@@ -6,5 +6,5 @@ python-u2flib-server
ua-parser
user-agents
python-jose
fido2 == 0.8.1
fido2 == 0.9.0
jsonLookup

View File

@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
setup(
name='django-mfa2',
version='2.1.0',
version='2.1.2b1',
description='Allows user to add 2FA to their accounts',
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
@@ -24,14 +24,14 @@ setup(
'ua-parser',
'user-agents',
'python-jose',
'fido2 == 0.9',
'fido2 == 0.9.1',
'jsonLookup'
],
python_requires=">=3.5",
include_package_data=True,
zip_safe=False, # because we're including static files
classifiers=[
"Development Status :: 5 - Production/Stable",
"Development Status :: 4 - Beta",
"Environment :: Web Environment",
"Framework :: Django",
"Framework :: Django :: 1.11",