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:
@@ -17,6 +17,8 @@ services:
|
||||
Psr\Cache\CacheItemPoolInterface $activationTokensCache: '@activation_tokens.cache'
|
||||
# Bind users cache pool (no TTL - persistent data)
|
||||
Psr\Cache\CacheItemPoolInterface $usersCache: '@users.cache'
|
||||
# Bind refresh tokens cache pool (7-day TTL)
|
||||
Psr\Cache\CacheItemPoolInterface $refreshTokensCache: '@refresh_tokens.cache'
|
||||
# Bind named message buses
|
||||
Symfony\Component\Messenger\MessageBusInterface $eventBus: '@event.bus'
|
||||
Symfony\Component\Messenger\MessageBusInterface $commandBus: '@command.bus'
|
||||
@@ -76,3 +78,66 @@ services:
|
||||
App\Administration\Infrastructure\Messaging\SendActivationConfirmationHandler:
|
||||
arguments:
|
||||
$appUrl: '%app.url%'
|
||||
|
||||
# Audit log handler (uses dedicated audit channel)
|
||||
App\Administration\Infrastructure\Messaging\AuditLoginEventsHandler:
|
||||
arguments:
|
||||
$auditLogger: '@monolog.logger.audit'
|
||||
$appSecret: '%env(APP_SECRET)%'
|
||||
|
||||
# JWT Authentication
|
||||
App\Administration\Infrastructure\Security\JwtPayloadEnricher:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_jwt_created, method: onJWTCreated }
|
||||
|
||||
App\Administration\Infrastructure\Security\DatabaseUserProvider:
|
||||
arguments:
|
||||
$userRepository: '@App\Administration\Domain\Repository\UserRepository'
|
||||
|
||||
# Refresh Token Repository
|
||||
App\Administration\Domain\Repository\RefreshTokenRepository:
|
||||
alias: App\Administration\Infrastructure\Persistence\Redis\RedisRefreshTokenRepository
|
||||
|
||||
# Login handlers
|
||||
App\Administration\Infrastructure\Security\LoginSuccessHandler:
|
||||
tags:
|
||||
- { name: kernel.event_listener, event: lexik_jwt_authentication.on_authentication_success, method: onAuthenticationSuccess }
|
||||
|
||||
App\Administration\Infrastructure\Security\LoginFailureHandler:
|
||||
tags:
|
||||
- { name: security.authentication_failure_handler, firewall: api_login }
|
||||
|
||||
# Rate Limiter (délai Fibonacci + CAPTCHA + blocage IP)
|
||||
App\Shared\Infrastructure\RateLimit\LoginRateLimiter:
|
||||
arguments:
|
||||
$cache: '@cache.rate_limiter'
|
||||
|
||||
App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
|
||||
alias: App\Shared\Infrastructure\RateLimit\LoginRateLimiter
|
||||
|
||||
# Rate Limit Listener (vérifie le rate limit AVANT authentification)
|
||||
App\Shared\Infrastructure\RateLimit\LoginRateLimitListener:
|
||||
arguments:
|
||||
$rateLimiterCache: '@cache.rate_limiter'
|
||||
|
||||
# Turnstile CAPTCHA Validator
|
||||
# failOpen: true en dev (ne pas bloquer si API down), false en prod (sécurité)
|
||||
App\Shared\Infrastructure\Captcha\TurnstileValidator:
|
||||
arguments:
|
||||
$secretKey: '%env(TURNSTILE_SECRET_KEY)%'
|
||||
$failOpen: '%env(bool:default::TURNSTILE_FAIL_OPEN)%'
|
||||
|
||||
App\Shared\Infrastructure\Captcha\TurnstileValidatorInterface:
|
||||
alias: App\Shared\Infrastructure\Captcha\TurnstileValidator
|
||||
|
||||
# =============================================================================
|
||||
# Test environment overrides
|
||||
# =============================================================================
|
||||
when@test:
|
||||
services:
|
||||
# Use null rate limiter in test environment to avoid IP blocking during E2E tests
|
||||
App\Shared\Infrastructure\RateLimit\LoginRateLimiterInterface:
|
||||
alias: App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter
|
||||
|
||||
App\Shared\Infrastructure\RateLimit\NullLoginRateLimiter:
|
||||
autowire: true
|
||||
|
||||
Reference in New Issue
Block a user