Add support for authentication
This commit is contained in:
parent
8defc104c6
commit
44d565c6da
@ -24,7 +24,7 @@
|
||||
work correctly both with client-side routing and a non-root public URL.
|
||||
Learn how to configure a non-root public URL by running `npm run build`.
|
||||
-->
|
||||
<title>React App</title>
|
||||
<title>VirtWeb</title>
|
||||
</head>
|
||||
<body>
|
||||
<noscript>You need to enable JavaScript to run this app.</noscript>
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"short_name": "React App",
|
||||
"name": "Create React App Sample",
|
||||
"short_name": "VirtWeb",
|
||||
"name": "Virtual machines management",
|
||||
"icons": [
|
||||
{
|
||||
"src": "favicon.ico",
|
||||
|
@ -37,7 +37,6 @@ export class AuthApi {
|
||||
await APIClient.exec({
|
||||
uri: "/auth/local",
|
||||
method: "POST",
|
||||
allowFail: true,
|
||||
jsonData: {
|
||||
username: username,
|
||||
password: password,
|
||||
|
@ -1,3 +1,167 @@
|
||||
export function LoginRoute() {
|
||||
return <></>;
|
||||
import { Visibility, VisibilityOff } from "@mui/icons-material";
|
||||
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 Grid from "@mui/material/Grid";
|
||||
import TextField from "@mui/material/TextField";
|
||||
import Typography from "@mui/material/Typography";
|
||||
import * as React from "react";
|
||||
import { Link, 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 ? <VisibilityOff /> : <Visibility />}
|
||||
</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>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user