---
title: "Supabase raw_user_meta_data: jak se registrace stala Ownerem"
description: "Trigger handle_new_user bral role_id z raw_user_meta_data, které nastavuje prohlížeč. Jak se kdokoli stal Ownerem, oprava přes pozvánky, kontrola za minutu."
url: "https://madsalt.tech/cs/notes/supabase-raw-user-meta-data-role/"
author: "Denys Zaichenko"
language: "cs"
published: 2026-10-18
---

# Supabase raw_user_meta_data: jak se registrace stala Ownerem

TL;DR

`raw_user_meta_data` obsahuje to, co prohlížeč předal do `supabase.auth.signUp()`. V jedné produkční aplikaci z něj trigger `handle_new_user` četl roli nového uživatele, takže se kdokoli mohl zaregistrovat s id role Owner a převzít účet. Oprava: role pochází z tabulky, kterou zapisuje jen server, vyplněné dřív, než se pozvánka odešle.

Stalo se to v [evidenci času pro auditorskou firmu](https://madsalt.tech/cs/work/audit-firm-time-tracking/), aplikaci na Supabase, kde každé přístupové pravidlo žije v databázi. Chyba seděla v triggeru, který vytváří profil pro každého nového uživatele.

## Co trigger dělal?

Zjednodušeně vypadal takto:

```sql
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 vkládal správné `role_id`, takže při běžném použití to fungovalo. Jenže `raw_user_meta_data` jsou `options.data` registrace a co v nich bude, rozhoduje prohlížeč:

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

Kontrola oprávnění vracela pro vlastníky true dřív, než se podívala na cokoli jiného, takže tohle jediné pole dávalo plnou kontrolu nad účtem. Samotný [průvodce Users](https://supabase.com/docs/guides/auth/users) od Supabase říká, že metadata uživatele se nesmějí používat v politikách RLS ani v autorizační logice, protože je uživatel může upravit bez jakékoli kontroly.

![Odkud se vzala role nového uživatele. Trigger handle_new_user před opravou a po ní. Před: kdokoli, kdo se zaregistruje: signUp options.data → raw_user_meta_data → handle_new_user → Role Owner; Po: jen pozvaní lidé: Funkce pozvánky → pending_invites → handle_new_user → Role pozvaného.](https://madsalt.tech/charts/cs/supabase-raw-user-meta-data-role.svg)

*Z registračního triggeru produkční aplikace na Supabase, červen 2026.*

## Co to opravilo?

Role se nově bere ze soukromé tabulky, na kterou klient nesáhne:

```sql
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;
```

Funkce pozvánky běží na serveru a kontroluje oprávnění zvoucího. Nejdřív zapíše čekající pozvánku a teprve potom vytvoří odkaz. Trigger vyhledá roli podle e-mailu a řádek smaže. Jméno se pořád bere z metadat, což je v pořádku: jsou to údaje k zobrazení.

Na pořadí záleží. Pozvánka vytvoří uživatele okamžitě: [průvodce Users](https://supabase.com/docs/guides/auth/users) od Supabase uvádí, že pozvání nového e-mailu vytvoří nepotvrzeného uživatele. Trigger se spustí v ten okamžik, takže čekající řádek už musí existovat.

## Jak zkontrolovat vlastní aplikaci?

Otevřete svou funkci `handle_new_user` nebo jakýkoli trigger na `auth.users` a vyhledejte `raw_user_meta_data`. Jméno a avatar odtud jsou v pořádku. Role, firma, tarif, tenant nebo cokoli jiného, co uděluje přístup, musí pocházet z tabulky nebo z `raw_app_meta_data`, které uživatel měnit nemůže.

Pak to vyzkoušejte: zaregistrujte se přes API s `role_id` nebo `role: 'admin'` v `options.data`. Nový účet nesmí dostat nic navíc.

Takováto kontrola je prvním krokem mé [metody testování RLS](https://madsalt.tech/cs/blog/how-to-test-supabase-rls/). U multi-tenant aplikací se podívejte i na [co RLS přehlédne u uživatele ve dvou org](https://madsalt.tech/cs/notes/supabase-rls-user-in-two-organizations/). Další kontroly, které spouštím u každé aplikace na Supabase, jsou v mém [checklistu zabezpečení aplikace z Lovable](https://madsalt.tech/cs/blog/lovable-app-security-checklist/). První, na co se v dashboardu dívám, je [která schémata Data API vystavuje](https://madsalt.tech/cs/notes/supabase-exposed-schemas/). [Kontrola zabezpečení Supabase](https://madsalt.tech/cs/tools/supabase-security-check/) pokrývá role, granty, politiky a nastavení autentizace podle 46 pravidel OWASP Top 10:2025, ne to, co trigger kopíruje z metadat při registraci, takže tato kontrola zůstává ruční.
