diff --git a/CHANGELOG.md b/CHANGELOG.md
index 824bf25..182523f 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,8 @@
# Change Log
+## 2.2.0 (Not released)
+ * Added: MFA_REDIRECT_AFTER_REGISTRATION settings parameter
+
## 2.1.2
* Fixed: Getting timestamp on Python 3.7 as ("%s") is raising an exception
* Upgraded to FIDO 0.9.1
diff --git a/README.md b/README.md
index c777bb4..77a147a 100644
--- a/README.md
+++ b/README.md
@@ -68,6 +68,8 @@ Depends on
MFA_UNALLOWED_METHODS=() # Methods that shouldn't be allowed for the user
MFA_LOGIN_CALLBACK="" # A function that should be called by username to login the user in session
MFA_RECHECK=True # Allow random rechecking of the user
+ MFA_REDIRECT_AFTER_REGISTRATION="mfa_home" # Allows Changing the page after successful registeration
+ MFA_SUCCESS_REGISTRATION_MSG = "Go to Security Home" # The text of the link
MFA_RECHECK_MIN=10 # Minimum interval in seconds
MFA_RECHECK_MAX=30 # Maximum in seconds
MFA_QUICKLOGIN=True # Allow quick login for returning users by provide only their 2FA
@@ -91,6 +93,8 @@ Depends on
**Notes**:
* Starting version 1.1, ~~FIDO_LOGIN_URL~~ isn't required for FIDO2 anymore.
* Starting version 1.7.0, Key owners can be specified.
+ * Starting version 2.2.0
+ * Added: `MFA_SUCCESS_REGISTRATION_MSG` & `MFA_REDIRECT_AFTER_REGISTRATION`
1. Break your login function
Usually your login function will check for username and password, log the user in if the username and password are correct and create the user session, to support mfa, this has to change
diff --git a/docs/change_login.md b/docs/change_login.md
index 32e3617..d1425e6 100644
--- a/docs/change_login.md
+++ b/docs/change_login.md
@@ -9,15 +9,15 @@ Usually your login function will check for username and password, log the user i
* if user has mfa then redirect to mfa page
* if user doesn't have mfa then call your function to create the user session
-
- def login(request): # this function handles the login form POST
- user = auth.authenticate(username=username, password=password)
- if user is not None: # if the user object exist
- from mfa.helpers import has_mfa
- res = has_mfa(username = username,request=request) # has_mfa returns false or HttpResponseRedirect
- if res:
- return res
- return log_user_in(request,username=user.username)
+```python
+def login(request): # this function handles the login form POST
+ user = auth.authenticate(username=username, password=password)
+ if user is not None: # if the user object exist
+ from mfa.helpers import has_mfa
+ res = has_mfa(username = username,request=request) # has_mfa returns false or HttpResponseRedirect
+ if res:
+ return res
+ return log_user_in(request,username=user.username)
#log_user_in is a function that handles creatung user session, it should be in the setting file as MFA_CALLBACK
-
+```
diff --git a/example/example/settings.py b/example/example/settings.py
index fe33941..37735a2 100644
--- a/example/example/settings.py
+++ b/example/example/settings.py
@@ -77,10 +77,8 @@ WSGI_APPLICATION = 'example.wsgi.application'
DATABASES = {
'default': {
- 'ENGINE': 'django.db.backends.mysql',
- 'NAME': 'mfa',
- 'USER': 'root',
- 'PASSWORD': 'password',
+ 'ENGINE': 'django.db.backends.sqlite3',
+ 'NAME': 'test_db',
}
}
@@ -141,7 +139,9 @@ MFA_RECHECK=True # Allow random rechecking of the user
MFA_RECHECK_MIN=10 # Minimum interval in seconds
MFA_RECHECK_MAX=30 # Maximum in seconds
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).
+MFA_HIDE_DISABLE=('',) # Can the user disable his key (Added in 1.2.0).
+MFA_REDIRECT_AFTER_REGISTRATION="registered"
+MFA_SUCCESS_REGISTRATION_MSG="Go to Home"
TOKEN_ISSUER_NAME="PROJECT_NAME" #TOTP Issuer name
diff --git a/example/example/templates/home.html b/example/example/templates/home.html
index b76df9e..c4bdbbd 100644
--- a/example/example/templates/home.html
+++ b/example/example/templates/home.html
@@ -12,6 +12,9 @@
+ {% if registered %}
+
Registered Successfully
+ {% endif %}
Welcome {{ request.user.username }}!
diff --git a/example/example/urls.py b/example/example/urls.py
index 3691a81..bd0dc4d 100644
--- a/example/example/urls.py
+++ b/example/example/urls.py
@@ -22,5 +22,6 @@ urlpatterns = [
path('auth/login',auth.loginView,name="login"),
path('auth/logout',auth.logoutView,name="logout"),
- re_path('^$',views.home,name='home')
+ re_path('^$',views.home,name='home'),
+ path('registered/',views.registered,name='registered')
]
diff --git a/example/example/views.py b/example/example/views.py
index f615692..98e80e9 100644
--- a/example/example/views.py
+++ b/example/example/views.py
@@ -5,3 +5,7 @@ from django.contrib.auth.decorators import login_required
@login_required()
def home(request):
return render(request,"home.html",{})
+
+@login_required()
+def registered(request):
+ return render(request,"home.html",{"registered":True})
diff --git a/example/requiremnts.txt b/example/requiremnts.txt
index 9a1fda4..ee8e548 100644
--- a/example/requiremnts.txt
+++ b/example/requiremnts.txt
@@ -1,2 +1,2 @@
-django==2.0
-django-sslserver
+django >= 2.2
+django_ssl
diff --git a/mfa/Common.py b/mfa/Common.py
index 340c82b..1504340 100644
--- a/mfa/Common.py
+++ b/mfa/Common.py
@@ -1,5 +1,9 @@
from django.conf import settings
from django.core.mail import EmailMessage
+try:
+ from django.urls import reverse
+except:
+ from django.core.urlresolver import reverse
def send(to,subject,body):
from_email_address = settings.EMAIL_HOST_USER
@@ -8,4 +12,8 @@ def send(to,subject,body):
From = "%s <%s>" % (settings.EMAIL_FROM, from_email_address)
email = EmailMessage(subject,body,From,to)
email.content_subtype = "html"
- return email.send(False)
\ No newline at end of file
+ return email.send(False)
+
+def get_redirect_url():
+ return {"redirect_html": reverse(getattr(settings, 'MFA_REDIRECT_AFTER_REGISTRATION', 'mfa_home')),
+ "reg_success_msg":getattr(settings,"MFA_SUCCESS_REGISTRATION_MSG")}
diff --git a/mfa/Email.py b/mfa/Email.py
index 9533c5d..010d6d5 100644
--- a/mfa/Email.py
+++ b/mfa/Email.py
@@ -7,7 +7,9 @@ from .models import *
#from django.template.context import RequestContext
from .views import login
from .Common import send
+
def sendEmail(request,username,secret):
+ """Send Email to the user after rendering `mfa_email_token_template`"""
from django.contrib.auth import get_user_model
User = get_user_model()
key = getattr(User, 'USERNAME_FIELD', 'username')
@@ -18,9 +20,10 @@ def sendEmail(request,username,secret):
@never_cache
def start(request):
+ """Start adding email as a 2nd factor"""
context = csrf(request)
if request.method == "POST":
- if request.session["email_secret"] == request.POST["otp"]:
+ if request.session["email_secret"] == request.POST["otp"]: #if successful
uk=User_Keys()
uk.username=request.user.username
uk.key_type="Email"
@@ -31,15 +34,16 @@ def start(request):
from django.core.urlresolvers import reverse
except:
from django.urls import reverse
- return HttpResponseRedirect(reverse('mfa_home'))
+ return HttpResponseRedirect(reverse(getattr(settings,'MFA_REDIRECT_AFTER_REGISTRATION','mfa_home')))
context["invalid"] = True
else:
- request.session["email_secret"] = str(randint(0,100000))
+ request.session["email_secret"] = str(randint(0,100000)) #generate a random integer
if sendEmail(request, request.user.username, request.session["email_secret"]):
context["sent"] = True
return render(request,"Email/Add.html", context)
@never_cache
def auth(request):
+ """Authenticating the user by email."""
context=csrf(request)
if request.method=="POST":
if request.session["email_secret"]==request.POST["otp"].strip():
diff --git a/mfa/FIDO2.py b/mfa/FIDO2.py
index addda1c..98a6e5e 100644
--- a/mfa/FIDO2.py
+++ b/mfa/FIDO2.py
@@ -14,10 +14,12 @@ from fido2.utils import websafe_decode, websafe_encode
from fido2.ctap2 import AttestedCredentialData
from .views import login, reset_cookie
import datetime
+from .Common import get_redirect_url
from django.utils import timezone
def recheck(request):
+ """Starts FIDO2 recheck"""
context = csrf(request)
context["mode"] = "recheck"
request.session["mfa_recheck"] = True
@@ -25,11 +27,13 @@ def recheck(request):
def getServer():
+ """Get Server Info from settings and returns a Fido2Server"""
rp = PublicKeyCredentialRpEntity(settings.FIDO_SERVER_ID, settings.FIDO_SERVER_NAME)
return Fido2Server(rp)
def begin_registeration(request):
+ """Starts registering a new FIDO Device, called from API"""
server = getServer()
registration_data, state = server.register_begin({
u'id': request.user.username.encode("utf8"),
@@ -43,6 +47,7 @@ def begin_registeration(request):
@csrf_exempt
def complete_reg(request):
+ """Completes the registeration, called by API"""
try:
data = cbor.decode(request.body)
@@ -72,7 +77,9 @@ def complete_reg(request):
def start(request):
+ """Start Registeration a new FIDO Token"""
context = csrf(request)
+ context.update(get_redirect_url())
return render(request, "FIDO2/Add.html", context)
diff --git a/mfa/U2F.py b/mfa/U2F.py
index f7da5dc..fe5ea19 100644
--- a/mfa/U2F.py
+++ b/mfa/U2F.py
@@ -76,6 +76,7 @@ def start(request):
request.session['_u2f_enroll_'] = enroll.json
context=csrf(request)
context["token"]=simplejson.dumps(enroll.data_for_client)
+ context.update(get_redirect_url())
return render(request,"U2F/Add.html",context)
diff --git a/mfa/__init__.py b/mfa/__init__.py
index 0f81ac0..e5dafea 100644
--- a/mfa/__init__.py
+++ b/mfa/__init__.py
@@ -1 +1 @@
-__version__="2.1.2"
+__version__="2.2.0b1"
diff --git a/mfa/models.py b/mfa/models.py
index b97123d..2f0d682 100644
--- a/mfa/models.py
+++ b/mfa/models.py
@@ -2,9 +2,9 @@ from django.db import models
from jsonfield import JSONField
from jose import jwt
from django.conf import settings
-from jsonLookup import shasLookup, hasLookup
-JSONField.register_lookup(shasLookup)
-JSONField.register_lookup(hasLookup)
+#from jsonLookup import shasLookup, hasLookup
+# JSONField.register_lookup(shasLookup)
+# JSONField.register_lookup(hasLookup)
class User_Keys(models.Model):
diff --git a/mfa/templates/FIDO2/Add.html b/mfa/templates/FIDO2/Add.html
index 6ff10b9..74381cf 100644
--- a/mfa/templates/FIDO2/Add.html
+++ b/mfa/templates/FIDO2/Add.html
@@ -32,7 +32,7 @@
}).then(function (res)
{
if (res["status"] =='OK')
- $("#res").html("")
+ $("#res").html("")
else
$("#res").html("")
diff --git a/mfa/templates/TOTP/Add.html b/mfa/templates/TOTP/Add.html
index 5bae45f..d303f96 100644
--- a/mfa/templates/TOTP/Add.html
+++ b/mfa/templates/TOTP/Add.html
@@ -43,7 +43,7 @@
else
{
alert("Your authenticator is added successfully.")
- window.location.href="{% url 'mfa_home' %}"
+ window.location.href="{{ redirect_html }}"
}
}
})
diff --git a/mfa/templates/U2F/Add.html b/mfa/templates/U2F/Add.html
index c354509..0eefcd4 100644
--- a/mfa/templates/U2F/Add.html
+++ b/mfa/templates/U2F/Add.html
@@ -24,7 +24,7 @@
if (data == "OK")
{
alert("Your device is added successfully.")
- window.location.href="{% url 'mfa_home' %}"
+ window.location.href="{{ redirect_html }}"
}
}
})
diff --git a/mfa/totp.py b/mfa/totp.py
index c0c008a..96617c7 100644
--- a/mfa/totp.py
+++ b/mfa/totp.py
@@ -72,4 +72,5 @@ def verify(request):
@never_cache
def start(request):
- return render(request,"TOTP/Add.html",{})
+ """Start Adding Time One Time Password (TOTP)"""
+ return render(request,"TOTP/Add.html",get_redirect_url())
diff --git a/requirements.txt b/requirements.txt
index da1c07d..3b3941a 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,4 @@
-django >= 1.7
+django >= 2.0
jsonfield
simplejson
pyotp
@@ -6,5 +6,5 @@ python-u2flib-server
ua-parser
user-agents
python-jose
-fido2 == 0.9.0
+fido2 == 0.9.1
jsonLookup
diff --git a/setup.py b/setup.py
index 4dac42d..8958ee5 100644
--- a/setup.py
+++ b/setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
setup(
name='django-mfa2',
- version='2.1.2',
+ version='2.2.0b1',
description='Allows user to add 2FA to their accounts',
long_description=open("README.md").read(),
long_description_content_type="text/markdown",
@@ -31,10 +31,9 @@ setup(
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",
"Framework :: Django :: 2.0",
"Framework :: Django :: 2.1",
"Framework :: Django :: 2.2",