Aller au contenu

Supabase raw_user_meta_data : une inscription devient Owner

Supabase

Voir en MarkdownOuvrir dans ClaudeOuvrir dans ChatGPTOuvrir dans Perplexity

La question est aussi copiée. Si le chat reste vide, collez-la.

En bref

raw_user_meta_data contient ce que le navigateur a passé à supabase.auth.signUp(). Sur une application en production, le trigger handle_new_user y lisait le rôle du nouvel utilisateur ; n’importe qui pouvait donc s’inscrire avec l’identifiant du rôle Owner et prendre le contrôle du compte. La correction : le rôle vient d’une table que seul le serveur écrit, remplie avant l’envoi de l’invitation.

C’était sur le suivi du temps d’un cabinet d’audit, une application Supabase où chaque règle d’accès vit dans la base de données. Le bug se trouvait dans le trigger qui crée un profil pour chaque nouvel utilisateur.

Que faisait le trigger ?

Simplifié, il ressemblait à ceci :

create function public.handle_new_user()
returns trigger
language plpgsql
security definer set search_path = ''
as $$
declare
  _role_id uuid := (new.raw_user_meta_data ->> 'role_id')::uuid;
begin
  insert into public.profiles (id, full_name, role_id)
  values (new.id, new.raw_user_meta_data ->> 'full_name', _role_id);
  return new;
end;
$$;

Le flux d’invitation y mettait le bon role_id, donc en usage normal tout fonctionnait. Mais raw_user_meta_data correspond aux options.data d’une inscription, et c’est le navigateur qui décide de ce qu’il contient :

await supabase.auth.signUp({
  email: 'me@example.com',
  password: '...',
  options: { data: { role_id: '<owner role uuid>' } },
})

Le contrôle de permission renvoyait vrai pour les owners avant de regarder quoi que ce soit d’autre, de sorte que ce seul champ donnait le contrôle total du compte. Le guide Users de Supabase indique lui-même que les métadonnées utilisateur ne doivent pas servir dans les politiques RLS ni dans la logique d’autorisation, car l’utilisateur peut les modifier sans aucun contrôle.

D'où venait le rôle d'un nouvel utilisateur. Le trigger handle_new_user avant et après la correction. Avant : toute personne qui s'inscrit: signUp options.data → raw_user_meta_data → handle_new_user → Rôle Owner; Après : seulement les invités: Fonction d'invitation → pending_invites → handle_new_user → Rôle invité.
Issu du trigger d'inscription d'une application Supabase en production, juin 2026.

Qu’est-ce qui l’a corrigé ?

Le rôle vient maintenant d’une table privée que le client ne peut pas toucher :

create table private.pending_invites (
  email text primary key,
  role_id uuid,
  invited_by uuid,
  created_at timestamptz default now()
);
revoke all on table private.pending_invites from anon, authenticated;

La fonction d’invitation s’exécute côté serveur, après vérification de la permission de la personne qui invite. Elle écrit d’abord l’invitation en attente, puis seulement crée le lien d’invitation. Le trigger retrouve le rôle par l’e-mail et supprime la ligne. Le nom vient toujours des métadonnées, ce qui ne pose pas de problème : ce sont des données d’affichage.

L’ordre compte. Une invitation crée l’utilisateur immédiatement : le guide Users de Supabase précise qu’inviter une nouvelle adresse e-mail crée un utilisateur non confirmé. Le trigger se déclenche à ce moment-là, la ligne en attente doit donc déjà exister.

Comment vérifier votre propre application ?

Ouvrez votre fonction handle_new_user, ou n’importe quel trigger sur auth.users, et cherchez raw_user_meta_data. Le nom et l’avatar viennent de là sans souci. Le rôle, l’entreprise, l’offre, le locataire ou tout ce qui donne un accès doit venir d’une table ou de raw_app_meta_data, que l’utilisateur ne peut pas modifier.

Essayez ensuite : inscrivez-vous via l’API avec role_id ou role: 'admin' dans options.data. Le nouveau compte ne doit rien obtenir en plus.

Ce type de vérification est la première étape de ma méthode de test de la RLS. Pour les applications multi-tenant, lisez aussi ce que la RLS laisse passer pour un utilisateur dans deux organisations. Les autres vérifications que je lance sur chaque application Supabase sont dans ma checklist de sécurité d’une application Lovable. La première que je regarde dans le Dashboard est quels schémas la Data API expose. L’Audit de sécurité Supabase couvre les rôles, droits, politiques et paramètres d’authentification avec 45 règles OWASP Top 10:2025, pas ce qu’un trigger copie des métadonnées d’inscription : cette vérification reste donc manuelle.

© 2026 Denys Zaichenko. Article original : https://madsalt.tech/fr/notes/supabase-raw-user-meta-data-role/