import { fromByteArray } from 'base64-js'; // Show an error in the U2F login page function showU2FError(error) { } // Encode an ArrayBuffer into a base64 string. function bufferEncode(value) { return fromByteArray(value) .replace(/\+/g, "-") .replace(/\//g, "_") .replace(/=/g, ""); } function bufferDecode(value) { return Uint8Array.from(atob(value.replace(/-/g, '+').replace(/_/g, '/')), c => c.charCodeAt(0)); } function verifyAssertion(url, assertedCredential) { // Move data into Arrays incase it is super long console.log('calling verify') let authData = new Uint8Array(assertedCredential.response.authenticatorData); let clientDataJSON = new Uint8Array(assertedCredential.response.clientDataJSON); let rawId = new Uint8Array(assertedCredential.rawId); let sig = new Uint8Array(assertedCredential.response.signature); let userHandle = new Uint8Array(assertedCredential.response.userHandle); document.getElementById('webauthnFormField').value = JSON.stringify({ id: assertedCredential.id, rawId: bufferEncode(rawId), type: assertedCredential.type, response: { authenticatorData: bufferEncode(authData), clientDataJSON: bufferEncode(clientDataJSON), signature: bufferEncode(sig), userHandle: bufferEncode(userHandle), }, }); document.getElementById('webauthnForm').submit(); } function logoutService(el, service_name, logout_url) { console.log('logging out of ' + service_name); fetch(logout_url, { method: 'GET', credentials: 'include', }) .then(response => { el.classList.add('logout-status-ok'); el.innerHTML = 'OK'; console.log('successful logout for ' + service_name); }) .catch(err => { el.classList.add('logout-status-error'); el.innerHTML = 'ERROR'; console.log('error logging out of ' + service_name); }); } document.addEventListener("DOMContentLoaded", function() { // Activate WebAuthN login process if the WebAuthN data element is found. var webauthnEl = document.getElementById('webAuthNData'); if (webauthnEl) { var validationURL = webauthnEl.dataset.webauthnUrl; var makeAssertionOptions = JSON.parse(webauthnEl.dataset.webauthnCredentialAssertion); console.log('webauthn validation url: ' + validationURL); console.log('webauthn assertion options:'); console.log(makeAssertionOptions); // Decode key material and turn into uint8 arrays. makeAssertionOptions.publicKey.challenge = bufferDecode(makeAssertionOptions.publicKey.challenge); makeAssertionOptions.publicKey.allowCredentials.forEach(function (listItem) { listItem.id = bufferDecode(listItem.id) }); navigator.credentials.get({ publicKey: makeAssertionOptions.publicKey }) .then(function(credential) { console.log('credential:'); console.log(credential); // Decode the clientDataJSON into a utf-8 string. const utf8Decoder = new TextDecoder('utf-8'); const decodedClientData = utf8Decoder.decode( credential.response.clientDataJSON); // parse the string as an object const clientDataObj = JSON.parse(decodedClientData); console.log(clientDataObj); verifyAssertion(validationURL, credential); }) .catch((error) => { // Show the error to the user. Drop the reference to the // RFC, if present, as that's just confusing to the user. var msg = error.message.replace(/\..*$/, '.'); document.getElementById('u2fError').innerText = msg; console.log('WebAuthN ERROR: ' + msg); }); } // Activate the logout process if logout elements are found in the document. document.querySelectorAll('.service-logout').forEach(el => { var url = el.dataset.serviceLogoutUrl; var name = el.dataset.serviceName; logoutService(el, name, url); }); });