import { Link as RouterLink, useNavigate, useSearchParams } from "react-router-dom"; import { Alert, Avatar, Box, Button, Card, CardContent, Collapse, Grid, LinearProgress, Link, Paper, TextField, ToggleButton, ToggleButtonGroup, Typography } from "@mui/material"; import { FormEvent, useState } from "react"; import { request } from "@/scripts/request.ts"; import { useUserinfo } from "@/stores/userinfo.tsx"; import LoginIcon from "@mui/icons-material/Login"; import SecurityIcon from "@mui/icons-material/Security"; import KeyIcon from "@mui/icons-material/Key"; import PasswordIcon from "@mui/icons-material/Password"; import EmailIcon from "@mui/icons-material/Email"; export default function SignInPage() { const [panel, setPanel] = useState(0); const [error, setError] = useState(null); const [loading, setLoading] = useState(false); const [factor, setFactor] = useState(); const [factorType, setFactorType] = useState(); const [factors, setFactors] = useState(null); const [challenge, setChallenge] = useState(null); const { readProfiles } = useUserinfo(); const [searchParams] = useSearchParams(); const navigate = useNavigate(); const handlers: any[] = [ async (evt: FormEvent) => { evt.preventDefault(); const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement)); if (!data.id) return; setLoading(true); const res = await request("/api/auth", { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data) }); if (res.status !== 200) { setError(await res.text()); } else { const data = await res.json(); setFactors(data["factors"]); setChallenge(data["challenge"]); setPanel(1); setError(null); } setLoading(false); }, async (evt: FormEvent) => { evt.preventDefault(); if (!factor) return; setLoading(true); const res = await request(`/api/auth/factors/${factor}`, { method: "POST" }); if (res.status !== 200 && res.status !== 204) { setError(await res.text()); } else { const item = factors.find((item: any) => item.id === factor).type; setError(null); setPanel(2); setFactorType(factorTypes[item]); } setLoading(false); }, async (evt: SubmitEvent) => { evt.preventDefault(); const data = Object.fromEntries(new FormData(evt.target as HTMLFormElement)); if (!data.credentials) return; setLoading(true); const res = await request(`/api/auth`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ challenge_id: challenge?.id, factor_id: factor, secret: data.credentials }) }); if (res.status !== 200) { setError(await res.text()); } else { const data = await res.json(); if (data["is_finished"]) { await grantToken(data["session"]["grant_token"]); await readProfiles(); callback(); } else { setError(null); setPanel(1); setFactor(undefined); setFactorType(undefined); setChallenge(data["challenge"]); } } setLoading(false); } ]; function callback() { if (searchParams.has("closable")) { window.close(); } else if (searchParams.has("redirect_uri")) { window.open(searchParams.get("redirect_uri") ?? "/", "_self"); } else { navigate("/users"); } } function getFactorAvailable(factor: any) { const blacklist: number[] = challenge?.blacklist_factors ?? []; return blacklist.includes(factor.id); } const factorTypes = [ { icon: , label: "Password Verification", autoComplete: "password" }, { icon: , label: "Email One Time Password", autoComplete: "one-time-code" } ]; const elements = [ ( <> Welcome back ), ( <> Verify that's you setFactor(val)} > {factors?.map((item: any, idx: number) => ( {factorTypes[item.type]?.icon} {factorTypes[item.type]?.label} ))} ), ( <> Enter the credentials ) ]; async function grantToken(tk: string) { const res = await request("/api/auth/token", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ code: tk, grant_type: "grant_token" }) }); if (res.status !== 200) { const err = await res.text(); setError(err); throw new Error(err); } else { setError(null); } } return ( <> {error && {error}} You need sign in before take an action. After that, we will take you back to your work. {elements[panel]} Risk {challenge?.risk_level}  Progress {challenge?.progress}/{challenge?.requirements} Haven't an account? Sign up! ); }