feat: émettrice SSO draco_session — cookie partagé .dracodev.net, renouvellement et révocation (#216)
This commit is contained in:
@@ -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).
|
||||
Reference in New Issue
Block a user