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
TenantMiddleware¶
- class src.module.tenant.infrastructure.middleware.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) :JWT : extrait
tenant_iddu claim JWTHeader : lit le header
X-Tenant-ID
Étapes du middleware :
Vérifie si le chemin est exempt (auth, invitations/accept)
Résout le tenant depuis JWT ou header
Valide que le tenant est actif
Vérifie la membership de l’utilisateur (sauf superuser)
Active le backend d’isolation (schema/RLS/FK)
Configure les contextvars (tenant, user, membership, correlation_id)
Traite la requête
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 :
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 introuvable403 Forbidden: tenant suspendu/archivé ou pas de membership
Exemple de requête :
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).