diff --git a/src/controllers/main.js b/src/controllers/main.js index 6fd216e..2137e33 100644 --- a/src/controllers/main.js +++ b/src/controllers/main.js @@ -21,6 +21,9 @@ const mainController = { }, chat : (req, res) => { res.sendFile(path.resolve(__dirname + '/../public/chat.js')); + }, + register : (req, res) => { + res.sendFile(path.resolve(__dirname + '/../public/register.js')); } }; diff --git a/src/public/popups.js b/src/public/popups.js index e78d4f5..1b786b9 100644 --- a/src/public/popups.js +++ b/src/public/popups.js @@ -1,14 +1,19 @@ -const currentUrl = window.location.href; -import { ab2str, exportedKeyToPem, pemToKey, genKey } from "./ecc.js"; +import { loginConfirm, registerConfirm } from "./register.js"; -// close popups with escape key -document.addEventListener("keydown", (event) => { - if (event.isComposing || event.key === 'Escape') { - Array.from(document.getElementsByClassName("popup")).forEach(function(x) { - x.style.display = 'none'; - }); - document.getElementById("registerPopupText").innerText = ""; - } +// handle key presses (close/confirm) +document.addEventListener("keydown", async function(event) { + if (event.isComposing || event.key === 'Escape') { + Array.from(document.getElementsByClassName("popup")).forEach(function(x) { + x.style.display = 'none'; + }); + document.getElementById("registerPopupText").innerText = ""; + } else if (event.key === 'Enter') { + if (registerPopup.style.display == 'flex') { + await registerConfirm(); + } else if (loginPopup.style.display == 'flex') { + await loginConfirm(); + } + } }); // register popup @@ -21,43 +26,8 @@ document.getElementById("registercancel").addEventListener("click", function () document.getElementById("registerPopupText").innerText = ""; }); // confirm -document.getElementById("registerconfirm").addEventListener("click", async function () { - const apiUrl = `${currentUrl}account/register`; - const inputFieldSharedSecret = document.getElementById("sharedsecret"); - const inputFieldPublicKey = document.getElementById("publickey"); - let pubkey = null; - if (!inputFieldPublicKey.value) { - const { privateKey, publicKey } = await genKey(); - pubkey = exportedKeyToPem(publicKey, "public"); - document.getElementById("registerPopupText").innerText = exportedKeyToPem(privateKey, "private"); - } else { - pubkey = inputFieldPublicKey.value; - } - const postData = { - sharedSecret: inputFieldSharedSecret.value, - publicKey: pubkey - }; - // clear input fields - inputFieldSharedSecret.value=''; - inputFieldPublicKey.value=''; - - const requestOptions = { - method: 'POST', - headers: { - 'Content-Type': 'application/json', - }, - body: JSON.stringify(postData) - }; - const response = await fetch(apiUrl, requestOptions) - .catch(error => { - console.error('Error during POST request:', error); - }); - - if (response.ok) { - console.log(response); - } else { - throw new Error('Error in server response'); - } +document.getElementById("registerconfirm").addEventListener("click", async () => { + await registerConfirm(); }); //login popup @@ -69,48 +39,6 @@ document.getElementById("logincancel").addEventListener("click", function () { loginPopup.style.display = 'none'; }); // confirm -document.getElementById("loginconfirm").addEventListener("click", async function () { - const apiUrl = `${currentUrl}account/login`; - const inputFieldPrivateKey = document.getElementById("privatekey"); - const response = await fetch(apiUrl, { method: 'GET' }); - if (!response.ok) { - throw new Error('Failed to fetch challenge'); - } - const { challenge } = await response.json(); - console.log("Received challenge:", challenge); - - let privKey = await pemToKey(inputFieldPrivateKey.value, "private"); - - const encoder = new TextEncoder(); - let encodedData = encoder.encode(challenge); - - // Sign the data using the private key. - const signature = await crypto.subtle.sign( - { - name: "Ed25519", - }, - privKey, - encodedData, - ); - let signatureString = ab2str(signature); - let signatureBase64 = window.btoa(signatureString); - - const verifyApiUrl = `${currentUrl}account/verify-challenge`; - const verifyResponse = await fetch(verifyApiUrl, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - signature: signatureBase64 - }) - }); - - if (!verifyResponse.ok) { - throw new Error('Failed to verify the challenge'); - } else { - const verifyResult = await verifyResponse.json(); - console.log("Verification result:", verifyResult); - location.reload(); - } +document.getElementById("loginconfirm").addEventListener("click", async () => { + await loginConfirm(); }); diff --git a/src/public/register.js b/src/public/register.js new file mode 100644 index 0000000..7df5c38 --- /dev/null +++ b/src/public/register.js @@ -0,0 +1,89 @@ +const currentUrl = window.location.href; +import { ab2str, exportedKeyToPem, pemToKey, genKey } from "./ecc.js"; + +export async function registerConfirm() { + const apiUrl = `${currentUrl}account/register`; + const inputFieldSharedSecret = document.getElementById("sharedsecret"); + const inputFieldPublicKey = document.getElementById("publickey"); + let pubkey = null; + if (!inputFieldPublicKey.value) { + const { privateKey, publicKey } = await genKey(); + pubkey = exportedKeyToPem(publicKey, "public"); + document.getElementById("registerPopupText").innerText = exportedKeyToPem(privateKey, "private"); + } else { + pubkey = inputFieldPublicKey.value; + } + const postData = { + sharedSecret: inputFieldSharedSecret.value, + publicKey: pubkey + }; + // clear input fields + inputFieldSharedSecret.value=''; + inputFieldPublicKey.value=''; + + const requestOptions = { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + }, + body: JSON.stringify(postData) + }; + const response = await fetch(apiUrl, requestOptions) + .catch(error => { + console.error('Error during POST request:', error); + }); + + if (response.ok) { + console.log(response); + } else { + document.getElementById("registerPopupText").innerText = "Registration failed. Please try again."; + console.error('Error in server response.', response); + } +} + +export async function loginConfirm() { + const apiUrl = `${currentUrl}account/login`; + const inputFieldPrivateKey = document.getElementById("privatekey"); + const response = await fetch(apiUrl, { method: 'GET' }); + if (!response.ok) { + throw new Error('Failed to fetch challenge'); + } + const { challenge } = await response.json(); + console.log("Received challenge:", challenge); + + let privKey = await pemToKey(inputFieldPrivateKey.value, "private"); + + const encoder = new TextEncoder(); + let encodedData = encoder.encode(challenge); + + // Sign the data using the private key. + const signature = await crypto.subtle.sign( + { + name: "Ed25519", + }, + privKey, + encodedData, + ); + let signatureString = ab2str(signature); + let signatureBase64 = window.btoa(signatureString); + + const verifyApiUrl = `${currentUrl}account/verify-challenge`; + const verifyResponse = await fetch(verifyApiUrl, { + method: 'POST', + headers: { + 'Content-Type': 'application/json' + }, + body: JSON.stringify({ + signature: signatureBase64 + }) + }); + + if (!verifyResponse.ok) { + throw new Error('Failed to verify the challenge'); + } else { + const verifyResult = await verifyResponse.json(); + console.log("Verification result:", verifyResult); + inputFieldPrivateKey.value = ''; + location.reload(); + } +} diff --git a/src/routes/root.js b/src/routes/root.js index ab86888..84fb1fe 100644 --- a/src/routes/root.js +++ b/src/routes/root.js @@ -26,4 +26,8 @@ router .route("/chat.js") .get(mainController.chat); +router + .route("/register.js") + .get(mainController.register); + module.exports = router; diff --git a/src/views/index.pug b/src/views/index.pug index f291830..87f5880 100644 --- a/src/views/index.pug +++ b/src/views/index.pug @@ -12,6 +12,7 @@ html(lang="en-US") script(src="/chat.js", defer) else script(type="module", src="/popups.js", defer) + script(type="module", src="/register.js", defer) link(rel="stylesheet" href="/css/bootstrap.min.css") body #mainbody