Dérivez l'appartenance côté serveur
Un ID d'enregistrement identifie quelque chose ; il ne prouve pas qui peut le lire. supastack délimite les enregistrements utilisateur par workspaceId. Le serveur résout l'espace de travail de l'utilisateur authentifié et inclut ce périmètre lors de la lecture ou de la modification d'un enregistrement.
Ne traitez pas un ID d'espace envoyé par le navigateur comme une preuve d'appartenance. Le navigateur peut demander une action, mais le contexte serveur de confiance doit établir de qui il s'agit. Cette règle doit valoir pour le statut de tâche, la configuration fournisseur sauvegardée, les enregistrements d'usage et chaque ressource appartenant au produit. Les layouts et composants peuvent filtrer ce qui s'affiche, mais le rendu n'est pas une autorisation — le contrôle appartient à l'endroit où l'enregistrement est réellement lu ou écrit.
Tout le reste de cet article découle de cette règle : l'appartenance se dérive, elle ne se demande jamais. À l'instant où un workspaceId envoyé par le client est pris pour argent comptant quelque part, tous les autres contrôles deviennent de la décoration. L'article sur l'authentification couvre comment la session elle-même est établie en amont de cette frontière.
L'appartenance fait partie de la frontière
Le périmètre répond à quel espace un enregistrement appartient ; l'appartenance répond si cet utilisateur appartient à cet espace. Résolvez les deux côté serveur. supastack modélise des espaces personnels et d'organisation avec les rôles owner, admin et member — le guide des espaces de travail documente le modèle.
Les rôles ajoutent un second axe : un membre peut lire ce que seul un admin peut modifier. Gardez les deux contrôles distincts — « appartient à cet espace » et « peut effectuer cette action » — pour qu'une rétrogradation ou un retrait prenne effet à la même frontière que tout le reste. Invitations et retraits font partie du même cycle de vie, pas une réflexion d'admin après coup.
Les changements d'appartenance doivent aussi prendre effet immédiatement. La prochaine requête d'un membre retiré doit échouer à la frontière, pas quand un jeton en cache arrive à expiration — une raison de plus pour que le contrôle vive là où les enregistrements sont lus, plutôt que dans un identifiant émis plus tôt.
Rendez la reprise utile sans donner accès
Une URL de tâche permet à un utilisateur de revenir après un rafraîchissement. Elle est utile précisément parce qu'elle conserve une référence au travail. L'API authentifiée doit toujours vérifier que la tâche demandée appartient à l'espace courant avant de renvoyer statut ou résultats.
Le même principe s'applique à la reprise de soumission. Une clé d'idempotence aide à identifier une requête répétée et à éviter le travail en double. Ce n'est ni une session ni un jeton d'accès. Réessayer une soumission sauvegardée exige toujours l'authentification, l'appartenance à l'espace et le droit approprié.
Les chemins de commodité sont l'endroit où l'isolation s'érode en silence. Un lien qui aide un utilisateur légitime à reprendre son travail — après un rafraîchissement, depuis un e-mail de fin — doit passer par le même contrôle authentifié que toute autre requête. Le pipeline requête-résultat utilise des clés d'idempotence bornées à la tâche exactement sur ce principe.
Testez les requêtes qui doivent échouer
Un test qui laisse un propriétaire récupérer un enregistrement prouve le chemin nominal. Il ne prouve pas l'isolation. Associez-le à un second espace demandant le même enregistrement, une requête anonyme et une écriture tentant de substituer un autre propriétaire.
Affirmez le comportement externe autant que le résultat persisté. Une requête rejetée ne doit pas muter les données d'un autre espace, et une réponse d'erreur ne doit pas fuiter de secrets ni de détails bruts de base. Ces contrôles rendent la frontière révisable quand l'implémentation change.
- Un second espace demande le même ID d'enregistrement — rejeté.
- Une requête anonyme frappe le même endpoint — rejetée.
- Un membre retiré de l'espace réessaie — rejeté.
- Une écriture nomme un autre propriétaire ou espace — rejetée, sans rien muter.
- Les réponses d'erreur restent génériques — pas de secrets, pas de détails de schéma.
Portez la règle dans votre produit
Quand vous ajoutez une nouvelle table produit, décidez de son appartenance avant de construire l'écran. Incluez workspaceId dans son modèle de persistance, résolvez l'espace via la frontière serveur existante et utilisez ce contexte dans les opérations de liste comme de détail.
Les workers en arrière-plan demandent le même soin. Une tâche en file doit porter assez de contexte de confiance pour charger le bon enregistrement, tandis que les identifiants fournisseur restent sur le serveur. Un job planifié qui charge des lignes par simple ID reste à l'intérieur de la frontière. Une règle d'appartenance cohérente est plus facile à maintenir qu'une collection d'exceptions par route.
La surface API appartient à la même frontière. Les clés API d'espace et les webhooks sortants signés portent avec eux le périmètre de l'espace — une clé ne doit jamais devenir un moyen de lire à travers les tenants, et une charge de webhook ne doit jamais transporter les enregistrements d'un voisin. Le guide du module API couvre la forme de cette surface.
Questions fréquentes sur l'isolation des espaces
L'isolation multi-tenant soulève la même poignée de questions sur chaque projet. Voici celles qu'il vaut mieux trancher tôt.
- Le périmètre au niveau applicatif suffit-il, ou faut-il la sécurité au niveau ligne ? — Un périmètre appliqué côté serveur sur chaque lecture et écriture couvre le modèle de menace de la plupart des SaaS. Le RLS en base ajoute de la défense en profondeur mais ne remplace pas les contrôles à la frontière — les workers et les clés API ont toujours besoin de la même règle.
- Comment le partage entre espaces doit-il fonctionner ? — Modélisez-le explicitement — une invitation, une ligne d'appartenance — plutôt qu'en affaiblissant le périmètre. « Toute personne ayant le lien » doit être une fonctionnalité conçue avec ses propres contrôles, pas un accident.
- Qu'est-ce qui casse en premier dans les apps multi-tenant ? — Les chemins de commodité : liens de reprise, outillage admin, workers qui chargent des enregistrements par simple ID. Auditez chaque chemin de code qui touche un enregistrement sans passer par le périmètre normal.
- Comment ajouter un périmètre à une table existante ? — Ajoutez
workspaceId, remplissez-le, appliquez-le dans les lectures et écritures, puis touchez à l'UI. Livrez d'abord les tests négatifs pour que la frontière soit prouvée plutôt que supposée. - Les actions serveur ont-elles besoin de contrôles si la page est protégée ? — Oui. Les gardes de page masquent des écrans ; elles n'autorisent pas les opérations. Chaque action résout la session et l'espace indépendamment.