Authentification, compte et 2FA
Spécification du cœur fonctionnel actuel de
telaria: comptes utilisateurs, connexion, vérification d'e-mail, mot de passe oublié, double authentification (2FA) et accès API par token. Reflète l'implémentation existante (tickets CDX-108..115) et le schéma cible de la phase de clean. Statut : spec de référence (doc-design).
1. Objectif et périmètre
telaria est, à ce stade, une application centrée sur le compte utilisateur (le reste — CMS, doc web, IA — vient se greffer dessus). Cette spec documente l'authentification, la gestion de compte, la 2FA et l'accès API.
Dans le périmètre : inscription, vérification d'e-mail, connexion, mot de passe oublié, 2FA, rôles/autorisations, token API, sécurité transverse (CSRF, rate limiting, hardening session).
Hors périmètre : CMS (telaria-cms.md), backoffice générique (telaria-admin.md), IA (ia-*.md).
2. Modèle de données (cible)
User
Implémente UserInterface, PasswordAuthenticatedUserInterface, TwoFactorInterface (scheb 2FA e-mail), TrustedDeviceInterface.
| Champ | Type | Notes |
|---|---|---|
id |
int | PK |
email |
string | identifiant de connexion ; index unique en base (au-delĂ du UniqueEntity applicatif) |
password |
string | haché (algorithme auto Symfony) |
roles |
json | ROLE_USER par défaut, ROLE_ADMIN pour le backoffice |
isVerified |
bool | e-mail vérifié |
apiToken |
string(128) nullable | hash SHA-512 (128 hex) — voir §8 |
is2faEnabled |
bool | 2FA activée par l'utilisateur |
authCode |
string nullable | code e-mail 2FA en cours |
totpSecret |
string nullable | réservé au TOTP (prévu plus tard) — champ conservé même si inactif |
trustedVersion |
int | versioning des appareils de confiance (trusted-device) |
ResetPasswordRequest
Entité du bundle SymfonyCasts ResetPassword (jeton, expiration, user).
Deltas de migration (phase clean, VPS-gated)
- Retirer
username(+ indexUNIQ_IDENTIFIER_USERNAME, accesseurs, usage fixtures) — vestige, la connexion se fait par e-mail. api_token: VARCHAR(255) → VARCHAR(128) (l'entité déclare 128 ; la migrationVersion20251209174030avait créé 255 → sinondoctrine:schema:validateéchoue).- Ajouter l'index unique sur
user.email(aucune contrainte DB ne le garantit aujourd'hui). DROP TABLE recipe+category(tables orphelines du démo supprimé).
Workflow : modifier entité + fixtures, puis sur le VPS
doctrine:migrations:diff+ migration manuelle pour les tables orphelines, puisdoctrine:schema:validate.
3. Inscription et vérification d'e-mail
RegistrationController: formulaire d'inscription (routeregister), création du compte (isVerified=false).EmailVerifier+ SymfonyCasts VerifyEmail : envoi d'un lien signé ; routesverify/email(confirmation) etverify/success.- À la vérification :
isVerified=true.
4. Connexion
SecurityController: connexion par e-mail + mot de passe, CSRF activé, user provider Doctrine suremail.- Redirection post-login par rôle (v0.5.0) :
ROLE_ADMIN→ back-office/admin;ROLE_USER(lecteur) → espace de lecture/lecteur. Cf. §7. - Session durcie en production (
.env.prod.dist, cf. CDX-114).
5. Mot de passe oublié
ResetPasswordController(SymfonyCasts ResetPassword) : route/password(demande) et/reset/{token}(réinitialisation), jeton à expiration.
6. Double authentification (2FA)
- Actif :
scheb/2fa-email(code envoyé par e-mail ;authCode). - Trusted device :
scheb/2fa-trusted-device(trustedVersion) — ne pas redemander la 2FA sur un appareil de confiance. - TOTP : prévu (
totpSecretréservé) ;scheb/2fa-totpà installer le moment venu. - Pilotage utilisateur : route
/2fa/toggle(activation/désactivation,is2faEnabled).
7. RĂ´les et autorisations
ROLE_USER(défaut) — lecteur : accès à l'espace de lecture de la veille/lecteur(cf.ia-veille.md; suivi lu/non-lu par utilisateur, filtres/tri, arborescence des sources).ROLE_ADMIN(backoffice).- Firewall
/adminrĂ©servĂ© ĂROLE_ADMIN(CDX-109). - Voters pour la propriĂ©tĂ© des ressources (CDX-113).
- CSRF sur les actions sensibles (suppressions — CDX-110).
7.1 Comptes lecteur — créés par l'admin (v0.5.0)
- En V1, les comptes lecteur (
ROLE_USER) sont créés par l'administrateur via/admin/utilisateurs(UserController) — aujourd'hui création + liste (index+new) ; édition/suppression à venir. Pas d'auto-inscription ouverte pour l'espace lecteur. - La vitrine publique
/veille(billets validés, lecture anonyme) reste conservée et distincte de l'espace lecteur authentifié/lecteur. - Point ouvert : chantier « gestion des utilisateurs » (auto-inscription + validation) acté au backlog — cf. §13.
8. Accès API (token)
- Authentification par token :
APIAuthenticatorvalide le token en comparant son hash SHA-512 ĂapiToken(le token en clair n'est jamais stockĂ©). - Header canonique =
X-Auth-Token(exclusif) : l'authenticator ne se déclenche que sur ce header (supports()/authenticate(), vérifié Lead inbox codexia #47,src/Security/APIAuthenticator.php).Bearern'est pas accepté (un headerBearerne passe passupports()→ 401). Le schémaBearerautrefois évoqué dans cette spec est obsolète — pas « les deux ». Un éventuel supportBearerserait une évolution de l'authenticator, pas l'état actuel. Pas-à -pas :tutos/securite-token-api-symfony.md. - Génération : commande
app:user:generate-token(CDX-108) — affiche le token en clair une seule fois, stocke le hash. - Endpoints :
/api,/me(profil courant),/check/email(disponibilité). - Rate limiting sur l'API (CDX-111).
-
Le modèle actuel est un token opaque haché (simple, robuste). OAuth 2.1 / JWT (cf.
rfc/README.md, en stand-by) restent une évolution possible, non requise à ce stade.
9. Sécurité transverse
- Rate limiting : connexion, contact, API (
symfony/rate-limiter, CDX-111). - Uploads sécurisés : vérification du type MIME réel + anti path traversal (
FileUploader, CDX-112). - CSRF, hardening session (prod), hachage des mots de passe et des tokens.
10. RGPD et accessibilité
- Données personnelles : e-mail (identifiant). Minimisation : pas de
usernamesuperflu. Voir la veille RGPD. - Accessibilité RGAA AA des formulaires (inscription, connexion, reset, 2FA) : labels explicites, erreurs reliées (
aria-describedby), focus visible.
11. Tests (cas concrets)
- Unicité e-mail : deux comptes avec le même e-mail → refus (contrainte DB et
UniqueEntity). - Token API : un token valide (hash SHA-512 correspondant) authentifie ; un token inconnu → 401 ; le clair n'est jamais persisté.
- Firewall admin : un
ROLE_USERsur/admin→ refus (403/redirection). - Reset : jeton expiré → refus ; jeton valide → changement de mot de passe.
- 2FA : avec
is2faEnabled=true, la connexion exige le code e-mail ; un appareil de confiance (trustedVersion courant) ne le redemande pas. - Rate limiting : dépassement sur login/API/contact → réponse de limitation.
- Vérification e-mail : lien signé valide →
isVerified=true; lien altéré → refus.
12. Production documentaire d'accompagnement (doctrine)
| Concept introduit | Forme | Emplacement | Statut |
|---|---|---|---|
| 2FA en Symfony (scheb : e-mail, TOTP, trusted device) | Tuto/fiche | 2fa-symfony-scheb.md |
✅ produit (e-mail + trusted ; TOTP prévu) |
| Auth API par token haché (SHA-512) | Tuto | securite-token-api-symfony.md |
✅ produit (implé confirmée Lead, inbox codexia #44) |
13. Points ouverts
- TOTP : quand activer
scheb/2fa-totp(le champtotpSecretest déjà réservé). - Évolution API : rester en token opaque haché, ou passer à OAuth 2.1 / JWT plus tard (RFC en stand-by).
- Politique de durée de vie / rotation des tokens API.
- Gestion des utilisateurs lecteur : en V1 les comptes
ROLE_USERsont créés par l'admin (cf. §7.1). Chantier ouvert : auto-inscription + validation (workflow d'enrôlement public pour l'espace lecteur).
Documents liés
- Architecture (modèle de données à resynchroniser) :
specs/architecture.md - Backoffice :
specs/telaria-admin.md - Sprint 01 (tickets CDX-108..115) :
pilotage/scrum/scrum-sprint-01.md - API accessible :
accessibility/api.md - Standards OAuth2/JWT (stand-by) :
rfc/README.md
Implémentation
| Aspect | Localisation |
|---|---|
| Bundle principal | telaria-app — domaine auth/compte |
| Entités | src/Entity/User.php, src/Entity/ResetPasswordRequest.php dans telaria-app |
| Controllers | src/Controller/RegistrationController.php, src/Controller/SecurityController.php, src/Controller/ResetPasswordController.php |
| Sécurité | src/Security/APIAuthenticator.php, src/Security/Voter/ |
| Commandes CLI | src/Command/UserGenerateTokenCommand.php (génération token SHA-512) |
| Config | config/security.yaml, config/packages/scheb_2fa.yaml |
| Tests | tests/Controller/RegistrationControllerTest.php (Ă documenter) |
Historique des décisions
| Version | Date | Décision |
|---|---|---|
| 1.0 | 2026-06-14 | Version initiale — première formalisation du versioning des specs. |
| — | 2026-06-05 | Spec alignée sur l'implémentation réelle (tickets CDX-108..115). Confirmation header canonique X-Auth-Token exclusif (Lead inbox codexia #47). |
| — | 2026-06-05 | Redirection post-login par rôle ajoutée : ROLE_ADMIN → /admin, ROLE_USER → /lecteur (v0.5.0). |
| — | 2026-06-05 | Gestion des utilisateurs lecteur : comptes ROLE_USER créés par admin uniquement en V1, pas d'auto-inscription publique. |