Browse Source

Add error handling on the client side, use Django form API

pull/456/head
Pēteris Caune 4 years ago
parent
commit
53688f1d87
No known key found for this signature in database GPG Key ID: E28D7679E9A9EDE2
4 changed files with 59 additions and 28 deletions
  1. +24
    -0
      hc/accounts/forms.py
  2. +7
    -16
      hc/accounts/views.py
  3. +9
    -6
      static/js/add_credential.js
  4. +19
    -6
      templates/accounts/add_credential.html

+ 24
- 0
hc/accounts/forms.py View File

@ -1,7 +1,11 @@
import base64
from datetime import timedelta as td
from django import forms
from django.contrib.auth import authenticate
from django.contrib.auth.models import User
from fido2.ctap2 import AttestationObject
from fido2.client import ClientData
from hc.api.models import TokenBucket
@ -112,3 +116,23 @@ class ProjectNameForm(forms.Form):
class TransferForm(forms.Form):
email = LowercaseEmailField()
class AddCredentialForm(forms.Form):
name = forms.CharField(max_length=100, required=False)
client_data_json = forms.CharField(required=True)
attestation_object = forms.CharField(required=True)
def clean_client_data_json(self):
v = self.cleaned_data["client_data_json"]
binary = base64.b64decode(v.encode())
obj = ClientData(binary)
return obj
def clean_attestation_object(self):
v = self.cleaned_data["attestation_object"]
binary = base64.b64decode(v.encode())
obj = AttestationObject(binary)
return obj

+ 7
- 16
hc/accounts/views.py View File

@ -18,8 +18,6 @@ from django.utils.timezone import now
from django.urls import resolve, Resolver404
from django.views.decorators.csrf import csrf_exempt
from django.views.decorators.http import require_POST
from fido2.client import ClientData
from fido2.ctap2 import AttestationObject
from fido2.server import Fido2Server
from fido2.webauthn import PublicKeyCredentialRpEntity
from fido2 import cbor
@ -559,18 +557,15 @@ def add_credential(request):
# FIXME use HTTPS, remove the verify_origin hack
server = Fido2Server(rp, verify_origin=_verify_origin)
def decode(form, key):
return base64.b64decode(request.POST[key].encode())
if request.method == "POST":
# idea: use AddCredentialForm
client_data = ClientData(decode(request.POST, "clientDataJSON"))
att_obj = AttestationObject(decode(request.POST, "attestationObject"))
print("clientData", client_data)
print("AttestationObject:", att_obj)
form = forms.AddCredentialForm(request.POST)
if not form.is_valid():
return HttpResponseBadRequest()
auth_data = server.register_complete(
request.session["state"], client_data, att_obj
request.session["state"],
form.cleaned_data["client_data_json"],
form.cleaned_data["attestation_object"],
)
c = Credential(user=request.user)
@ -578,12 +573,9 @@ def add_credential(request):
c.data = auth_data.credential_data
c.save()
print("REGISTERED CREDENTIAL:", auth_data.credential_data)
return render(request, "accounts/success.html")
return redirect("hc-profile")
credentials = [c.unpack() for c in request.user.credentials.all()]
print(credentials)
options, state = server.register_begin(
{
"id": request.user.username.encode(),
@ -595,6 +587,5 @@ def add_credential(request):
request.session["state"] = state
# FIXME: avoid using cbor and cbor.js?
ctx = {"options": base64.b64encode(cbor.encode(options)).decode()}
return render(request, "accounts/add_credential.html", ctx)

+ 9
- 6
static/js/add_credential.js View File

@ -1,8 +1,8 @@
$(function() {
var form = document.getElementById("add-credential-form");
var optionsBinary = btoa(form.dataset.options);
var array = Uint8Array.from(atob(form.dataset.options), c => c.charCodeAt(0));
var options = CBOR.decode(array.buffer);
var optionsBytes = Uint8Array.from(atob(form.dataset.options), c => c.charCodeAt(0));
// cbor.js expects ArrayBuffer as input when decoding
var options = CBOR.decode(optionsBytes.buffer);
console.log("decoded options:", options);
function b64(arraybuffer) {
@ -12,11 +12,14 @@ $(function() {
navigator.credentials.create(options).then(function(attestation) {
console.log("got attestation: ", attestation);
document.getElementById("attestationObject").value = b64(attestation.response.attestationObject);
document.getElementById("clientDataJSON").value = b64(attestation.response.clientDataJSON);
$("#attestation_object").val(b64(attestation.response.attestationObject));
$("#client_data_json").val(b64(attestation.response.clientDataJSON));
console.log("form updated, all is well");
$("#add-credential-submit").prop("disabled", "");
$("#add-credential-success").removeClass("hide");
}).catch(function(err) {
console.log("Something went wrong", err);
$("#add-credential-error span").text(err);
$("#add-credential-error").removeClass("hide");
});
});

+ 19
- 6
templates/accounts/add_credential.html View File

@ -2,18 +2,20 @@
{% load compress static %}
{% block content %}
<h1>Add Credential</h1>
{{ registration_dict|json_script:"registration" }}
<div class="row">
<form
id="add-credential-form"
class="col-sm-6 col-sm-offset-3"
data-options="{{ options }}"
method="post"
encrypt="multipart/form-data">
<h1>Add Credential</h1>
{% csrf_token %}
<input id="attestationObject" type="hidden" name="attestationObject">
<input id="clientDataJSON" type="hidden" name="clientDataJSON">
<input id="attestation_object" type="hidden" name="attestation_object">
<input id="client_data_json" type="hidden" name="client_data_json">
<div class="form-group">
<label for="name">Name</label>
@ -23,14 +25,25 @@
</div>
</div>
<div id="add-credential-error" class="alert alert-danger hide">
<strong>Something went wrong.</strong>
<span></span>
</div>
<div id="add-credential-success" class="alert alert-success hide">
<strong>Success!</strong>
Credential acquired.
</div>
<input
id="add-credential-submit"
class="btn btn-default"
class="btn btn-default pull-right"
type="submit"
name=""
value="Save Credential" disabled>
</form>
</div>
{% endblock %}
{% block scripts %}


Loading…
Cancel
Save