feat(auth): real JWT login, RBAC routes, RGPD cookie banner + users/journal admin
Build & Deploy / build (push) Successful in 11s

This commit is contained in:
neckfire
2026-06-20 12:54:01 +02:00
parent 4466b38f75
commit 7a4206a522
12 changed files with 693 additions and 80 deletions
+41 -21
View File
@@ -1,14 +1,23 @@
/* Page de connexion : formulaire + SSO Windows */
/* Page de connexion : formulaire avec authentification JWT + captcha anti-bot */
import { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import { useAuth } from '../../context/AuthContext';
import { getHealth } from '../../services/api';
import styles from './Login.module.css';
/* Génère deux entiers aléatoires pour le captcha additif */
function genererCaptcha() {
return {
x: Math.floor(Math.random() * 10) + 1,
y: Math.floor(Math.random() * 10) + 1,
};
}
export default function Login() {
const [identifiant, setIdentifiant] = useState('');
const [motDePasse, setMotDePasse] = useState('');
const [captcha, setCaptcha] = useState(() => genererCaptcha());
const [reponseCaptcha, setReponseCaptcha] = useState('');
const [loading, setLoading] = useState(false);
const [erreur, setErreur] = useState('');
@@ -18,25 +27,29 @@ export default function Login() {
async function handleSubmit(e) {
e.preventDefault();
setErreur('');
setLoading(true);
/* Vérification anti-bot du captcha avant tout appel réseau */
if (parseInt(reponseCaptcha, 10) !== captcha.x + captcha.y) {
setErreur('Réponse au captcha incorrecte.');
setCaptcha(genererCaptcha());
setReponseCaptcha('');
return;
}
setLoading(true);
try {
/* Vérification de la disponibilité de l'API avant connexion */
await getHealth();
login({ nom: identifiant || 'Utilisateur', role: 'user', token: 'session' });
await login(identifiant, motDePasse);
navigate('/dashboard');
} catch {
setErreur('Impossible de contacter le serveur. Vérifiez que l\'API est démarrée sur le port 8000.');
} catch (err) {
/* Message générique en cas d'identifiants invalides ou d'erreur serveur */
setErreur(err.message === 'Identifiants invalides' ? 'Identifiants invalides' : (err.message || 'Identifiants invalides'));
setCaptcha(genererCaptcha());
setReponseCaptcha('');
} finally {
setLoading(false);
}
}
function handleSso() {
/* Connexion SSO Windows : même vérification API */
handleSubmit({ preventDefault: () => {} });
}
return (
<div className={styles.page}>
<div className={styles.card}>
@@ -73,6 +86,21 @@ export default function Login() {
/>
</div>
<div className={styles.field}>
<label className={styles.label} htmlFor="captcha">
Vérification : combien font {captcha.x} + {captcha.y} ?
</label>
<input
id="captcha"
type="number"
className={styles.input}
value={reponseCaptcha}
onChange={e => setReponseCaptcha(e.target.value)}
placeholder="Votre réponse"
autoComplete="off"
/>
</div>
{erreur && <p className={styles.erreur}>{erreur}</p>}
<button type="submit" className={styles.btnPrimary} disabled={loading}>
@@ -80,14 +108,6 @@ export default function Login() {
</button>
</form>
<div className={styles.divider}>
<span>ou</span>
</div>
<button type="button" className={styles.btnSecondary} onClick={handleSso} disabled={loading}>
Connexion Windows SSO
</button>
<div className={styles.links}>
<a href="#" className={styles.forgotLink}>Mot de passe oublié ?</a>
</div>