diff --git a/src/front/index.html b/src/front/index.html index fd32cc8..c348c97 100644 --- a/src/front/index.html +++ b/src/front/index.html @@ -11,7 +11,8 @@ - + +
diff --git a/src/front/static/ts/views/LoginPage.ts b/src/front/static/ts/views/LoginPage.ts index cffe8fb..2daecda 100644 --- a/src/front/static/ts/views/LoginPage.ts +++ b/src/front/static/ts/views/LoginPage.ts @@ -53,7 +53,47 @@ export default class extends Aview { } async run() { - dragElement(document.getElementById("window")); + dragElement(document.getElementById("window")); + + const totpVerify = async () => { + const username = (document.getElementById("username") as HTMLInputElement).value; + const password = (document.getElementById("password") as HTMLInputElement).value; + const totpPin = (document.getElementById('totpPin') as HTMLInputElement).value; + const idWindow = (document.getElementById('2fa-popup-content') as HTMLInputElement); + try { + const data_req = await fetch("http://localhost:3001/login", { + method: "POST", + headers: { "Content-Type": "application/json", }, + credentials: "include", + body: JSON.stringify({ user: username, password: password, token: totpPin }), + }); + if (data_req.status === 200) { + isLogged(); + navigationManager("/"); + } else if (data_req.status === 401) { + const data = await data_req.json(); + + if (!document.getElementById("error-totp")) { + const error = document.createElement("p"); + error.innerHTML = data.error; + error.classList.add("text-red-700", "dark:text-red-500"); + + idWindow.appendChild(error); + } else { + const error = document.getElementById("error-totp") as HTMLParagraphElement; + error.innerHTML = data.error; + } + + } else { + console.log(data_req.status) + console.log(await data_req.json()) + // throw new Error("invalid response"); + } + } catch (error) { + console.error(error); + } + } + const login = async () => { const username = (document.getElementById("username") as HTMLInputElement).value; const password = (document.getElementById("password") as HTMLInputElement).value; @@ -71,11 +111,69 @@ export default class extends Aview { isLogged(); navigationManager("/"); } + else if (data_req.status === 402) { + const popup: HTMLDivElement = document.createElement("div"); + popup.id = "2fa-popup"; + popup.classList.add("z-10", "absolute", "default-border"); + const header = popup.appendChild(document.createElement("div"));; + header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); + header.id = "2fa-header"; + header.appendChild(document.createElement("span")).innerText = "2fa.ts"; + const btn = header.appendChild(document.createElement("button")); + btn.innerText = " × "; + btn.onclick = () => { document.getElementById("2fa-popup").remove(); }; + + const popup_content: HTMLSpanElement = popup.appendChild(document.createElement("div")); + popup_content.id = "2fa-popup-content"; + popup_content.classList.add("flex", "flex-col", "bg-neutral-200", "dark:bg-neutral-800", "p-6", "pt-4", "text-neutral-900", "dark:text-white", "space-y-4"); + + const tokenInput = document.createElement("input"); + tokenInput.type = "tel"; + tokenInput.id = "totpPin"; + tokenInput.name = "totpPin"; + tokenInput.placeholder = "TOTP code"; + tokenInput.required = true; + tokenInput.autocomplete = "off"; + tokenInput.pattern = "[0-9]*"; + tokenInput.setAttribute("inputmode", "numeric"); + tokenInput.classList.add("bg-white", "text-neutral-900","w-full", "px-4", "py-2", "input-border"); + + const tokenSubmit = document.createElement("button"); + tokenSubmit.type = "submit"; + tokenSubmit.classList.add("default-button", "w-full"); + tokenSubmit.id = "totp-submit"; + tokenSubmit.innerHTML = "submit"; + + const tokenTitle = document.createElement("h1"); + tokenTitle.innerHTML = `hey ${username}, please submit your 2fa code below :`; + tokenTitle.classList.add("text-gray-900", "dark:text-white", "text-lg", "pt-0", "pb-4", "justify-center"); + + const form = document.createElement("form"); + form.method = "dialog"; + form.classList.add("space-y-4"); + form.appendChild(tokenTitle); + form.appendChild(tokenInput); + form.appendChild(tokenSubmit); + + popup_content.appendChild(form); + + const uu = document.getElementById("username") as HTMLInputElement; + const pass = document.getElementById("password") as HTMLInputElement; + + uu.disabled = true; + pass.disabled = true; + + document.getElementById("app")?.appendChild(popup); + tokenInput.focus(); + dragElement(document.getElementById("2fa-popup")); + + document.getElementById("totp-submit")?.addEventListener("click", totpVerify); + } else if (data_req.status === 400) { const data = await data_req.json(); - document.getElementById("login-error-message").innerHTML = "error: " + data.error; - document.getElementById("login-error-message").classList.remove("hidden"); + document.getElementById("login-error-message").innerHTML = "error: " + data.error; + document.getElementById("login-error-message").classList.remove("hidden"); } else { diff --git a/src/front/static/ts/views/Profile.ts b/src/front/static/ts/views/Profile.ts index 24faa0a..4374d70 100644 --- a/src/front/static/ts/views/Profile.ts +++ b/src/front/static/ts/views/Profile.ts @@ -132,6 +132,7 @@ export default class extends Aview { if (!main) return console.error("what"); + // don't read this shit for you mental health if (matches.matchHistory) { for (let match of matches.matchHistory) { const newEntry = document.createElement("li"); diff --git a/src/front/static/ts/views/Settings.ts b/src/front/static/ts/views/Settings.ts index 06632fa..87e3066 100644 --- a/src/front/static/ts/views/Settings.ts +++ b/src/front/static/ts/views/Settings.ts @@ -2,12 +2,12 @@ import Aview from "./Aview.ts" import { dragElement } from "./drag.ts"; import { setOnekoState } from "../oneko.ts" import { isLogged, navigationManager } from "../main.ts" - +import { totpEnablePopup } from "./TotpEnable.ts"; +import { totpVerify } from "../../../../api/auth/totpVerify.js"; export default class extends Aview { - constructor() - { + constructor() { super(); this.setTitle("profile"); setOnekoState("default"); @@ -37,6 +37,8 @@ export default class extends Aview {
+
+ `; @@ -48,6 +50,21 @@ export default class extends Aview { dragElement(document.getElementById("window")); + const isTOTPEnabled = async () => { + const totpVerify_req = await fetch('http://localhost:3001/2fa', { + method: "GET", + credentials: "include" + }) + + if (totpVerify_req.status === 200) { + const totpVerify_data = await totpVerify_req.json(); + if (totpVerify_data.totp == true) { + return true; + } + } + return false; + }; + let uuid: String; uuid = document.cookie.match(new RegExp('(^| )' + "uuid" + '=([^;]+)'))[2]; const userdata_req = await fetch(`http://localhost:3002/users/${uuid}`, { @@ -123,5 +140,40 @@ export default class extends Aview { }); console.log(up_req.status); }; + + const totpButton = document.getElementById("2fa-button") as HTMLButtonElement; + + if ((await isTOTPEnabled()) === true) { + totpButton.innerHTML = "disable 2fa"; + + document.getElementById("2fa-button")?.addEventListener("click", async () => { + const totp_req = await fetch(`http://localhost:3001/2fa`, { + method: "DELETE", + credentials: "include" + }) + if (totp_req.status === 200) { + console.log("working") + navigationManager("/settings") + } else { + console.log("wut") + } + }); + } else { + totpButton.innerHTML = "enable 2fa"; + + document.getElementById("2fa-button")?.addEventListener("click", async () => { + const totp_req = await fetch(`http://localhost:3001/2fa`, { + method: "POST", + credentials: "include" + }) + if (totp_req.status === 200) { + console.log("working") + const totp_data = await totp_req.json(); + totpEnablePopup(uuid, totp_data.secret, totp_data.otpauthUrl); + } else { + console.log("wut") + } + }); + } } } diff --git a/src/front/static/ts/views/TotpEnable.ts b/src/front/static/ts/views/TotpEnable.ts new file mode 100644 index 0000000..baf4fd9 --- /dev/null +++ b/src/front/static/ts/views/TotpEnable.ts @@ -0,0 +1,110 @@ +import { navigationManager } from "../main.ts"; +import { dragElement } from "./drag.ts"; + +async function totpVerify() { + const code = (document.getElementById("totpPin") as HTMLInputElement).value; + const data_req = await fetch('http://localhost:3001/2fa/verify', { + method: "POST", + credentials: "include", + headers: { + "Content-Type": "application/json", + }, + body: JSON.stringify({ + token: code + }) + }) + + if (data_req.status === 200) { + navigationManager("/settings"); + } else if (data_req.status === 401 || data_req.status === 400) { + const popup_content = document.getElementById("2fa-enable-content"); + + if (!document.getElementById("error-totp")) { + const error = document.createElement("p"); + error.id = "error-totp"; + error.classList.add("text-red-700", "dark:text-red-500", "text-center"); + error.innerHTML = (await data_req.json()).error; + + popup_content?.appendChild(error) + } else { + const error = document.getElementById("error-totp") as HTMLParagraphElement; + error.innerHTML = (await data_req.json()).error; + } + } else { + console.log("Unexpected error") + } +} + +export async function totpEnablePopup(username: String, secret: String, url: String) { + const popup: HTMLDivElement = document.createElement("div"); + popup.id = "2fa-enable-popup"; + popup.classList.add("z-10", "absolute", "default-border"); + const header = popup.appendChild(document.createElement("div"));; + header.classList.add("bg-linear-to-r", "from-orange-200", "to-orange-300", "flex", "flex-row", "min-w-35", "justify-between", "px-2"); + header.id = "2fa-enable-popup-header"; + header.appendChild(document.createElement("span")).innerText = "2fa_enable.ts"; + const btn = header.appendChild(document.createElement("button")); + btn.innerText = " × "; + btn.onclick = () => { document.getElementById("2fa-enable-popup")?.remove(); }; + + const popup_content: HTMLSpanElement = popup.appendChild(document.createElement("div")); + popup_content.id = "2fa-enable-content"; + popup_content.classList.add("flex", "flex-col", "bg-neutral-200", "dark:bg-neutral-800", "p-6", "pt-4", "text-neutral-900", "dark:text-white", "space-y-4"); + + const qrDivTOTP = document.createElement("div"); + qrDivTOTP.classList.add("flex", "justify-center"); + + const qrCodeTOTP = document.createElement("img"); + qrCodeTOTP.id = "qrCodeTOTP"; + qrCodeTOTP.src = `https://api.qrserver.com/v1/create-qr-code/?margin=10&size=512x512&data=${url}`; + qrCodeTOTP.classList.add("w-60"); + qrDivTOTP.appendChild(qrCodeTOTP); + + const secretText = document.createElement("p"); + secretText.innerHTML = `key:
${secret}
`; + secretText.classList.add("text-center") + + const tokenInput = document.createElement("input"); + tokenInput.type = "tel"; + tokenInput.id = "totpPin"; + tokenInput.name = "totpPin"; + tokenInput.placeholder = "TOTP code"; + tokenInput.required = true; + tokenInput.autocomplete = "off"; + tokenInput.pattern = "[0-9]*"; + tokenInput.setAttribute("inputmode", "numeric"); + tokenInput.classList.add("bg-white", "text-neutral-900", "w-full", "px-4", "py-2", "input-border"); + + const tokenSubmit = document.createElement("button"); + tokenSubmit.type = "submit"; + tokenSubmit.classList.add("default-button", "w-full"); + tokenSubmit.id = "totp-submit"; + tokenSubmit.innerHTML = "submit"; + + const hr = document.createElement("hr"); + hr.classList.add("my-2", "w-full", "reverse-border"); + + const t = document.createElement("h2"); + t.innerHTML = "hey " + username + + ` you are trying to add 2fa
+ just add the following to your app and enter the code bellow ↓ + `; + t.classList.add("text-center") + + document.getElementById("app")?.appendChild(popup); + + const form = document.createElement("form"); + form.method = "dialog"; + form.classList.add("space-y-4"); + form.appendChild(tokenInput); + form.appendChild(tokenSubmit); + + popup_content.appendChild(t) + popup_content.appendChild(qrDivTOTP); + popup_content.appendChild(secretText); + popup_content.appendChild(hr) + popup_content.appendChild(form); + dragElement(document.getElementById("2fa-enable-popup")); + + document.getElementById("totp-submit")?.addEventListener("click", totpVerify) +}