All checks were successful
continuous-integration/drone/push Build is passing
178 lines
4.7 KiB
TypeScript
178 lines
4.7 KiB
TypeScript
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 required 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>
|
|
</>
|
|
);
|
|
}
|