feat: Connexion utilisateur avec sécurité renforcée
Implémente la Story 1.4 du système d'authentification avec plusieurs couches de protection contre les attaques par force brute. Sécurité backend : - Authentification JWT avec access token (15min) + refresh token (7j) - Rotation automatique des refresh tokens avec détection de replay - Rate limiting progressif par IP (délai Fibonacci après échecs) - Intégration Cloudflare Turnstile CAPTCHA après 5 tentatives - Alerte email à l'utilisateur après blocage temporaire - Isolation multi-tenant (un utilisateur ne peut se connecter que sur son établissement) Frontend : - Page de connexion avec feedback visuel des délais et erreurs - Composant TurnstileCaptcha réutilisable - Gestion d'état auth avec stockage sécurisé des tokens - Tests E2E Playwright pour login, tenant isolation, et activation Infrastructure : - Configuration Symfony Security avec json_login + jwt - Cache pools séparés (filesystem en test, Redis en prod) - NullLoginRateLimiter pour environnement de test (évite blocage CI) - Génération des clés JWT en CI après démarrage du backend
This commit is contained in:
@@ -0,0 +1,58 @@
|
||||
<?php
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\Administration\Infrastructure\Messaging;
|
||||
|
||||
use App\Administration\Domain\Event\CompteBloqueTemporairement;
|
||||
use Symfony\Component\Mailer\MailerInterface;
|
||||
use Symfony\Component\Messenger\Attribute\AsMessageHandler;
|
||||
use Symfony\Component\Mime\Email;
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Envoie un email d'alerte quand un compte est bloqué temporairement.
|
||||
*
|
||||
* @see Story 1.4 - T4: Email alerte lockout
|
||||
*/
|
||||
#[AsMessageHandler]
|
||||
final readonly class SendLockoutAlertHandler
|
||||
{
|
||||
public function __construct(
|
||||
private MailerInterface $mailer,
|
||||
private Environment $twig,
|
||||
private string $fromEmail = 'noreply@classeo.fr',
|
||||
) {
|
||||
}
|
||||
|
||||
public function __invoke(CompteBloqueTemporairement $event): void
|
||||
{
|
||||
$blockedForMinutes = (int) ceil($event->blockedForSeconds / 60);
|
||||
|
||||
$htmlContent = $this->twig->render('email/lockout_alert.html.twig', [
|
||||
'email' => $event->email,
|
||||
'ipAddress' => $event->ipAddress,
|
||||
'failedAttempts' => $event->failedAttempts,
|
||||
'blockedForMinutes' => $blockedForMinutes,
|
||||
'occurredOn' => $event->occurredOn,
|
||||
]);
|
||||
|
||||
$textContent = $this->twig->render('email/lockout_alert.txt.twig', [
|
||||
'email' => $event->email,
|
||||
'ipAddress' => $event->ipAddress,
|
||||
'failedAttempts' => $event->failedAttempts,
|
||||
'blockedForMinutes' => $blockedForMinutes,
|
||||
'occurredOn' => $event->occurredOn,
|
||||
]);
|
||||
|
||||
$email = (new Email())
|
||||
->from($this->fromEmail)
|
||||
->to($event->email)
|
||||
->subject('🔒 Alerte de sécurité - Tentatives de connexion suspectes')
|
||||
->html($htmlContent)
|
||||
->text($textContent)
|
||||
->priority(Email::PRIORITY_HIGH);
|
||||
|
||||
$this->mailer->send($email);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user