async function fetch_challenge () { const username = document.getElementById("user-name").value; const init_data = { username: username, }; const challenge = await fetch("/login/init", { method: 'POST', body: JSON.stringify(init_data), headers: { 'Content-Type': 'application/json' } }); return await challenge.json(); } async function solve_challenge (challenge) { // Decode challenge and credential ID base64 strings into JS arrays. challenge.publicKey.challenge = Base64.toUint8Array(challenge.publicKey.challenge); challenge.publicKey.allowCredentials = challenge.publicKey.allowCredentials.map((cred) => { return Object.assign({}, cred, { id: Base64.toUint8Array(cred.id) }); }); return navigator.credentials.get(challenge); } function encodeSolvedChallenge(solvedChallenge) { const uinttobase64 = (array) => Base64.fromUint8Array(new Uint8Array(array), true); return { id: solvedChallenge.id, rawId: uinttobase64(solvedChallenge.rawId), response: { authenticatorData: uinttobase64(solvedChallenge.response.authenticatorData), clientDataJSON: uinttobase64(solvedChallenge.response.clientDataJSON), signature: uinttobase64(solvedChallenge.response.signature), userHandle: solvedChallenge.response.userHandle }, type: solvedChallenge.type }; } async function finish_auth (challengeUuid, solvedChallenge) { // Encode challenge response const encodedSolvedChallenge = encodeSolvedChallenge(solvedChallenge); const resp = await fetch("/login/finish", { method: 'POST', body: JSON.stringify({uuid: challengeUuid, challenge: encodedSolvedChallenge}), headers: { 'Content-Type': 'application/json' } }); return resp; } async function perform_webauthn_dance () { const challenge = await fetch_challenge() let solved = await solve_challenge(challenge.challenge); let finished = await finish_auth(challenge.uuid, solved); } // Main (async () => { const button = document.getElementById("key-form"); button.addEventListener("submit", async (e) => { e.preventDefault(); await perform_webauthn_dance(); window.location = "/"; }); }) ();