feat: émettrice SSO draco_session — cookie partagé .dracodev.net, renouvellement et révocation (#216)
CI / lint (push) Successful in 3m32s
CI / security (push) Successful in 2m18s
CI / test (push) Successful in 5m38s
CI / build (push) Successful in 3m52s
CI / e2e (push) Successful in 19m0s

This commit is contained in:
Spark
2026-10-20 23:33:52 -04:00
parent e5577b9996
commit cb80b08f77
17 changed files with 677 additions and 13 deletions
+24 -1
View File
@@ -1,6 +1,6 @@
# ObsiGate — Roadmap
> **Version :** 2.70.1 | **Dernière mise à jour :** 2026-10-20
> **Version :** 2.71.0 | **Dernière mise à jour :** 2026-10-20
> **Ce fichier ne contient que le travail à venir** (🔵 En cours + ⚪ Backlog) et un index compact
> vers les fonctionnalités livrées.
> - **Méthode de livraison à appliquer pour toute tâche : [DELIVERY_WORKFLOW.md](./DELIVERY_WORKFLOW.md)**
@@ -56,6 +56,29 @@
bandeau « Tâche TikTak projetée » + « Ouvrir dans TikTak »
- [x] i18n FR/EN (clés `draco.links.*`), tests `test_external_refs.py`
### 216. Fusion ACC-3.1 — émettrice SSO `draco_session`
- **Effort :** ~1 jour | **Impact :** 🟢 | **Ouvert :** 2026-10-21 | **Statut :** ✅ livré le 2026-10-21 (2.71.0)
- **Fiche :** [features/fusion-acc31-sso-draco-session-216.md](./features/fusion-acc31-sso-draco-session-216.md)
- **Description :** couche 2 (identité) du programme **Fusion** — décision C2
de Bruno : cookie de session partagé sous `.dracodev.net` (architecture
v2.0 §5.2, option 2a). ObsiGate est l'unique émettrice ; les autres apps
valideront localement (ACC-3.2). Issue Gitea ObsiGate #8 ; épopée
Projets/fusion-trois-apps#7.
- **Sous-tâches :**
- [x] Module `backend/draco_session.py` : JWS HS256, claims minimaux
(sub, name, iat, exp 12 h, jti — aucun rôle), rotation par
`DRACO_SESSION_SECRET_PREV`, inerte sans `DRACO_SESSION_SECRET`
- [x] Émission du cookie à chaque `_issue_tokens` (mot de passe ou MFA)
- [x] Renouvellement glissant (middleware ASGI `DracoRenewalMiddleware`,
fenêtre 2 h, conditionné au cookie d'accès natif valide)
- [x] Déconnexion globale : révocation du jti (`data/draco_revoked.json`)
+ effacement du cookie ; `GET /api/sso/revocations` sous clé machine
- [x] `GET /sso/authorize` : `return_to` en liste blanche
`*.dracodev.net` (anti open-redirect), retour post-connexion
re-validé côté frontend (`auth.js`)
- [x] Tag OpenAPI « SSO », tests `test_draco_session.py` (22)
### 196. Partage dirigé entre utilisateurs (document → utilisateur)
- **Effort :** 0,5-1 jour | **Impact :** 🟡 | **Ouvert :** 2026-10-10 | **Statut :** ✅ livré le 2026-10-10
@@ -0,0 +1,67 @@
# #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).