diff --git a/hc/accounts/forms.py b/hc/accounts/forms.py index ffba42ae..9b1f5dc2 100644 --- a/hc/accounts/forms.py +++ b/hc/accounts/forms.py @@ -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 diff --git a/hc/accounts/views.py b/hc/accounts/views.py index 35a78266..b55a50c3 100644 --- a/hc/accounts/views.py +++ b/hc/accounts/views.py @@ -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) diff --git a/static/js/add_credential.js b/static/js/add_credential.js index 32559cba..449a38ff 100644 --- a/static/js/add_credential.js +++ b/static/js/add_credential.js @@ -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"); }); }); \ No newline at end of file diff --git a/templates/accounts/add_credential.html b/templates/accounts/add_credential.html index 49b60d9d..e8bb62ca 100644 --- a/templates/accounts/add_credential.html +++ b/templates/accounts/add_credential.html @@ -2,18 +2,20 @@ {% load compress static %} {% block content %} -

Add Credential

{{ registration_dict|json_script:"registration" }} - +
+

Add Credential

+ {% csrf_token %} - - + +
@@ -23,14 +25,25 @@
+
+ Something went wrong. + +
+ +
+ Success! + Credential acquired. +
+ + - + {% endblock %} {% block scripts %}