Demo, all content is generated
Question

Let people who are not logged in submit a contact form into Supabase, safely?

Open · 190 views · asked by valentina_s · edited

Landing page with a waitlist form. No accounts. I want the form to insert into a waitlist table. Right now it fails with the RLS error. What's the right policy? I don't want strangers reading the list of emails.

What I’ve tried

Tried create policy ... for all using (true) which works but then I can also select everything with the anon key, which is exactly what I don't want.

Comment
Do you ever need to read the list from the app, or only from the dashboard? sanne_dev · edited
only dashboard valentina_s · edited

3 answers

sanne_dev · edited

Insert-only policy for the anon role, no select policy at all:

create policy "anyone can join waitlist" on public.waitlist
  for insert to anon
  with check (true);

With RLS on and no select policy, reads return an empty array. One gotcha: don't chain .select() after the insert in your client code, because returning the row needs select permission and you'll get the RLS error again.

Comment
the .select() thing was exactly why my first attempt failed. ok! valentina_s · edited
chidi_eze · edited

Sanne's policy is right. The part people skip: with the anon key public, anyone can script 50,000 inserts into that table. Add a check constraint on the email length and format, and consider sending the form through a server action or edge function with a captcha (Turnstile is free) instead of inserting directly from the browser.

Comment
Turnstile looks easy enough. Is the check done in an edge function then? valentina_s · edited
Yes, verify the token server side (edge function or server action), then insert. Never trust a captcha checked only in the browser. chidi_eze · edited
sam_builds · edited

I did the server action route for my waitlist, using the anon key server side with the same insert policy. Keeps the table out of the browser entirely and you can rate limit per IP there.

Comment