---
title: "Supabase raw_user_meta_data: slik ble en registrering Owner"
description: "Triggeren handle_new_user hentet role_id fra raw_user_meta_data, som nettleseren setter ved registrering. Hvordan hvem som helst ble Owner, og løsningen."
url: "https://madsalt.tech/no/notes/supabase-raw-user-meta-data-role/"
author: "Denys Zaichenko"
language: "no"
published: 2026-10-18
---

# Supabase raw_user_meta_data: slik ble en registrering Owner

TL;DR

`raw_user_meta_data` inneholder det nettleseren sendte til `supabase.auth.signUp()`. I en produksjonsapp leste triggeren `handle_new_user` rollen til den nye brukeren derfra, så hvem som helst kunne registrere seg med id-en til Owner-rollen og eie kontoen. Løsningen: rollen kommer fra en tabell som bare serveren skriver til, fylt ut før invitasjonen sendes.

Dette skjedde i [timeregistreringssystemet for revisjonsselskapet](https://madsalt.tech/no/work/audit-firm-time-tracking/), en Supabase-app der hver tilgangsregel ligger i databasen. Feilen satt i triggeren som oppretter en profil for hver nye bruker.

## Hva gjorde triggeren?

Forenklet så den slik ut:

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

Invitasjonsflyten la riktig `role_id` der, så ved vanlig bruk fungerte det. Men `raw_user_meta_data` er `options.data` fra en registrering, og nettleseren bestemmer hva som står i den:

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

Tilgangssjekken returnerte sant for eiere før den så på noe annet, så det ene feltet ga full kontroll over kontoen. Supabases egen [Users-guide](https://supabase.com/docs/guides/auth/users) sier at brukermetadata ikke må brukes i RLS-policyer eller autorisasjonslogikk, fordi brukeren kan endre den uten noen kontroller.

![Hvor rollen til en ny bruker kom fra. Triggeren handle_new_user før og etter løsningen. Før: alle som registrerer seg: signUp options.data → raw_user_meta_data → handle_new_user → Owner-rolle; Etter: bare inviterte: Invitasjonsfunksjon → pending_invites → handle_new_user → Invitert rolle.](https://madsalt.tech/charts/no/supabase-raw-user-meta-data-role.svg)

*Fra registreringstriggeren i en Supabase-app i produksjon, juni 2026.*

## Hva løste det?

Rollen kommer nå fra en privat tabell klienten ikke kan røre:

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

Invitasjonsfunksjonen kjører på serveren, etter at tilgangen til den som inviterer er sjekket. Den skriver først den ventende invitasjonen og lager deretter invitasjonslenken. Triggeren slår opp rollen via e-postadressen og sletter raden. Navnet kommer fortsatt fra metadata, og det er greit: det er visningsdata.

Rekkefølgen er viktig. En invitasjon oppretter brukeren med en gang: Supabases [Users-guide](https://supabase.com/docs/guides/auth/users) noterer at å invitere en ny e-postadresse oppretter en ubekreftet bruker. Triggeren kjøres i det øyeblikket, så den ventende raden må allerede finnes.

## Hvordan sjekker du din egen app?

Åpne `handle_new_user`-funksjonen din, eller en hvilken som helst trigger på `auth.users`, og søk etter `raw_user_meta_data`. Navn og avatar derfra er greit. Rolle, selskap, abonnement, tenant eller alt annet som gir tilgang må komme fra en tabell eller fra `raw_app_meta_data`, som brukeren ikke kan endre.

Prøv så: registrer deg via API-et med `role_id` eller `role: 'admin'` i `options.data`. Den nye kontoen skal ikke få noe ekstra.

Denne typen sjekk er første steg i [RLS-testmetoden min](https://madsalt.tech/no/blog/how-to-test-supabase-rls/). For multi-tenant-apper, se også [hva RLS overser for en bruker i to organisasjoner](https://madsalt.tech/no/notes/supabase-rls-user-in-two-organizations/). De andre sjekkene jeg kjører på hver Supabase-app, står i [sikkerhetsjekklisten min for Lovable-apper](https://madsalt.tech/no/blog/lovable-app-security-checklist/). Det første jeg ser på i dashbordet, er [hvilke skjemaer Data API-et eksponerer](https://madsalt.tech/no/notes/supabase-exposed-schemas/). [Supabase sikkerhetssjekk](https://madsalt.tech/no/tools/supabase-security-check/) dekker roller, tilganger, policyer og auth-innstillinger mot 46 OWASP Top 10:2025-regler, ikke hva en trigger kopierer fra registreringsmetadata, så dette er fortsatt en manuell sjekk.
