(text "{% extends \"auth/base.html\" %} {% block head %}") (title (text "Login")) (text "{% endblock %} {% block title %}Login{% endblock %} {% block content %}") (form ("class" "w_full flex flex_col gap_4") ("onsubmit" "login(event)") (div ("id" "flow_1") ("style" "display: contents") (div ("class" "flex flex_col gap_1") (label ("for" "username") (b (text "Username"))) (input ("type" "text") ("placeholder" "username") ("required" "") ("name" "username") ("id" "username"))) (div ("class" "flex flex_col gap_1") (label ("for" "username") (b (text "Password"))) (input ("type" "password") ("placeholder" "password") ("required" "") ("name" "password") ("id" "password")))) (div ("id" "flow_2") ("style" "display: none") (div ("class" "flex flex_col gap_1") (label ("for" "totp") (b (text "TOTP code"))) (input ("type" "text") ("placeholder" "totp code") ("name" "totp") ("id" "totp")))) (button (icon (text "arrow-right")) (str (text "auth:action.continue")))) (script (text "let flow_page = 1; function next_page() { document.getElementById(`flow_${flow_page}`).style.display = \"none\"; flow_page += 1; document.getElementById(`flow_${flow_page}`).style.display = \"contents\"; } async function login(e) { e.preventDefault(); if (flow_page === 1) { // check if we need TOTP const res = await ( await fetch( `/api/v1/auth/user/${e.target.username.value}/totp/check`, ) ).json(); trigger(\"atto::toast\", [res.ok ? \"success\" : \"error\", res.message]); if (res.ok && res.payload) { // user exists AND totp is required return next_page(); } } fetch(\"/api/v1/auth/login\", { method: \"POST\", headers: { \"Content-Type\": \"application/json\", }, body: JSON.stringify({ username: e.target.username.value, password: e.target.password.value, totp: e.target.totp.value, }), }) .then((res) => res.json()) .then(async (res) => { trigger(\"atto::toast\", [ res.ok ? \"success\" : \"error\", res.message, ]); if (res.ok) { // update tokens const new_tokens = (await ns(\"me\")).LOGIN_ACCOUNT_TOKENS; new_tokens[e.target.username.value] = res.message; trigger(\"me::set_login_account_tokens\", [new_tokens]); // redirect setTimeout(() => { window.location.href = \"/\"; }, 150); } }); }")) (text "{% endblock %} {% block footer %}") (span ("class" "small w_full text_center") (text "Or, ") (a ("href" "/auth/register") (text "register"))) (text "{% endblock %}")