Can authenticate using password
This commit is contained in:
		@@ -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
 | 
				
			||||||
   *
 | 
					   *
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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>
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user