Compare commits
44 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| fa0fd3172c | |||
| fcfc6904b6 | |||
| ace60d7343 | |||
| aa76553edb | |||
| 33938ac6e8 | |||
| 5c799f15e9 | |||
| f33631ab48 | |||
| 11079528cb | |||
| b4ca28479a | |||
| e1d2bfa330 | |||
| a9ac8e7709 | |||
| 97ba9eeb44 | |||
| 71a0378ef7 | |||
| 7055a7da02 | |||
| e63e315f29 | |||
| b381a43927 | |||
| a31ecff6ef | |||
| 3eab9cbd46 | |||
| fc301a5a06 | |||
| c71c6d7cab | |||
| 79e9e906dd | |||
| fe1e2abd6f | |||
| 5db1bdca81 | |||
| 1c15c74444 | |||
| bc59406713 | |||
| 3ea974f750 | |||
| 37184edfb9 | |||
| 62a240ac28 | |||
| aa1d417c08 | |||
|
|
77905b0e83 | ||
|
|
1fe11273e3 | ||
|
|
be3cf69956 | ||
|
|
98b9fce1d2 | ||
|
|
17ef0f4b1e | ||
|
|
669fef84fd | ||
|
|
25be381ca9 | ||
|
|
bdb4de3375 | ||
|
|
ab89a204bb | ||
|
|
caaa059d5b | ||
|
|
2d7b80bf5a | ||
|
|
8dba66b7b2 | ||
|
|
54db5a513b | ||
|
|
4903967c23 | ||
|
|
cb2149acf3 |
18
CHANGELOG.md
18
CHANGELOG.md
@@ -1,5 +1,21 @@
|
||||
# Change Log
|
||||
## 2.6.0 (dev)
|
||||
## 2.8.0
|
||||
* Support For Django 4.0+ JSONField
|
||||
* Removed jsonfield package from requirements
|
||||
|
||||
## 2.7.0
|
||||
* Fixed #70
|
||||
* Add QR Code for trusted device link
|
||||
* Better formatting for trusted device start page.
|
||||
## 2.6.1
|
||||
* Fix: CVE-2022-42731: related to the possibility of registration replay attack.
|
||||
Thanks to 'SSE (Secure Systems Engineering)'
|
||||
|
||||
## 2.5.1
|
||||
* Fix: CVE-2022-42731: related to the possibility of registration replay attack.
|
||||
Thanks to 'SSE (Secure Systems Engineering)'
|
||||
|
||||
## 2.6.0
|
||||
* Adding Backup Recovery Codes (Recovery) as a method.
|
||||
Thanks to @Spitfireap for work, and @peterthomassen for guidance.
|
||||
* Added: `RECOVERY_ITERATION` to set the number of iteration when hashing recovery token
|
||||
|
||||
13
README.md
13
README.md
@@ -1,6 +1,7 @@
|
||||
# django-mfa2
|
||||
A Django app that handles MFA, it supports TOTP, U2F, FIDO2 U2F (Web Authn), Email Tokens , Trusted Devices and backup codes.
|
||||
|
||||
[](https://fidoalliance.org/passkeys/)
|
||||
### Pip Stats
|
||||
[](https://badge.fury.io/py/django-mfa2)
|
||||
[](https://pepy.tech/project/django-mfa2)
|
||||
@@ -21,7 +22,9 @@ For FIDO2, the following are supported
|
||||
* **android-safetynet** (Chrome 70+, Firefox 68+)
|
||||
* **NFC devices using PCSC** (Not Tested, but as supported in fido2)
|
||||
* **Soft Tokens**
|
||||
* [krypt.co](https://krypt.co/): Login by a notification on your phone.
|
||||
* ~~[krypt.co](https://krypt.co/): Login by a notification on your phone.~~
|
||||
|
||||
**Update**: Dec 2022, krypt.co has been killed by Google for Passkeys.
|
||||
|
||||
In English :), It allows you to verify the user by security keys on PC, Laptops or Mobiles, Windows Hello (Fingerprint, PIN) on Windows 10 Build 1903+ (May 2019 Update) Touch/Face ID on Macbooks (Chrome, Safari), Touch/Face ID on iPhone and iPad and Fingerprint/Face/Iris/PIN on Android Phones.
|
||||
|
||||
@@ -31,6 +34,8 @@ Trusted device is a mode for the user to add a device that doesn't support secur
|
||||
|
||||
Package tested with Django 1.8, Django 2.2 on Python 2.7 and Python 3.5+ but it was not checked with any version in between but open for issues.
|
||||
|
||||
If you just need WebAuthn and Passkeys, you can use **[django-passkeys](https://github.com/mkalioby/django-passkeys)**, which is a slim-down of this app and much easier to integrate.
|
||||
|
||||
Depends on
|
||||
|
||||
* pyotp
|
||||
@@ -42,8 +47,12 @@ Depends on
|
||||
|
||||
# Installation
|
||||
1. using pip
|
||||
* For Django >= 4.0
|
||||
|
||||
`pip install django-mfa2`
|
||||
* For Django < 4.0
|
||||
|
||||
`pip install django-mfa2 jsonfield`
|
||||
2. Using Conda forge
|
||||
|
||||
`conda config --add channels conda-forge`
|
||||
@@ -196,6 +205,8 @@ function some_func() {
|
||||
* [AndreasDickow](https://github.com/AndreasDickow)
|
||||
* [mnelson4](https://github.com/mnelson4)
|
||||
* [ezrajrice](https://github.com/ezrajrice)
|
||||
* [Spitfireap](https://github.com/Spitfireap)
|
||||
* [peterthomassen](https://github.com/peterthomassen)
|
||||
|
||||
|
||||
# Security contact information
|
||||
|
||||
@@ -16,12 +16,13 @@ Including another URLconf
|
||||
from django.contrib import admin
|
||||
from django.urls import path,re_path,include
|
||||
from . import views,auth
|
||||
from mfa import TrustedDevice
|
||||
urlpatterns = [
|
||||
path('admin/', admin.site.urls),
|
||||
path('mfa/', include('mfa.urls')),
|
||||
path('auth/login',auth.loginView,name="login"),
|
||||
path('auth/logout',auth.logoutView,name="logout"),
|
||||
|
||||
path('devices/add/', TrustedDevice.add,name="add_trusted_device"),
|
||||
re_path('^$',views.home,name='home'),
|
||||
path('registered/',views.registered,name='registered')
|
||||
]
|
||||
|
||||
BIN
img/Works with PassKeys-black.png
Normal file
BIN
img/Works with PassKeys-black.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.3 KiB |
@@ -16,7 +16,7 @@ from .views import login, reset_cookie
|
||||
import datetime
|
||||
from .Common import get_redirect_url
|
||||
from django.utils import timezone
|
||||
|
||||
from django.http import JsonResponse
|
||||
|
||||
def recheck(request):
|
||||
"""Starts FIDO2 recheck"""
|
||||
@@ -49,13 +49,15 @@ def begin_registeration(request):
|
||||
def complete_reg(request):
|
||||
"""Completes the registeration, called by API"""
|
||||
try:
|
||||
if not "fido_state" in request.session:
|
||||
return JsonResponse({'status': 'ERR', "message": "FIDO Status can't be found, please try again"})
|
||||
data = cbor.decode(request.body)
|
||||
|
||||
client_data = CollectedClientData(data['clientDataJSON'])
|
||||
att_obj = AttestationObject((data['attestationObject']))
|
||||
server = getServer()
|
||||
auth_data = server.register_complete(
|
||||
request.session['fido_state'],
|
||||
request.session.pop('fido_state'),
|
||||
client_data,
|
||||
att_obj
|
||||
)
|
||||
@@ -79,7 +81,7 @@ def complete_reg(request):
|
||||
client.captureException()
|
||||
except:
|
||||
pass
|
||||
return HttpResponse(simplejson.dumps({'status': 'ERR', "message": "Error on server, please try again later"}))
|
||||
return JsonResponse({'status': 'ERR', "message": "Error on server, please try again later"})
|
||||
|
||||
|
||||
def start(request):
|
||||
|
||||
@@ -7,10 +7,11 @@ from django.template.context_processors import csrf
|
||||
from .models import *
|
||||
import user_agents
|
||||
from django.utils import timezone
|
||||
from django.urls import reverse
|
||||
|
||||
def id_generator(size=6, chars=string.ascii_uppercase + string.digits):
|
||||
x=''.join(random.choice(chars) for _ in range(size))
|
||||
if not User_Keys.objects.filter(properties__shas="$.key="+x).exists(): return x
|
||||
if not User_Keys.objects.filter(properties__icontains='"key": "%s"'%x).exists(): return x
|
||||
else: return id_generator(size,chars)
|
||||
|
||||
def getUserAgent(request):
|
||||
@@ -57,12 +58,13 @@ def getCookie(request):
|
||||
def add(request):
|
||||
context=csrf(request)
|
||||
if request.method=="GET":
|
||||
context.update({"username":request.GET.get('u',''),"key":request.GET.get('k','')})
|
||||
return render(request,"TrustedDevices/Add.html",context)
|
||||
else:
|
||||
key=request.POST["key"].replace("-","").replace(" ","").upper()
|
||||
context["username"] = request.POST["username"]
|
||||
context["key"] = request.POST["key"]
|
||||
trusted_keys=User_Keys.objects.filter(username=request.POST["username"],properties__has="$.key="+key)
|
||||
trusted_keys=User_Keys.objects.filter(username=request.POST["username"],properties__icontains='"key": "%s"'%key)
|
||||
cookie=False
|
||||
if trusted_keys.exists():
|
||||
tk=trusted_keys[0]
|
||||
@@ -97,7 +99,7 @@ def start(request):
|
||||
request.session["td_id"]=td.id
|
||||
try:
|
||||
if td==None: td=User_Keys.objects.get(id=request.session["td_id"])
|
||||
context={"key":td.properties["key"]}
|
||||
context={"key":td.properties["key"],"url":request.scheme+"://"+request.get_host() + reverse('add_trusted_device')}
|
||||
except:
|
||||
del request.session["td_id"]
|
||||
return start(request)
|
||||
@@ -124,12 +126,14 @@ def verify(request):
|
||||
json= jwt.decode(request.COOKIES.get('deviceid'),settings.SECRET_KEY)
|
||||
if json["username"].lower()== request.session['base_username'].lower():
|
||||
try:
|
||||
uk = User_Keys.objects.get(username=request.POST["username"].lower(), properties__has="$.key=" + json["key"])
|
||||
uk = User_Keys.objects.get(username=request.POST["username"].lower(), properties__icontains='"key": "%s"'%json["key"])
|
||||
if uk.enabled and uk.properties["status"] == "trusted":
|
||||
uk.last_used=timezone.now()
|
||||
uk.save()
|
||||
request.session["mfa"] = {"verified": True, "method": "Trusted Device","id":uk.id}
|
||||
return True
|
||||
except:
|
||||
import traceback
|
||||
print(traceback.format_exc())
|
||||
return False
|
||||
return False
|
||||
|
||||
@@ -4,8 +4,14 @@ from . import TrustedDevice, U2F, FIDO2, totp
|
||||
import simplejson
|
||||
from django.shortcuts import HttpResponse
|
||||
from mfa.views import verify,goto
|
||||
from mfa.recovery import delTokens
|
||||
def has_mfa(request,username):
|
||||
if User_Keys.objects.filter(username=username,enabled=1).count()>0:
|
||||
uk = User_Keys.objects.filter(username=username,enabled=1)
|
||||
if uk.count()==1:
|
||||
for u in uk:
|
||||
if u.key_type == "RECOVERY":
|
||||
delTokens(request)
|
||||
elif uk.count()>0:
|
||||
return verify(request, username)
|
||||
return False
|
||||
|
||||
|
||||
@@ -2,7 +2,14 @@
|
||||
from __future__ import unicode_literals
|
||||
|
||||
from django.db import models, migrations
|
||||
import jsonfield.fields
|
||||
try:
|
||||
from django.db.models import JSONField
|
||||
except ImportError:
|
||||
try:
|
||||
from jsonfield.fields import JSONField
|
||||
except ImportError:
|
||||
raise ImportError("Can't find a JSONField implementation, please install jsonfield if django < 4.0")
|
||||
|
||||
|
||||
|
||||
def modify_json(apps, schema_editor):
|
||||
@@ -24,7 +31,7 @@ class Migration(migrations.Migration):
|
||||
migrations.AddField(
|
||||
model_name='user_keys',
|
||||
name='properties',
|
||||
field=jsonfield.fields.JSONField(null=True),
|
||||
field=JSONField(null=True),
|
||||
),
|
||||
migrations.RunPython(modify_json)
|
||||
]
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
from django.db import models
|
||||
try:
|
||||
from django.db.models import JSONField
|
||||
except ModuleNotFoundError:
|
||||
try:
|
||||
from jsonfield import JSONField
|
||||
except ModuleNotFoundError:
|
||||
raise ModuleNotFoundError("Can't find a JSONField implementation, please install jsonfield if django < 4.0")
|
||||
|
||||
from jose import jwt
|
||||
from django.conf import settings
|
||||
#from jsonLookup import shasLookup, hasLookup
|
||||
|
||||
1
mfa/static/mfa/js/bootstrap-toggle.min.js.map
Normal file
1
mfa/static/mfa/js/bootstrap-toggle.min.js.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"bootstrap-toggle.min.js","sources":["bootstrap-toggle.js"],"names":["$","Plugin","option","this","each","$this","data","options","Toggle","element","$element","extend","defaults","render","VERSION","DEFAULTS","on","off","onstyle","offstyle","size","style","width","height","prototype","attr","_onstyle","_offstyle","$toggleOn","html","addClass","$toggleOff","$toggleHandle","$toggleGroup","append","$toggle","prop","wrap","parent","Math","max","outerWidth","outerHeight","css","update","trigger","toggle","silent","removeClass","enable","removeAttr","disable","change","proxy","destroy","remove","removeData","unwrap","old","fn","bootstrapToggle","Constructor","noConflict","document","e","$checkbox","find","preventDefault","jQuery"],"mappings":"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"}
|
||||
1
mfa/static/mfa/js/qrious.min.js.map
Normal file
1
mfa/static/mfa/js/qrious.min.js.map
Normal file
File diff suppressed because one or more lines are too long
@@ -61,20 +61,23 @@
|
||||
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="container">
|
||||
<div class="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Adding a New {{ method.name }}</strong>
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0"><strong> Adding a New {{ method.name }}</strong></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row alert alert-pr" id="res" align="center">
|
||||
<p style="color: green">Your browser should ask you to confirm you identity.</p>
|
||||
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% include "modal.html" %}
|
||||
|
||||
@@ -1,14 +1,15 @@
|
||||
{% load static %}
|
||||
<script type="application/javascript" src="{% static 'mfa/js/cbor.js' %}"></script>
|
||||
<script type="application/javascript" src="{% static 'mfa/js/ua-parser.min.js' %}"></script>
|
||||
<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 offset-2 col-8">
|
||||
<div class="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Security Key</strong>
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0"><strong> Security Key</strong></h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<div style="padding-left: 15px" class="col-md-10 col-md-offset-1" id="main_paragraph" align="center">
|
||||
@@ -16,7 +17,6 @@
|
||||
Welcome back {% comment %}<img src="{% url 'getUserImage' request.session.base_username %}" title="{{ request.session.base_username }}" style="padding: 3px;height: 50px" class="img-circle"/>{% endcomment %} {{ request.session.base_username }}<br/>
|
||||
<a href="{% url 'mfa_reset_cookie' %}">Not me</a>
|
||||
<br/>
|
||||
|
||||
{% endif %}
|
||||
<div id="res">
|
||||
<p style="color: green">please press the button on your security key to prove it is you.</p>
|
||||
@@ -32,19 +32,13 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
||||
|
||||
{% if request.session.mfa_methods|length > 1 %}
|
||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script type="text/javascript">
|
||||
|
||||
@@ -40,12 +40,15 @@
|
||||
<script src="{% static 'mfa/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="col-lg-10 col-md-10 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-body">
|
||||
<div class="" style="text-align: center">
|
||||
<div class="btn-group">
|
||||
<button class="btn btn-success dropdown-toggle" data-toggle="dropdown" data-bs-toggle="dropdown">
|
||||
Add Method <span class="caret"></span>
|
||||
@@ -70,10 +73,9 @@
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<br/>
|
||||
<table class="table table-striped">
|
||||
<div class="table-responsive">
|
||||
<table class="table table-responsive table-striped">
|
||||
<tr>
|
||||
|
||||
<th>Type</th>
|
||||
<th>Date Added</th>
|
||||
<th>Expires On</th>
|
||||
@@ -85,7 +87,6 @@
|
||||
{% if keys %}
|
||||
{% for key in keys %}
|
||||
<tr>
|
||||
|
||||
<td>{{ key.name }}</td>
|
||||
<td>{{ key.added_on }}</td>
|
||||
<td>{% if key.expires %}{{ key.expires }}{% else %}N/A{% endif %}</td>
|
||||
@@ -121,5 +122,10 @@
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
</div>
|
||||
|
||||
{% include "modal.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% block head %}
|
||||
@@ -98,35 +97,25 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="container d-flex justify-content-center">
|
||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
||||
|
||||
<div class="row">
|
||||
|
||||
<h4>Recovery Codes List</h4>
|
||||
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
|
||||
<div class="tokenrow" id="tokens">
|
||||
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">
|
||||
Recovery Codes List
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<div class="tokenrow mb-4" id="tokens">
|
||||
</div>
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="row">
|
||||
|
||||
<div class="col-md-4 col-md-offset-4" style="padding-left: 0px" align="center">
|
||||
|
||||
<button onclick="confirmRegenerateTokens()" class="btn btn-success">Regenerate</button>
|
||||
|
||||
</div>
|
||||
|
||||
<div class="col-md-6" align="right" style="padding-right: 30px">
|
||||
|
||||
<a href="{{redirect_html}}" class="btn btn-default btn-secondary" role="button"> {{reg_success_msg}}</a>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -22,17 +22,18 @@
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<div class="row">
|
||||
<div class="col-sm-10 col-sm-offset-1 col-xs-12 col-md-10 col-md-offset-1 col-lg-10 col-lg-offset-1">
|
||||
<div class="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Recovery code</strong>
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
|
||||
<FORM METHOD="POST" ACTION="{% url 'recovery_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||
|
||||
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">
|
||||
Recovery code
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form METHOD="POST" ACTION="{% url 'recovery_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||
{% csrf_token %}
|
||||
{% if invalid %}
|
||||
<div class="alert alert-danger">
|
||||
@@ -46,40 +47,30 @@
|
||||
{% endif %}
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<p>Enter the 11-digits on your authenticator. Or input a recovery code</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group input-group-static mb-3">
|
||||
<span class="input-group-addon input-group-text">
|
||||
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
||||
</span>
|
||||
<input class="form-control" size="11" MaxLength="11" value="" placeholder="e.g abcde-fghij" name="recovery" type="text" id="recovery" autofocus>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group d-grid gap-2">
|
||||
|
||||
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_recovery()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in">
|
||||
</div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</FORM>
|
||||
</div>
|
||||
</fieldset>
|
||||
</form>
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
||||
{% if request.session.mfa_methods|length > 1 %}
|
||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "modal.html" %}
|
||||
@@ -1,13 +1,8 @@
|
||||
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% block head %}
|
||||
<style>
|
||||
#two-factor-steps {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 15px;
|
||||
}
|
||||
|
||||
.row{
|
||||
margin: 0px;
|
||||
}
|
||||
@@ -87,11 +82,19 @@
|
||||
{% block content %}
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="container d-flex justify-content-center">
|
||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
||||
<div class="row" align="center">
|
||||
<h4>Adding a new {{ method.name }}</h4>
|
||||
<div class="container-fluid px-2 px-md-4">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100" id="two-factor-steps" >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">Adding a new {{ method.name }}</h6>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="row">
|
||||
|
||||
<p>Scan the image below with the two-factor authentication app on your <a href="javascript:void(0)" onclick="showTOTP()">phone/PC</a>. If you can’t use a barcode,
|
||||
@@ -111,23 +114,27 @@
|
||||
<div class="row">
|
||||
|
||||
<p><b>Enter the six-digit code from the application</b></p>
|
||||
<p style="color: #333333;font-size: 10px">After scanning the barcode image, the app will display a six-digit code that you can enter below. </p>
|
||||
<p >After scanning the barcode image, the app will display a six-digit code that you can enter below. </p>
|
||||
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="offset-md-4 col-md-4">
|
||||
<div class="input-group input-group-static">
|
||||
<input style="display: inline;width: 95%" maxlength="6" size="6" class="form-control" id="answer" placeholder="e.g 785481"/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row" style="padding-top: 10px;">
|
||||
<div class="col-md-4 offset-md-4" style="padding-left: 0px">
|
||||
<div class="offset-md-4 col-md-4">
|
||||
<button class="btn btn-success" onclick="verify()">Enable</button>
|
||||
<a href="{% url 'mfa_home' %}" class="btn btn-default btn-secondary" role="button">Cancel</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
</div>
|
||||
{% include "modal.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
{% extends "mfa_auth_base.html" %}
|
||||
{% block head %}
|
||||
<style>
|
||||
<!--<style>
|
||||
.row{
|
||||
margin-left: 15px;
|
||||
}
|
||||
</style>
|
||||
</style>-->
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
|
||||
@@ -1,30 +1,19 @@
|
||||
<script type="application/javascript">
|
||||
function send_totp() {
|
||||
$.ajax({"url":"{% url 'totp_recheck' %}", method:"POST",dataType:"JSON",
|
||||
data:{"csrfmiddlewaretoken":"{{ csrf_token }}","otp":$("#otp").val()},
|
||||
success:function (data) {
|
||||
if (data["recheck"])
|
||||
mfa_success_function();
|
||||
else {
|
||||
mfa_failed_function();
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
</script>
|
||||
<div class='container'>
|
||||
<div class="row">
|
||||
{% extends "mfa_auth_base.html" %}
|
||||
{% load static %}
|
||||
|
||||
<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="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> One Time Password</strong>
|
||||
{% block content %}
|
||||
|
||||
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
|
||||
<FORM METHOD="POST" ACTION="{% url 'totp_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||
|
||||
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3 ">
|
||||
<h6 class="mb-0">One Time Password</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
<form METHOD="POST" ACTION="{% url 'totp_auth' %}" Id="formLogin" onSubmit="" name="FrontPage_Form1">
|
||||
{% csrf_token %}
|
||||
{% if invalid %}
|
||||
<div class="alert alert-danger">
|
||||
@@ -37,41 +26,39 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<fieldset>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
|
||||
<div class=" text-left">
|
||||
<p>Enter the 6-digits on your authenticator</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group input-group-dynamic mb-3">
|
||||
<span class="input-group-addon input-group-text">
|
||||
<i class="glyphicon glyphicon-lock bi bi-lock"></i>
|
||||
</span>
|
||||
<input class="form-control" size="6" MaxLength="6" value="" placeholder="e.g 55552" name="otp" type="text" id="otp" autofocus>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="form-group d-grid gap-2">
|
||||
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_totp()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in">
|
||||
</div>
|
||||
|
||||
|
||||
<input type="{% if mode == "auth" %}submit{% elif mode == 'recheck' %}button{% endif %}" {% if mode == "recheck" %}onclick="send_totp()" {% endif %} class="btn btn-lg btn-success btn-block" value="Sign in"> </div>
|
||||
</div>
|
||||
</fieldset>
|
||||
</FORM>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3" style="padding-left: 25px">
|
||||
</form>
|
||||
|
||||
|
||||
<div class="">
|
||||
{% if request.session.mfa_methods|length > 1 %}
|
||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{% include "modal.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -25,17 +25,17 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<br/>
|
||||
|
||||
<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="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Add Trusted Device</strong>
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">
|
||||
<strong> Add Trusted Device</strong>
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if success %}
|
||||
<div class="alert alert-warning">
|
||||
Please check your PC window, to continue the process.
|
||||
@@ -46,8 +46,8 @@
|
||||
</div>
|
||||
|
||||
{% else %}
|
||||
<div class="alert alert-warning">Please make sure you are not in private (incognito) mode <i class="fal fa-user-secret"></i></div>
|
||||
<FORM METHOD="POST" ACTION="{% url 'add_td' %}" Id="formLogin" onSubmit="return checkFlag()" name="FrontPage_Form1">
|
||||
<div class="alert alert-warning text-white">Please make sure you are not in private (incognito) mode <i class="fa fa-user-secret"></i></div>
|
||||
<form METHOD="POST" ACTION="{% url 'add_td' %}" Id="formLogin" onSubmit="return checkFlag()" name="FrontPage_Form1">
|
||||
{% csrf_token %}
|
||||
{% if invalid %}
|
||||
<div class="alert alert-danger">
|
||||
@@ -60,23 +60,19 @@
|
||||
</div>
|
||||
{% endif %}
|
||||
<fieldset>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="col-sm-12 col-md-12">
|
||||
<div>
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group input-group-static mb-3">
|
||||
<span class="input-group-addon input-group-text">
|
||||
<i class="glyphicon glyphicon-user bi bi-person"></i>
|
||||
</span>
|
||||
<input class="form-control" id="username" size="30" MaxLength="30" placeholder="Username" name="username" value="{{ username }}" type="text" autofocus autocomplete="on">
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<div class="input-group input-group-static mb-3">
|
||||
<span class="input-group-addon input-group-text">
|
||||
<i class="fa fa-key"></i>
|
||||
</span>
|
||||
@@ -84,27 +80,25 @@
|
||||
</div>
|
||||
</div>
|
||||
<div class="form-group">
|
||||
<span class="input-group mb-3">
|
||||
<input id='agree' name="agree" type="checkbox"><span style="color: red"> I confirm that this device is mine and it is only used by me.</span>
|
||||
|
||||
<span class=" form-check ">
|
||||
<input class="form-check-input" id='agree' name="agree" type="checkbox">
|
||||
<span style="color: red"> I confirm that this device is mine and it is only used by me.
|
||||
</span>
|
||||
</div>
|
||||
{% comment %}
|
||||
<div class="form-group">
|
||||
<div class="input-group mb-3">
|
||||
<span class="input-group-addon input-group-text">
|
||||
|
||||
|
||||
<select size="1" name="Institution_Code" style="font-size: 10pt; font-family: Calibri; height: 34px;width: 230px">
|
||||
{% for ins in institutes %}
|
||||
<option value="{{ ins.institution_code }}">{{ ins.alias }}</option>
|
||||
{% endfor %}
|
||||
|
||||
</select>
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
{% endcomment %}
|
||||
<div class="form-group d-grid gap-2">
|
||||
<div class="form-group d-grid gap-2 mt-2">
|
||||
<input type="submit" class="btn btn-lg btn-success btn-block" value="Trust Device">
|
||||
</div>
|
||||
</div>
|
||||
@@ -113,13 +107,6 @@
|
||||
</form>
|
||||
{% endif %}
|
||||
</div>
|
||||
<div class="panel-footer card-footer">
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
{% include "modal.html" %}
|
||||
{% endblock %}
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% block head %}
|
||||
<script src="{% static 'mfa/js/qrious.min.js' %}" type="text/javascript"></script>
|
||||
<style>
|
||||
#two-factor-steps {
|
||||
border: 1px solid #ccc;
|
||||
@@ -12,6 +14,12 @@
|
||||
</style>
|
||||
|
||||
<script type="text/javascript">
|
||||
$(document).ready(function (){
|
||||
var qr = new QRious({
|
||||
element: document.getElementById('qr'),
|
||||
value: "{{ url }}?u={{ request.user.username }}&k={{ key }}"
|
||||
});
|
||||
})
|
||||
function sendEmail() {
|
||||
$("#modal-title").html("Send Link")
|
||||
$("#modal-body").html("Sending Email, Please wait....");
|
||||
@@ -70,33 +78,58 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="container">
|
||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
||||
<div class="row" align="center">
|
||||
<h4>Add Trusted Device</h4>
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
|
||||
<div class="row" >
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">
|
||||
Add Trusted Device
|
||||
</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
{% if not_allowed %}
|
||||
<div class="alert alert-danger">You can't add any more devices, you need to remove previously trusted devices first.</div>
|
||||
{% else %}
|
||||
<p style="color: green">Allow access from mobile phone and tables.</p>
|
||||
<p style="color: green">Allow access from mobile phone and tables.</p><br/>
|
||||
<br/>
|
||||
<div class="row">
|
||||
<h5>Steps:</h5>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-6">
|
||||
<h5>Using Camera</h5>
|
||||
<ol>
|
||||
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
||||
<li>Go to <b>{{ HOST }}{{ BASE_URL }}devices/add</b> <a href="javascript:void(0)" onclick="sendEmail()" title="Send to my email"><i class="fas fa-paper-plane"></i></a></li>
|
||||
<li>Scan the following barcode <br/>
|
||||
<img id="qr"/> <br/>
|
||||
</li>
|
||||
<li>Confirm the consent and submit form.</li>
|
||||
</ol>
|
||||
</div>
|
||||
<div class="col-md-6">
|
||||
<h5>Manual</h5>
|
||||
<ol>
|
||||
<li>Using your mobile/table, open Chrome/Firefox.</li>
|
||||
<li>Go to <b>{{ url }}</b> </li>
|
||||
<li>Enter your username & following 6 digits<br/>
|
||||
<span style="font-size: 16px;font-weight: bold; margin-left: 50px">{{ key|slice:":3" }} - {{ key|slice:"3:" }}</span>
|
||||
</li>
|
||||
<li>This window will ask to confirm the device.</li>
|
||||
<li>Confirm the consent and submit form.</li>
|
||||
</div>
|
||||
</div>
|
||||
<div class="row">
|
||||
This window will ask to confirm the device.
|
||||
</div>
|
||||
|
||||
|
||||
</ol>
|
||||
{% endif %}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% include "modal.html" %}
|
||||
{% include 'mfa_check.html' %}
|
||||
{% endblock %}
|
||||
@@ -1,16 +1,7 @@
|
||||
{% extends "base.html" %}
|
||||
{% load static %}
|
||||
{% block head %}
|
||||
<style>
|
||||
#two-factor-steps {
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 3px;
|
||||
padding: 15px;
|
||||
}
|
||||
.row{
|
||||
margin: 0px;
|
||||
}
|
||||
</style>
|
||||
|
||||
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||
<script type="text/javascript">
|
||||
function addToken() {
|
||||
@@ -43,20 +34,29 @@
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% block content %}
|
||||
<div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-body">
|
||||
<br/>
|
||||
<br/>
|
||||
<div class="container">
|
||||
|
||||
<div class="col-md-6 col-md-offset-3" id="two-factor-steps">
|
||||
<div id="two-factor-steps">
|
||||
<div id="res"></div>
|
||||
<div class="row" align="center">
|
||||
<h4>Adding {{ method.name}}</h4>
|
||||
</div>
|
||||
<div class="row">
|
||||
<div class="row" align="center">
|
||||
<p style="color: green">Your secure Key should be flashing now, please press on button.</p>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
{% include "modal.html" %}
|
||||
{% endblock %}
|
||||
@@ -1,15 +1,16 @@
|
||||
{% load static %}
|
||||
<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="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Verify your identity using {{ method.name }}</strong>
|
||||
< <div class="col-lg-8 col-md-8 col-12 mx-auto">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="panel-body card-body">
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100 z-index-0 fadeIn3 fadeInBottom " >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">Verify your identity using {{ method.name }}</h6>
|
||||
</div>
|
||||
<div class="card-body">
|
||||
|
||||
<div class="row">
|
||||
<div style="padding-left: 15px" class="col-md-10 col-md-offset-1" id="main_paragraph" align="center">
|
||||
<div id="main_paragraph" align="center">
|
||||
<p style="color: green">Your key should be flashing now, please press the button.</p>
|
||||
{% if mode == "auth" %}
|
||||
<form id="u2f_login" action="{% url 'u2f_verify' %}" method="post">
|
||||
@@ -21,18 +22,17 @@
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="row">
|
||||
<div class="col-md-12 mb-3" style="padding-left: 15px">
|
||||
|
||||
{% if request.session.mfa_methods|length > 1 %}
|
||||
<a href="{% url 'mfa_methods_list' %}">Select Another Method</a>
|
||||
{% endif %}
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<script src="{% static 'mfa/js/u2f-api.js' %}" type="text/javascript"></script>
|
||||
|
||||
@@ -1,19 +1,20 @@
|
||||
{% extends "mfa_auth_base.html" %}
|
||||
{% block content %}
|
||||
<br/>
|
||||
<div class='container'>
|
||||
<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 offset-2 col-8">
|
||||
<div class="panel panel-default card">
|
||||
<div class="panel-heading card-header">
|
||||
<strong> Select Second Verification Method</strong>
|
||||
<div class="container-fluid px-2 px-md-4 mt-6">
|
||||
<div class="page-header min-height-300 border-radius-xl mt-4" style="background-repeat: no-repeat; background-position: cover; background-size:contain; background-image: url('{% static 'img/keys.jpg'%}');">
|
||||
<span class="mask bg-gradient-primary opacity-6"></span>
|
||||
</div>
|
||||
<div class="card mx-3 mx-md-4 mt-n6 h-100" >
|
||||
<div class="card-header pb-0 p-3">
|
||||
<h6 class="mb-0">Select Second Verification Method</h6>
|
||||
</div>
|
||||
<div class="card-body p-3">
|
||||
<div class="panel-body card-body">
|
||||
<ul>
|
||||
{% for method in request.session.mfa_methods %}
|
||||
|
||||
<li><a href="{% url "mfa_goto" method %}">
|
||||
<li style="list-style-type: none;"><a class="btn btn-sm btn-outline-primary" href="{% url 'mfa_goto' method %}" >
|
||||
{% if method == "TOTP" %}{% if 'TOTP' in RENAME_METHODS %}{{ RENAME_METHODS.TOTP }}{% else %}Authenticator App{% endif %}
|
||||
{% elif method == "Email" %}{% if 'Email' in RENAME_METHODS %}{{ RENAME_METHODS.Email }}{% else %}Send OTP by Email{% endif %}
|
||||
{% elif method == "U2F" %}{% if 'U2F' in RENAME_METHODS %}{{ RENAME_METHODS.U2F }}{% else %}Secure Key{% endif %}
|
||||
@@ -25,8 +26,6 @@
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
7
setup.py
7
setup.py
@@ -4,7 +4,7 @@ from setuptools import find_packages, setup
|
||||
|
||||
setup(
|
||||
name='django-mfa2',
|
||||
version='2.5.0',
|
||||
version='2.8.0',
|
||||
description='Allows user to add 2FA to their accounts',
|
||||
long_description=open("README.md").read(),
|
||||
long_description_content_type="text/markdown",
|
||||
@@ -17,7 +17,6 @@ setup(
|
||||
packages=find_packages(),
|
||||
install_requires=[
|
||||
'django >= 2.0',
|
||||
'jsonfield',
|
||||
'simplejson',
|
||||
'pyotp',
|
||||
'python-u2flib-server',
|
||||
@@ -25,13 +24,13 @@ setup(
|
||||
'user-agents',
|
||||
'python-jose',
|
||||
'fido2 == 1.0.0',
|
||||
'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 :: 2.0",
|
||||
@@ -41,6 +40,7 @@ setup(
|
||||
"Framework :: Django :: 3.1",
|
||||
"Framework :: Django :: 3.2",
|
||||
"Framework :: Django :: 4.0",
|
||||
"Framework :: Django :: 4.1",
|
||||
"Intended Audience :: Developers",
|
||||
"Operating System :: OS Independent",
|
||||
"Programming Language :: Python",
|
||||
@@ -51,6 +51,7 @@ setup(
|
||||
"Programming Language :: Python :: 3.8",
|
||||
"Programming Language :: Python :: 3.9",
|
||||
"Programming Language :: Python :: 3.10",
|
||||
"Programming Language :: Python :: 3.11",
|
||||
"Topic :: Software Development :: Libraries :: Python Modules",
|
||||
]
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user