========================== Tenant — Middleware ========================== Le middleware de résolution du tenant est le point d'entrée central du système multi-tenant. Il résout le tenant, valide l'accès, et configure le contexte pour toute la durée de la requête. Module : ``src.module.tenant.infrastructure.middleware`` .. module:: src.module.tenant.infrastructure.middleware :synopsis: Middleware de résolution et activation du tenant TenantMiddleware ================ .. class:: TenantMiddleware Middleware dual WSGI/ASGI qui résout le tenant à partir du JWT ou du header HTTP et configure le contexte d'isolation. **Ordre de résolution** (configurable via ``TENANT_RESOLUTION_ORDER``) : 1. **JWT** : extrait ``tenant_id`` du claim JWT 2. **Header** : lit le header ``X-Tenant-ID`` **Étapes du middleware :** 1. Vérifie si le chemin est exempt (auth, invitations/accept) 2. Résout le tenant depuis JWT ou header 3. Valide que le tenant est actif 4. Vérifie la membership de l'utilisateur (sauf superuser) 5. Active le backend d'isolation (schema/RLS/FK) 6. Configure les contextvars (tenant, user, membership, correlation_id) 7. Traite la requête 8. Désactive le backend et réinitialise les contextvars **Chemins exempts :** Les routes d'authentification et d'acceptation d'invitation ne nécessitent pas de contexte tenant : - ``/api/v1/auth/register/`` - ``/api/v1/auth/login/`` - ``/api/v1/auth/refresh/`` - ``/api/v1/invitations/accept/`` - ``/admin/`` **Configuration dans settings.py :** .. code-block:: python MIDDLEWARE = [ ... 'django.contrib.auth.middleware.AuthenticationMiddleware', 'src.module.tenant.infrastructure.middleware.TenantMiddleware', # Après AuthenticationMiddleware ... ] **Réponses d'erreur :** - ``401 Unauthorized`` : pas de tenant identifié - ``404 Not Found`` : tenant introuvable - ``403 Forbidden`` : tenant suspendu/archivé ou pas de membership **Exemple de requête :** .. code-block:: http GET /api/v1/products/ HTTP/1.1 Authorization: Bearer eyJ... (contient claim tenant_id) X-Tenant-ID: acme-corp (fallback si pas de JWT) Compatibilité ASGI ================== Le middleware détecte automatiquement le mode ASGI vs WSGI et gère les contextvars correctement dans les deux cas. Sous ASGI, les contextvars sont scopés par coroutine (conforme au ticket Django #32815).