# #216 — Émettrice SSO `draco_session` (Fusion ACC-3.1) Statut : ✅ livré le 2026-10-21 (2.71.0) ## Contexte Programme Fusion (accès centralisé ObsiGate / Flowdeck / TikTak / Kanbante), couche 2 — identité. Décision **C2** de Bruno (2026-10-20) : cookie de session partagé `draco_session` sous `.dracodev.net` (architecture v2.0 §5.2, option 2a). ObsiGate est l'**unique émettrice** ; les autres applications valideront le jeton localement (ACC-3.2, leurs tickets respectifs) et redirigeront leur connexion vers `GET /sso/authorize`. ## Ce qui est livré - **`backend/draco_session.py`** — émission et vérification du JWS (HS256, jose) : claims minimaux `sub` (id utilisateur ObsiGate), `name`, `iat`, `exp` (**12 h**), `jti` ; **aucun rôle** dans le jeton. Rotation : `DRACO_SESSION_SECRET_PREV` accepté en vérification pendant la fenêtre de bascule. - **Émission à la connexion** : `_issue_tokens` (auth/router.py) pose le cookie à chaque émission de jetons natifs (mot de passe ou MFA) — attributs `Domain=.dracodev.net; Path=/; HttpOnly; Secure (si la requête est en https, via `is_secure_cookies`); SameSite=Lax`. - **Renouvellement glissant** : `DracoRenewalMiddleware` (ASGI pur, aucun tamponnage des flux SSE/WS) — si la requête porte un cookie d'accès natif valide et que le cookie draco est absent, invalide ou à moins de 2 h de son expiration, un cookie frais est ajouté à la réponse. - **Déconnexion globale** : le logout révoque le `jti` draco (en plus des jetons natifs) et efface le cookie du domaine parent. Révocations persistées dans `data/draco_revoked.json` (jti → exp, purge des expirés), exposées aux autres apps par **`GET /api/sso/revocations`** protégé par la clé machine `DRACO_SSO_MACHINE_KEY` (en-tête `X-Draco-Machine-Key`, 404 si la clé n'est pas configurée) — cache 60 s côté consommatrices. - **`GET /sso/authorize?return_to=`** : valide le `return_to` (https + `dracodev.net` ou sous-domaine, liste blanche stricte anti open-redirect) puis redirige vers la connexion ObsiGate ; après connexion, `frontend/js/auth.js` repart vers l'application demanderesse (re-validation du domaine côté client). - **Inerte par défaut** : sans `DRACO_SESSION_SECRET`, aucun cookie n'est émis et le comportement d'ObsiGate est inchangé. ## Configuration (déploiement) | Variable | Rôle | |---|---| | `DRACO_SESSION_SECRET` | Secret partagé de signature — **le même** devra être configuré dans Flowdeck et TikTak pour leurs validateurs ACC-3.2. À générer fort (ex. 64 hex) et à ne jamais committer. | | `DRACO_SESSION_SECRET_PREV` | Ancien secret pendant une rotation (24 h). | | `DRACO_SESSION_COOKIE_DOMAIN` | Domaine du cookie (défaut `.dracodev.net` ; vide = hôte seul, dev local). | | `DRACO_SSO_MACHINE_KEY` | Clé machine de lecture des révocations, à partager avec les apps consommatrices. | ⚠️ **Déploiement** : la fonction ne s'active qu'au redéploiement d'ObsiGate ≥ 2.71.0 **avec** `DRACO_SESSION_SECRET` posé dans l'environnement du conteneur. Le SSO complet exige ensuite les validateurs ACC-3.2 (ObsiGate #9, Flowdeck #21, TikTak #213). ## Tests `tests/test_draco_session.py` — 22 tests : claims minimaux, durée de vie, falsification, expiration, rotation, révocation, fenêtre de renouvellement, liste blanche `return_to` (8 cas), cookie posé à la connexion (attributs), inactivité sans secret, révocation au logout, renouvellement glissant via middleware, endpoint de révocations (404/401/200).