Apparence
13. Rôles, sécurité & couches d'enforcement
👔 En clair
Qui a le droit de faire quoi, et comment l'application l'empêche techniquement. Principe : chaque client ne voit que ses propres données, et les droits sont vérifiés côté serveur (pas seulement en masquant des boutons). Le détail ci-dessous est technique.
v2. L'autorité de sécurité se déplace des RLS PostgreSQL (v1) vers une couche applicative Convex : chaque fonction serveur passe par des wrappers authentifiés (
zQuery/zMutation) qui appliquent RBAC + ABAC. L'isolation multi-tenant est portée parorganizationIdet, optionnellement, un RLS helperconvex-helpersuniforme.
13.1 Trois couches de défense
- Couche 1 — UI (
features/<x>/constants, héritière deutils/roleGuard.js) : masque les boutons selon le rôle. Jamais une sécurité. - Couche 2 — Fonctions Convex (
packages/backend/convex/lib/authz.ts) : chaque mutation part des wrapperszMutation/zQuery(functions.ts) qui injectentctx.user(Better Auth) et valident les arguments (Zod), puis vérifientcanInContext(user, action, resource)(rôle et organisation) avant d'agir. - Couche 3 — RLS applicative :
wrapDatabaseReader/wrapDatabaseWriter(convex-helpers/server/rowLevelSecurity) applique uniformément le filtreorganizationId === user.organizationIdsur toutes les collections. C'est l'équivalent Convex des RLS Postgres — et il corrige l'incohérence v1 (les tablesetapes/anomaliesétaientUSING(true), non org-scopées).
ℹ️ Différence clé avec la v1 : Convex n'a pas de RLS déclaratif comme PostgreSQL. Le filtrage par tenant est explicite dans le code (hook ABAC / RLS helper) — d'où l'intérêt de le poser dès le socle (
organizationIddans le schéma +canInContext), pour éviter un retrofit risqué feature par feature (cf.CLAUDE.md, arbitrage multi-tenant).
13.2 Authentification & session
- Better Auth via
@convex-dev/better-auth(packages/backend/convex/auth.ts) : email + mot de passe (emailAndPassword.enabled),requireEmailVerification: false(configurable). - Handler d'auth exposé côté Next.js :
app/api/auth/[...all]/route.ts(+lib/auth-client.ts,lib/auth-server.ts). Pas de middleware de rafraîchissement de cookie type v1. - L'identité courante (+
organizationId) provient deauthComponent.getAuthUser(ctx)— exposée par la querygetCurrentUser(remplace leGET /api/auth/mede v1).
13.3 Secrets & privilèges
- Variables d'environnement Convex (ex.
SITE_URL, clés Better Auth), non versionnées (.env). - Opérations privilégiées : en v1, la clé service-role Supabase contournait la RLS. En v2, l'équivalent est l'usage des fonctions internes Convex (
internalMutation/internalQuery) ou d'actions admin — leur surface doit être auditée et jamais exposée au client.
❓ À CLARIFIER : recenser les fonctions Convex qui opèrent hors contexte utilisateur (internes/admin, bypass ABAC) et justifier chacune. Voir Q7.