Prejsť na obsah

Supabase raw_user_meta_data: registrácia sa stala Ownerom

Supabase

Zobraziť ako MarkdownOtvoriť v ClaudeOtvoriť v ChatGPTOtvoriť v Perplexity

Otázka sa tiež skopíruje. Ak sa chat otvorí prázdny, vložte ju.

TL;DR

raw_user_meta_data obsahuje to, čo prehliadač odovzdal do supabase.auth.signUp(). V jednej produkčnej aplikácii z neho trigger handle_new_user čítal rolu nového používateľa, takže sa ktokoľvek mohol zaregistrovať s id roly Owner a prevziať účet. Oprava: rola pochádza z tabuľky, ktorú zapisuje iba server, vyplnenej skôr, než sa pozvánka odošle.

Stalo sa to v sledovaní času pre audítorskú firmu, aplikácii na Supabase, kde každé pravidlo prístupu žije v databáze. Chyba sedela v triggeri, ktorý vytvára profil pre každého nového používateľa.

Čo trigger robil?

Zjednodušene vyzeral takto:

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

Pozvánkový tok sem vkladal správne role_id, takže pri bežnom použití to fungovalo. Lenže raw_user_meta_data sú options.data registrácie a čo v nich bude, rozhoduje prehliadač:

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

Kontrola oprávnení vracala pre vlastníkov true skôr, než sa pozrela na čokoľvek iné, takže toto jediné pole dávalo plnú kontrolu nad účtom. Samotný sprievodca Users od Supabase hovorí, že metadáta používateľa sa nesmú používať v politikách RLS ani v autorizačnej logike, pretože ich používateľ môže upraviť bez akejkoľvek kontroly.

Odkiaľ sa vzala rola nového používateľa. Trigger handle_new_user pred opravou a po nej. Pred: ktokoľvek, kto sa zaregistruje: signUp options.data → raw_user_meta_data → handle_new_user → Rola Owner; Po: iba pozvaní ľudia: Funkcia pozvánky → pending_invites → handle_new_user → Rola pozvaného.
Z registračného triggera produkčnej aplikácie na Supabase, jún 2026.

Čo to opravilo?

Rola sa odteraz berie zo súkromnej tabuľky, na ktorú klient nesiahne:

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;

Funkcia pozvánky beží na serveri a kontroluje oprávnenie pozývajúceho. Najprv zapíše čakajúcu pozvánku a až potom vytvorí odkaz. Trigger vyhľadá rolu podľa e-mailu a riadok zmaže. Meno sa stále berie z metadát, čo je v poriadku: sú to údaje na zobrazenie.

Na poradí záleží. Pozvánka vytvorí používateľa okamžite: sprievodca Users od Supabase uvádza, že pozvanie nového e-mailu vytvorí nepotvrdeného používateľa. Trigger sa spustí v tom okamihu, takže čakajúci riadok už musí existovať.

Ako skontrolovať vlastnú aplikáciu?

Otvorte svoju funkciu handle_new_user alebo akýkoľvek trigger na auth.users a vyhľadajte raw_user_meta_data. Meno a avatar odtiaľ sú v poriadku. Rola, firma, plán, tenant alebo čokoľvek iné, čo udeľuje prístup, musí pochádzať z tabuľky alebo z raw_app_meta_data, ktoré používateľ meniť nemôže.

Potom to vyskúšajte: zaregistrujte sa cez API s role_id alebo role: 'admin' v options.data. Nový účet nesmie dostať nič navyše.

Takáto kontrola je prvým krokom mojej metódy testovania RLS. Pri multi-tenant aplikáciách sa pozrite aj na čo RLS prehliadne u používateľa v dvoch org. Ďalšie kontroly, ktoré spúšťam pri každej aplikácii na Supabase, sú v mojom checkliste bezpečnosti aplikácie z Lovable. Prvé, na čo sa v dashboarde pozerám, je ktoré schémy Data API vystavuje. Kontrola zabezpečenia Supabase pokrýva roly, granty, politiky a nastavenia autentifikácie podľa 45 pravidiel OWASP Top 10:2025, nie to, čo trigger kopíruje z metadát pri registrácii, takže táto kontrola zostáva ručná.

© 2026 Denys Zaichenko. Pôvodný článok: https://madsalt.tech/sk/notes/supabase-raw-user-meta-data-role/