---
title: "Supabase raw_user_meta_data: sådan blev en tilmelding Owner"
description: "handle_new_user-triggeren tog role_id fra raw_user_meta_data, som browseren sætter. Hvordan enhver blev Owner, rettelsen og et tjek på et minut."
url: "https://madsalt.tech/da/notes/supabase-raw-user-meta-data-role/"
author: "Denys Zaichenko"
language: "da"
published: 2026-10-18
---

# Supabase raw_user_meta_data: sådan blev en tilmelding Owner

TL;DR

`raw_user_meta_data` indeholder det, browseren sendte til `supabase.auth.signUp()`. I en produktionsapp læste triggeren `handle_new_user` den nye brugers rolle derfra, så enhver kunne tilmelde sig med rolle-id'et for Owner og overtage kontoen. Rettelsen: rollen kommer fra en tabel, som kun serveren skriver til, udfyldt før invitationen sendes.

Det skete i [tidsregistreringen til et revisionsfirma](https://madsalt.tech/da/work/audit-firm-time-tracking/), en Supabase-app, hvor hver adgangsregel ligger i databasen. Fejlen sad i den trigger, der opretter en profil for hver ny bruger.

## Hvad gjorde triggeren?

Forenklet så den sådan ud:

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

Invitationsflowet lagde det rigtige `role_id` derind, så ved normal brug virkede det. Men `raw_user_meta_data` er `options.data` fra en tilmelding, og det er browseren, der bestemmer, hvad der står i den:

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

Rettighedstjekket returnerede true for ejere, før det så på noget andet, så det ene felt gav fuld kontrol over kontoen. Supabases egen [Users-guide](https://supabase.com/docs/guides/auth/users) siger, at brugermetadata ikke må bruges i RLS-policies eller autorisationslogik, fordi brugeren kan redigere dem uden nogen kontrol.

![Hvor en ny brugers rolle kom fra. handle_new_user-triggeren før og efter rettelsen. Før: alle, der tilmelder sig: signUp options.data → raw_user_meta_data → handle_new_user → Rollen Owner; Efter: kun inviterede: Invitationsfunktion → pending_invites → handle_new_user → Invitationens rolle.](https://madsalt.tech/charts/da/supabase-raw-user-meta-data-role.svg)

*Fra tilmeldingstriggeren i en Supabase-app i produktion, juni 2026.*

## Hvad rettede det?

Rollen kommer nu fra en privat tabel, som 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;
```

Invitationsfunktionen kører på serveren, og den inviterendes rettighed bliver tjekket. Den skriver først den ventende invitation og opretter først derefter invitationslinket. Triggeren slår rollen op på e-mailadressen og sletter rækken. Navnet kommer stadig fra metadata, hvilket er fint: det er visningsdata.

Rækkefølgen betyder noget. En invitation opretter brugeren med det samme: Supabases [Users-guide](https://supabase.com/docs/guides/auth/users) skriver, at invitation af en ny e-mailadresse opretter en ubekræftet bruger. Triggeren udløses i det øjeblik, så den ventende række skal allerede findes.

## Hvordan tjekker du din egen app?

Åbn din `handle_new_user`-funktion eller en hvilken som helst trigger på `auth.users`, og søg efter `raw_user_meta_data`. Navn og avatar derfra er i orden. Rolle, virksomhed, abonnement, tenant eller andet, der giver adgang, skal komme fra en tabel eller fra `raw_app_meta_data`, som brugeren ikke kan ændre.

Prøv det derefter: tilmeld dig via API'et med `role_id` eller `role: 'admin'` i `options.data`. Den nye konto skal ikke få noget ekstra.

Den slags tjek er første skridt i min [metode til at teste RLS](https://madsalt.tech/da/blog/how-to-test-supabase-rls/). For multi-tenant-apps, se også [hvad RLS overser for en bruger i to orgs](https://madsalt.tech/da/notes/supabase-rls-user-in-two-organizations/). De andre tjek, jeg kører på hver Supabase-app, står i min [sikkerhedstjekliste til Lovable-apps](https://madsalt.tech/da/blog/lovable-app-security-checklist/). Det første, jeg kigger på i dashboardet, er [hvilke schemas Data API'et eksponerer](https://madsalt.tech/da/notes/supabase-exposed-schemas/). [Supabase sikkerhedstjek](https://madsalt.tech/da/tools/supabase-security-check/) dækker roller, rettigheder, policies og auth-indstillinger mod 46 OWASP Top 10:2025-regler, ikke hvad en trigger kopierer fra tilmeldingsmetadata, så dette er stadig et manuelt tjek.
