Can authenticate using password

This commit is contained in:
Pierre HUBERT 2023-06-13 10:42:28 +02:00
parent ae84ae8822
commit fc1104ec89
2 changed files with 144 additions and 24 deletions

View File

@ -9,6 +9,13 @@ export enum CreateAccountResult {
Error, Error,
} }
export enum PasswordLoginResult {
TooManyRequests,
InvalidCredentials,
Success,
Error,
}
export interface CheckResetTokenResponse { export interface CheckResetTokenResponse {
name: string; name: string;
} }
@ -65,6 +72,40 @@ export class AuthApi {
} }
} }
/**
* Authenticate using an email and a password
*
* @param mail The email address to use
* @param password The password to use
*/
static async LoginWithPassword(
mail: string,
password: string
): Promise<PasswordLoginResult> {
const res = await APIClient.exec({
uri: "/auth/password_login",
method: "POST",
allowFail: true,
jsonData: {
mail: mail,
password: password,
},
});
switch (res.status) {
case 429:
return PasswordLoginResult.TooManyRequests;
case 401:
return PasswordLoginResult.InvalidCredentials;
case 200:
case 201:
sessionStorage.setItem(TokenStateKey, res.data.token);
return PasswordLoginResult.Success;
default:
return PasswordLoginResult.Error;
}
}
/** /**
* Start OpenID login * Start OpenID login
* *

View File

@ -1,13 +1,24 @@
import { Alert, CircularProgress } from "@mui/material"; import {
Alert,
CircularProgress,
FormControl,
IconButton,
InputAdornment,
InputLabel,
OutlinedInput,
Tooltip,
} from "@mui/material";
import Box from "@mui/material/Box"; import Box from "@mui/material/Box";
import Button from "@mui/material/Button"; import Button from "@mui/material/Button";
import Grid from "@mui/material/Grid"; import Grid from "@mui/material/Grid";
import TextField from "@mui/material/TextField"; import TextField from "@mui/material/TextField";
import Typography from "@mui/material/Typography"; import Typography from "@mui/material/Typography";
import * as React from "react"; import * as React from "react";
import { AuthApi } from "../../api/AuthApi"; import { AuthApi, PasswordLoginResult } from "../../api/AuthApi";
import { ServerApi } from "../../api/ServerApi"; import { ServerApi } from "../../api/ServerApi";
import { Link } from "react-router-dom"; import { Link, useNavigate } from "react-router-dom";
import { VisibilityOff, Visibility } from "@mui/icons-material";
import { useSetAtom } from "jotai";
/** /**
* Login form * Login form
@ -16,13 +27,55 @@ export function LoginRoute(): React.ReactElement {
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const [error, setError] = React.useState<string | null>(null); const [error, setError] = React.useState<string | null>(null);
const handleSubmit = (event: React.FormEvent<HTMLFormElement>) => { const setAuth = useSetAtom(AuthApi.authStatus);
const navigate = useNavigate();
const [mail, setMail] = React.useState("");
const [password, setPassword] = React.useState("");
const canSubmit = mail.length > 0 && password.length > 0;
const [showPassword, setShowPassword] = React.useState(false);
const handleClickShowPassword = () => setShowPassword((show) => !show);
const handleMouseDownPassword = (
event: React.MouseEvent<HTMLButtonElement>
) => {
event.preventDefault(); event.preventDefault();
const data = new FormData(event.currentTarget); };
console.log({
email: data.get("email"), const handleLoginSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
password: data.get("password"), event.preventDefault();
}); if (!canSubmit) return;
try {
const res = await AuthApi.LoginWithPassword(mail, password);
switch (res) {
case PasswordLoginResult.TooManyRequests:
setError(
"Trop de tentatives de connection. Veuillez réessayer ultérieurement."
);
break;
case PasswordLoginResult.InvalidCredentials:
setError("Identifiants saisis invalides !");
break;
case PasswordLoginResult.Success:
navigate("/");
setAuth(true);
break;
case PasswordLoginResult.Error:
setError("Echec de la connexion !");
break;
}
} catch (e) {
console.error(e);
setError("Echec de l'authentification !");
}
setLoading(false);
}; };
const authWithProvider = async (id: string) => { const authWithProvider = async (id: string) => {
@ -46,9 +99,7 @@ export function LoginRoute(): React.ReactElement {
return ( return (
<> <>
{error === null ? ( {error && (
<></>
) : (
<Alert style={{ width: "100%" }} severity="error"> <Alert style={{ width: "100%" }} severity="error">
{error} {error}
</Alert> </Alert>
@ -57,7 +108,12 @@ export function LoginRoute(): React.ReactElement {
<Typography component="h2" variant="body1"> <Typography component="h2" variant="body1">
Connexion Connexion
</Typography> </Typography>
<Box component="form" noValidate onSubmit={handleSubmit} sx={{ mt: 1 }}> <Box
component="form"
noValidate
onSubmit={handleLoginSubmit}
sx={{ mt: 1 }}
>
<TextField <TextField
margin="normal" margin="normal"
required required
@ -65,24 +121,47 @@ export function LoginRoute(): React.ReactElement {
id="email" id="email"
label="Adresse mail" label="Adresse mail"
name="email" name="email"
value={mail}
onChange={(e) => setMail(e.target.value)}
autoComplete="email" autoComplete="email"
autoFocus autoFocus
/> />
<TextField
margin="normal" <FormControl fullWidth variant="outlined">
required <InputLabel htmlFor="password">Mot de passe</InputLabel>
fullWidth <OutlinedInput
name="password" required
label="Mot de passe" fullWidth
type="password" name="password"
id="password" label="Mot de passe"
autoComplete="current-password" type={showPassword ? "text" : "password"}
/> id="password"
value={password}
onChange={(e) => setPassword(e.target.value)}
autoComplete="current-password"
endAdornment={
<InputAdornment position="end">
<Tooltip title="Afficher le mot de passe">
<IconButton
aria-label="toggle password visibility"
onClick={handleClickShowPassword}
onMouseDown={handleMouseDownPassword}
edge="end"
>
{showPassword ? <VisibilityOff /> : <Visibility />}
</IconButton>
</Tooltip>
</InputAdornment>
}
/>
</FormControl>
<Button <Button
type="submit" type="submit"
fullWidth fullWidth
variant="contained" variant="contained"
sx={{ mt: 3, mb: 2 }} sx={{ mt: 3, mb: 2 }}
disabled={!canSubmit}
> >
Connexion Connexion
</Button> </Button>
@ -117,7 +196,7 @@ export function LoginRoute(): React.ReactElement {
style={{ textAlign: "center", width: "100%", marginTop: "20px" }} style={{ textAlign: "center", width: "100%", marginTop: "20px" }}
onClick={() => authWithProvider(p.id)} onClick={() => authWithProvider(p.id)}
> >
Connection avec {p.name} Connexion avec {p.name}
</Button> </Button>
))} ))}
</div> </div>