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>
 | |
|     </>
 | |
|   );
 | |
| }
 |