Zum Inhalt springen

Supabase raw_user_meta_data: Registrierung wurde Owner

Supabase

Als Markdown anzeigenIn Claude öffnenIn ChatGPT öffnenIn Perplexity öffnen

Die Frage wird ebenfalls kopiert. Falls der Chat leer öffnet, fügen Sie sie ein.

Kurz gesagt

raw_user_meta_data enthält, was der Browser an supabase.auth.signUp() übergeben hat. In einer Produktions-App las der handle_new_user-Trigger die Rolle des neuen Nutzers daraus, sodass sich jeder mit der Owner-Rollen-ID registrieren und das Konto übernehmen konnte. Die Lösung: Die Rolle kommt aus einer Tabelle, die nur der Server beschreibt, und sie wird gefüllt, bevor die Einladung verschickt wird.

Das war beim Zeiterfassungs-Tool für eine Wirtschaftsprüfungsgesellschaft, einer Supabase-App, in der jede Zugriffsregel in der Datenbank liegt. Der Fehler steckte im Trigger, der für jeden neuen Nutzer ein Profil anlegt.

Was tat der Trigger?

Vereinfacht sah er so aus:

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;
$$;

Der Einladungsablauf legte dort die richtige role_id ab, im normalen Betrieb funktionierte es also. Aber raw_user_meta_data sind die options.data einer Registrierung, und was darin steht, bestimmt der Browser:

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

Die Berechtigungsprüfung gab für Owner true zurück, bevor sie etwas anderes ansah, deshalb reichte dieses eine Feld für volle Kontrolle über das Konto. Die Users-Anleitung von Supabase selbst sagt, dass Nutzer-Metadaten nicht in RLS-Policies oder Autorisierungslogik verwendet werden dürfen, weil der Nutzer sie ohne jede Prüfung ändern kann.

Woher die Rolle eines neuen Nutzers kam. Der handle_new_user-Trigger vor und nach der Lösung. Vorher: jeder, der sich registriert: signUp options.data → raw_user_meta_data → handle_new_user → Owner-Rolle; Nachher: nur Eingeladene: Einladungsfunktion → pending_invites → handle_new_user → Eingeladenen-Rolle.
Aus dem Registrierungs-Trigger einer Supabase-App in Produktion, Juni 2026.

Was hat es behoben?

Die Rolle kommt jetzt aus einer privaten Tabelle, an die der Client nicht herankommt:

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;

Die Einladungsfunktion läuft auf dem Server, nachdem die Berechtigung des Einladenden geprüft wurde. Sie schreibt zuerst die offene Einladung und erstellt erst dann den Einladungslink. Der Trigger sucht die Rolle über die E-Mail-Adresse und löscht die Zeile. Der Name kommt weiterhin aus den Metadaten, das ist in Ordnung: Es sind Anzeigedaten.

Die Reihenfolge ist wichtig. Eine Einladung legt den Nutzer sofort an: Die Users-Anleitung von Supabase hält fest, dass das Einladen einer neuen E-Mail-Adresse einen unbestätigten Nutzer erzeugt. Der Trigger feuert in diesem Moment, die offene Einladung muss also schon existieren.

Wie prüfen Sie Ihre eigene App?

Öffnen Sie Ihre handle_new_user-Funktion oder einen beliebigen Trigger auf auth.users und suchen Sie nach raw_user_meta_data. Name und Avatar von dort sind in Ordnung. Rolle, Firma, Tarif, Mandant oder alles andere, was Zugriff gewährt, muss aus einer Tabelle oder aus raw_app_meta_data kommen, das der Nutzer nicht ändern kann.

Probieren Sie es dann aus: Registrieren Sie sich über die API mit role_id oder role: 'admin' in options.data. Das neue Konto sollte nichts zusätzlich bekommen.

Diese Art von Prüfung ist der erste Schritt meiner Methode, RLS zu testen. Bei mandantenfähigen Apps lesen Sie auch, was RLS bei einem Nutzer in zwei Organisationen übersieht. Die anderen Prüfungen, die ich bei jeder Supabase-App durchführe, stehen in meiner Sicherheits-Checkliste für Lovable-Apps. Die erste, die ich im Dashboard ansehe, ist welche Schemas die Data API exponiert. Der Supabase Sicherheitscheck deckt Rollen, Grants, Policies und Auth-Einstellungen gegen 45 Regeln der OWASP Top 10:2025 ab, nicht aber, was ein Trigger aus den Metadaten der Registrierung übernimmt, daher bleibt dies eine manuelle Prüfung.

© 2026 Denys Zaichenko. Originalartikel: https://madsalt.tech/de/notes/supabase-raw-user-meta-data-role/