{% extends "base_settings_page.html" %} {% block content %} <div style="max-width: 700px;"> <p>In order to continue, please click on the "Start Enrollment" button, insert your security key and approve the registration request.</p> <div class="form-group"> <label for="inputKeyName" class="form-label mt-4">Key name</label> <input type="text" class="form-control" id="inputKeyName" placeholder="Device / Authenticator app name" value="Security key" minlength="1" required/> <small class="form-text text-muted">Please give a name to your key to identify it more easily later.</small> <div class="invalid-feedback">Please give a name to this security key</div> </div> <input type="button" class="btn btn-primary" value="Start enrollment" onclick="startEnrollment()" style="margin-top: 20px;" id="submitButton" /> <script src="/assets/js/base64_lib.js"></script> <script> const OPAQUE_STATE = "{{ opaque_state }}"; const REGISTRATION_CHALLENGE = JSON.parse(decodeURIComponent("{{ challenge_json }}")); // Decode data REGISTRATION_CHALLENGE.publicKey.challenge = base64NoPaddingToUint8Array( REGISTRATION_CHALLENGE.publicKey.challenge ); REGISTRATION_CHALLENGE.publicKey.user.id = base64NoPaddingToUint8Array( REGISTRATION_CHALLENGE.publicKey.user.id ); const submitButton = document.getElementById("submitButton"); async function startEnrollment() { submitButton.disabled = true; try { const factorNameInput = document.getElementById("inputKeyName"); factorNameInput.classList.remove("is-invalid"); if (factorNameInput.value.length === 0) { factorNameInput.classList.add("is-invalid"); return; } const cred = await navigator.credentials.create(REGISTRATION_CHALLENGE); // Encode data that needs to be encoded const credential_res = { id: cred.id, rawId: ArrayBufferToBase64(cred.rawId), type: cred.type, response: { attestationObject: ArrayBufferToBase64( cred.response.attestationObject ), clientDataJSON: ArrayBufferToBase64( cred.response.clientDataJSON ), }, }; const res = await fetch("/settings/api/two_factor/save_webauthn_factor", { method: "post", headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ opaque_state: OPAQUE_STATE, factor_name: factorNameInput.value, credential: credential_res, }) }); let text = await res.text(); alert(text); if (res.status == 200) location.href = "/settings/two_factors"; } catch(e) { console.error(e); alert("Failed enrollment, please try again!"); } finally { submitButton.disabled = false; } } </script> </div> {% endblock content %}