Compare commits
1 Commits
master
...
ResidentKe
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
41e105b45b |
@@ -12,7 +12,7 @@ A Django app that handles MFA, it supports TOTP, U2F, FIDO2 U2F (Web Authn), Ema
|
||||
|
||||
Web Authencation API (WebAuthn) is state-of-the art techology that is expected to replace passwords.
|
||||
|
||||

|
||||

|
||||
|
||||
For FIDO2, the following are supported
|
||||
* **security keys** (Firefox 60+, Chrome 67+, Edge 18+, Safari 13 on Mac OS, Chrome on Andriod, Safari on iOS 13.3+),
|
||||
@@ -75,6 +75,7 @@ Depends on
|
||||
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_RESIDENT_KEY = None # Use Resident Key (Only supported in Chromimum based browsers)
|
||||
MFA_HIDE_DISABLE=('FIDO2',) # Can the user disable his key (Added in 1.2.0).
|
||||
MFA_OWNED_BY_ENTERPRISE = FALSE # Who owns security keys
|
||||
|
||||
|
||||
@@ -3,6 +3,7 @@ from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
from django.contrib.auth import authenticate,login,logout
|
||||
from django.contrib.auth.models import User
|
||||
|
||||
def loginView(request):
|
||||
context={}
|
||||
if request.method=="POST":
|
||||
|
||||
@@ -142,9 +142,10 @@ MFA_QUICKLOGIN=True # Allow quick login for returning users by provide on
|
||||
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"
|
||||
MFA_RESIDENT_KEY = True
|
||||
|
||||
TOKEN_ISSUER_NAME="PROJECT_NAME" #TOTP Issuer name
|
||||
|
||||
U2F_APPID="https://localhost" #URL For U2F
|
||||
FIDO_SERVER_ID=u"local.mkalioby.com" # Server rp id for FIDO2, it the full domain of your project
|
||||
FIDO_SERVER_ID=u"localhost" # Server rp id for FIDO2, it the full domain of your project
|
||||
FIDO_SERVER_NAME=u"TestApp"
|
||||
|
||||
@@ -45,6 +45,10 @@
|
||||
</div>
|
||||
|
||||
<button class="btn btn-primary btn-block" type="submit">Login</button>
|
||||
<br/>
|
||||
OR
|
||||
<br/>
|
||||
<a href="{% url 'fido2_auth' %}"><button class="btn btn-primary btn-block" type="button">Login By Security Key</button></a>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
23
mfa/FIDO2.py
23
mfa/FIDO2.py
@@ -39,7 +39,7 @@ def begin_registeration(request):
|
||||
u'id': request.user.username.encode("utf8"),
|
||||
u'name': (request.user.first_name + " " + request.user.last_name),
|
||||
u'displayName': request.user.username,
|
||||
}, getUserCredentials(request.user.username))
|
||||
}, getUserCredentials(request.user.username),resident_key=getattr(settings,'MFA_RESIDENT_KEY',None))
|
||||
request.session['fido_state'] = state
|
||||
|
||||
return HttpResponse(cbor.encode(registration_data), content_type = 'application/octet-stream')
|
||||
@@ -63,6 +63,8 @@ def complete_reg(request):
|
||||
uk = User_Keys()
|
||||
uk.username = request.user.username
|
||||
uk.properties = {"device": encoded, "type": att_obj.fmt, }
|
||||
if data.get('userHandle'):
|
||||
uk.properties["userHandle"] = data['userHandle']
|
||||
uk.owned_by_enterprise = getattr(settings, "MFA_OWNED_BY_ENTERPRISE", False)
|
||||
uk.key_type = "FIDO2"
|
||||
uk.save()
|
||||
@@ -97,6 +99,8 @@ def auth(request):
|
||||
|
||||
def authenticate_begin(request):
|
||||
server = getServer()
|
||||
credentials=None
|
||||
if not getattr(settings,'MFA_RESIDENT_KEY',None):
|
||||
credentials = getUserCredentials(request.session.get("base_username", request.user.username))
|
||||
auth_data, state = server.authenticate_begin(credentials)
|
||||
request.session['fido_state'] = state
|
||||
@@ -107,13 +111,26 @@ def authenticate_begin(request):
|
||||
def authenticate_complete(request):
|
||||
try:
|
||||
credentials = []
|
||||
data = cbor.decode(request.body)
|
||||
|
||||
if data.get("userHandle"):
|
||||
keys = User_Keys.objects.filter(key_type="FIDO2", properties__icontains='"userHandle": "%s"'%data["userHandle"])
|
||||
if keys.count()==1:
|
||||
username = keys[0].username
|
||||
request.session["base_username"]=username
|
||||
request.session.update = 1
|
||||
|
||||
else:
|
||||
username = request.session.get("base_username", request.user.username)
|
||||
|
||||
|
||||
server = getServer()
|
||||
credentials = getUserCredentials(username)
|
||||
data = cbor.decode(request.body)
|
||||
auth_data = AuthenticatorData(data['authenticatorData'])
|
||||
|
||||
credential_id = data['credentialId']
|
||||
client_data = ClientData(data['clientDataJSON'])
|
||||
auth_data = AuthenticatorData(data['authenticatorData'])
|
||||
|
||||
signature = data['signature']
|
||||
try:
|
||||
cred = server.authenticate_complete(
|
||||
|
||||
@@ -17,10 +17,12 @@
|
||||
|
||||
return navigator.credentials.create(options);
|
||||
}).then(function(attestation) {
|
||||
console.log(attestation)
|
||||
return fetch('{% url 'fido2_complete_reg' %}', {
|
||||
method: 'POST',
|
||||
headers: {'Content-Type': 'application/cbor'},
|
||||
body: CBOR.encode({
|
||||
"userHandle":attestation.id,
|
||||
"attestationObject": new Uint8Array(attestation.response.attestationObject),
|
||||
"clientDataJSON": new Uint8Array(attestation.response.clientDataJSON),
|
||||
})
|
||||
|
||||
@@ -58,8 +58,11 @@
|
||||
}).then(CBOR.decode).then(function(options) {
|
||||
console.log(options)
|
||||
return navigator.credentials.get(options);
|
||||
|
||||
}).then(function(assertion) {
|
||||
console.log(assertion)
|
||||
res=CBOR.encode({
|
||||
"userHandle":assertion.id,
|
||||
"credentialId": new Uint8Array(assertion.rawId),
|
||||
"authenticatorData": new Uint8Array(assertion.response.authenticatorData),
|
||||
"clientDataJSON": new Uint8Array(assertion.response.clientDataJSON),
|
||||
|
||||
Reference in New Issue
Block a user