import VisibilityIcon from '@mui/icons-material/Visibility'; import VisibilityOffIcon from '@mui/icons-material/VisibilityOff'; import { Alert, CircularProgress, FormControl, IconButton, InputAdornment, InputLabel, OutlinedInput, Tooltip, } from "@mui/material"; import Box from "@mui/material/Box"; import Button from "@mui/material/Button"; import TextField from "@mui/material/TextField"; import Typography from "@mui/material/Typography"; import * as React from "react"; import { useNavigate } from "react-router-dom"; import { useAuth } from "../../App"; import { AuthApi } from "../../api/AuthApi"; import { ServerApi } from "../../api/ServerApi"; /** * Login form */ export function LoginRoute(): React.ReactElement { const [loading, setLoading] = React.useState(false); const [error, setError] = React.useState<string | null>(null); const auth = useAuth(); const navigate = useNavigate(); const [username, setUsername] = React.useState(""); const [password, setPassword] = React.useState(""); const canSubmit = username.length > 0 && password.length > 0; const [showPassword, setShowPassword] = React.useState(false); const handleClickShowPassword = () => { setShowPassword((show) => !show); }; const handleMouseDownPassword = ( event: React.MouseEvent<HTMLButtonElement> ) => { event.preventDefault(); }; const handleLoginSubmit = async (event: React.FormEvent<HTMLFormElement>) => { event.preventDefault(); if (!canSubmit) return; try { await AuthApi.LoginWithPassword(username, password); navigate("/"); auth.setSignedIn(true); } catch (e) { console.error(e); setError("Auth failed!"); } setLoading(false); }; const authWithOpenID = async () => { try { setLoading(true); const res = await AuthApi.StartOpenIDLogin(); window.location.href = res.url; } catch (e) { console.error(e); setError("Failed to initialize OpenID login"); } }; if (loading) return ( <> <CircularProgress /> </> ); return ( <> {error && ( <Alert style={{ width: "100%" }} severity="error"> {error} </Alert> )} {ServerApi.Config.local_auth_enabled && ( <> <Typography component="h2" variant="body1"> Local authentication </Typography> <Box component="form" noValidate onSubmit={handleLoginSubmit} sx={{ mt: 1 }} > <TextField margin="normal" required fullWidth id="username" label="Username" name="username" value={username} onChange={(e) => { setUsername(e.target.value); }} autoComplete="username" autoFocus /> <FormControl fullWidth variant="outlined"> <InputLabel htmlFor="password">Password</InputLabel> <OutlinedInput required fullWidth name="password" label="Password" type={showPassword ? "text" : "password"} id="password" value={password} onChange={(e) => { setPassword(e.target.value); }} autoComplete="current-password" endAdornment={ <InputAdornment position="end"> <Tooltip title="Show password"> <IconButton aria-label="toggle password visibility" onClick={handleClickShowPassword} onMouseDown={handleMouseDownPassword} edge="end" > {showPassword ? <VisibilityOffIcon /> : <VisibilityIcon />} </IconButton> </Tooltip> </InputAdornment> } /> </FormControl> <Button type="submit" fullWidth variant="contained" sx={{ mt: 3, mb: 2 }} disabled={!canSubmit} > Login </Button> </Box> </> )} <div> {ServerApi.Config.oidc_auth_enabled && ( <Button style={{ textAlign: "center", width: "100%", marginTop: "20px" }} onClick={() => authWithOpenID()} > Authenticate using OpenID </Button> )} </div> </> ); }