feat: Réinitialisation de mot de passe avec tokens sécurisés

Implémentation complète du flux de réinitialisation de mot de passe (Story 1.5):

Backend:
- Aggregate PasswordResetToken avec TTL 1h, UUID v7, usage unique
- Endpoint POST /api/password/forgot avec rate limiting (3/h par email, 10/h par IP)
- Endpoint POST /api/password/reset avec validation token
- Templates email (demande + confirmation)
- Repository Redis avec TTL 2h pour distinguer expiré/invalide

Frontend:
- Page /mot-de-passe-oublie avec message générique (anti-énumération)
- Page /reset-password/[token] avec validation temps réel des critères
- Gestion erreurs: token invalide, expiré, déjà utilisé

Tests:
- 14 tests unitaires PasswordResetToken
- 7 tests unitaires RequestPasswordResetHandler
- 7 tests unitaires ResetPasswordHandler
- Tests E2E Playwright pour le flux complet
This commit is contained in:
2026-02-01 23:15:01 +01:00
parent b7354b8448
commit affad287f9
71 changed files with 4829 additions and 222 deletions

View File

@@ -15,7 +15,7 @@ final readonly class CorrelationId
public static function generate(): self
{
return new self(Uuid::uuid4()->toString());
return new self(Uuid::uuid7()->toString());
}
public static function fromString(string $value): self

View File

@@ -19,7 +19,7 @@ abstract readonly class EntityId
public static function generate(): static
{
return new static(Uuid::uuid4());
return new static(Uuid::uuid7());
}
public static function fromString(string $value): static

View File

@@ -34,6 +34,8 @@ final readonly class TenantMiddleware implements EventSubscriberInterface
'/api/activation-tokens',
'/api/activate',
'/api/login',
'/api/password',
'/api/token',
'/_profiler',
'/_wdt',
'/_error',