Supabase auth prompts for AI coding — Updated October 2026. Every vibe-coded SaaS hits the same wall: the UI looks done, then you need real users, row-level security, and a database that does not leak data on the first penetration test. Pasting vague instructions like “add Supabase auth” into Cursor or Lovable burns credits and produces half-wired policies. This post gives you 15 copy-paste RLS prompt templates for schema design, email and OAuth login, storage buckets, edge functions, seed data, and migration safety — plus when to use each one so you stop regenerating the same broken setup.

Supabase is the default backend on most vibe coding stacks for good reason: Postgres, auth, storage, and edge functions in one dashboard. The failure mode is prompting without constraints — the model invents table names, skips auth.uid() checks, or writes RLS policies that block your own admin user. Start with our copy-paste prompts to scaffold a web app if you do not have a project yet, then layer these database prompts on top. For prompt discipline across the stack, keep the CRISP-E method handy — context and specificity matter more here than anywhere else in vibe coding.
- When to use these Supabase prompts
- Schema and migration prompts (1–3)
- Auth prompts: email, magic link, OAuth (4–7)
- RLS policy prompts (8–11)
- Storage, edge functions, seed data (12–15)
- Workflow tips for AI + Supabase
- FAQ
When to use these Supabase auth prompts for AI coding
Use these templates in Cursor, Claude Code, Lovable (after export), Bolt, or Replit Agent once you have a frontend framework chosen — Next.js App Router is assumed in several prompts but you can swap for Remix or Vite. Paste one prompt per task, not all fifteen at once. The model loses track of dependencies if you ask for schema + auth + RLS + storage in a single message.
Database setup vibe coding order that works:
- Schema + migrations (prompts 1–3)
- Auth provider wiring (4–7)
- RLS policies before any production data (8–11)
- Storage and edge functions (12–14)
- Seed data last, on staging only (15)
If a prompt fails, do not mash prompts together — debug with our debugging prompts guide and re-run with the error message appended.
Schema and migration prompts
Prompt 1 — Core schema with UUID primary keys and timestamps
When to use: Day one, before any auth or RLS. Establishes conventions the rest of your prompts reference.
Project: [APP NAME] on Next.js 15 App Router + Supabase JS v2.
Create a SQL migration file `supabase/migrations/001_initial_schema.sql` with:
Tables:
- profiles (id uuid PK references auth.users on delete cascade, display_name text, avatar_url text, created_at timestamptz default now(), updated_at timestamptz)
- [YOUR_DOMAIN_TABLE] (id uuid PK default gen_random_uuid(), user_id uuid NOT NULL references auth.users, title text NOT NULL, body text, status text default 'draft' check (status in ('draft','published')), created_at timestamptz default now(), updated_at timestamptz)
Requirements:
- Enable pgcrypto extension if needed for gen_random_uuid()
- Add updated_at trigger function reused across tables
- Add indexes on user_id and status
- Do NOT enable RLS yet — migration only
- Output only the SQL file contents and a one-line note on how to run: supabase db push or migration applyPrompt 2 — Foreign keys and soft-delete pattern
When to use: When your app has related entities (comments, tags, teams) and you need consistent referential integrity.
Given existing tables: profiles, [YOUR_DOMAIN_TABLE]. Add migration `002_relations.sql`: - comments (id uuid PK default gen_random_uuid(), post_id uuid references [YOUR_DOMAIN_TABLE](id) on delete cascade, author_id uuid references auth.users, content text not null, deleted_at timestamptz null, created_at timestamptz default now()) - Index: comments(post_id) where deleted_at is null Add soft-delete column deleted_at to [YOUR_DOMAIN_TABLE] if missing. Document in SQL comments which tables use soft delete vs hard delete. No RLS in this migration.
Prompt 3 — Migration safety check before push
When to use: Before every supabase db push or CI migration — catches destructive changes the agent sneaks in.
Review all files in supabase/migrations/ for destructive operations. List each migration with: ADD / ALTER / DROP / DATA RISK (none|low|high). Flag: DROP COLUMN, DROP TABLE, TRUNCATE, type changes on populated columns, missing IF NOT EXISTS, policies that reference wrong table names. If high risk found, rewrite migration to use additive-only pattern (new column, backfill, switch reads, drop old in later migration). Output: checklist table + corrected SQL if needed.
Auth prompts: email, magic link, OAuth
Prompt 4 — Email + password auth (Next.js App Router)
When to use: Standard SaaS signup with email verification. Configure Supabase dashboard email templates separately.
Stack: Next.js 15 App Router, @supabase/ssr, TypeScript. Implement email/password auth: 1) `lib/supabase/server.ts` and `lib/supabase/client.ts` using createServerClient / createBrowserClient with cookie handling per Supabase SSR docs. 2) Routes: /login, /signup, /auth/callback (route handler), /auth/confirm 3) Server actions: signUp (email redirectTo /auth/callback), signIn, signOut 4) Middleware protecting /dashboard/* — redirect unauthenticated to /login 5) On first signup, insert profiles row via database trigger or server action (id = auth.users.id) Do not add OAuth. Use environment variables NEXT_PUBLIC_SUPABASE_URL and NEXT_PUBLIC_SUPABASE_ANON_KEY only in client-safe code. Include error toast messages for invalid credentials and unconfirmed email.
Prompt 5 — Magic link (passwordless email)
When to use: B2B tools, newsletters, or apps where password management is not worth the support burden.
Add magic link login to existing Supabase auth setup.
- Login page: single email field, "Send magic link" button
- Use supabase.auth.signInWithOtp({ email, options: { emailRedirectTo: `${origin}/auth/callback` } })
- Show "Check your email" state; handle rate limit errors
- Ensure /auth/callback exchanges code for session (PKCE)
- Update middleware to treat magic-link session same as password session
- Add comment: enable Email provider + Magic Link in Supabase Dashboard → Authentication → ProvidersPrompt 6 — Google OAuth
When to use: Consumer apps where Google sign-in lifts conversion. Requires Google Cloud OAuth client IDs in Supabase dashboard.
Add Google OAuth to Next.js Supabase auth:
- Login page button "Continue with Google" calling signInWithOAuth({ provider: 'google', options: { redirectTo: `${origin}/auth/callback`, queryParams: { access_type: 'offline', prompt: 'consent' } } })
- Handle /auth/callback for OAuth code exchange
- On first OAuth login, upsert profiles (display_name from user.user_metadata.full_name, avatar_url from avatar_url metadata)
- Document required env vars and Supabase Dashboard steps (Client ID/Secret, redirect URL allowlist)
Do not implement other providers.Prompt 7 — Session refresh and server component auth helper
When to use: After basic auth works but server components show stale user state or random logouts.
Fix Supabase session handling for Next.js App Router: - middleware.ts: refresh session via supabase.auth.getUser() on matched routes - Export async function getUser() from lib/supabase/server.ts for Server Components - Client hook useUser() with onAuthStateChange subscription - Ensure cookies use same options Supabase SSR recommends for 2026 Add minimal test plan: login → hard refresh dashboard → user still present → signOut clears cookies
RLS policy prompt templates
Row-level security is where Supabase auth prompts AI coding projects succeed or leak data. Never ship without policies on tables containing user data. Run prompts 8–11 in order.
Prompt 8 — Enable RLS and profiles policies
When to use: Immediately after schema exists and before connecting the app to production Supabase.
Migration `003_rls_profiles.sql`: - ALTER TABLE profiles ENABLE ROW LEVEL SECURITY; - Policy profiles_select_own: FOR SELECT USING (auth.uid() = id); - Policy profiles_update_own: FOR UPDATE USING (auth.uid() = id) WITH CHECK (auth.uid() = id); - Policy profiles_insert_own: FOR INSERT WITH CHECK (auth.uid() = id); - No public read of other users' profiles unless we add a separate public view later Verify: anonymous SELECT returns 0 rows; authenticated user sees only own row.
Prompt 9 — Owner-based CRUD on domain table
When to use: Standard “users own their posts/projects/tasks” model — the most common SaaS pattern.
Table: [YOUR_DOMAIN_TABLE] with user_id uuid NOT NULL. Enable RLS. Policies: - SELECT: auth.uid() = user_id (owners see own rows only) - INSERT: WITH CHECK (auth.uid() = user_id) - UPDATE: USING (auth.uid() = user_id) WITH CHECK (auth.uid() = user_id) - DELETE: USING (auth.uid() = user_id) Add service_role bypass note in comments (server admin scripts only — never expose service key client-side). Generate SQL migration only.
Prompt 10 — Team / organization multi-tenant RLS
When to use: B2B apps where multiple users share access to the same records via org membership.
Schema addition:
- organizations (id uuid PK, name text, created_at timestamptz)
- org_members (org_id uuid references organizations, user_id uuid references auth.users, role text check (role in ('owner','admin','member')), primary key (org_id, user_id))
- Add org_id uuid references organizations to [YOUR_DOMAIN_TABLE]
RLS on [YOUR_DOMAIN_TABLE]:
- SELECT/INSERT/UPDATE/DELETE allowed if exists (select 1 from org_members m where m.org_id = [YOUR_DOMAIN_TABLE].org_id and m.user_id = auth.uid())
- INSERT WITH CHECK: user must be member of org_id being inserted
- org_members RLS: users see rows where user_id = auth.uid() OR they are admin/owner of shared org (define helper function is_org_member(org uuid) security definer)
Use security definer functions carefully — set search_path = public.Prompt 11 — Public read, authenticated write
When to use: Blogs, marketplaces with public listings, or published content with draft/private states.
[YOUR_DOMAIN_TABLE] has status ('draft','published').
RLS policies:
- SELECT: status = 'published' OR auth.uid() = user_id (anyone reads published; owners read all own)
- INSERT/UPDATE/DELETE: auth.uid() = user_id only
- UPDATE WITH CHECK: cannot set status = 'published' unless [optional: subscription active — stub comment only]
Add policy names prefixed by table name. Include pgTAP-style SQL comments describing expected behavior for anon vs auth vs owner.
Storage, edge functions, and seed data prompts
Prompt 12 — Storage bucket with RLS-aligned uploads
When to use: Avatar uploads, document attachments, or user-generated images.
Create Supabase Storage bucket `avatars` (public read for object URLs OR private with signed URLs — pick one and document).
Policies on storage.objects:
- INSERT: authenticated users path folder = auth.uid()::text (path pattern: {userId}/filename)
- SELECT: public if bucket public; else signed URL helper in lib/storage.ts
- UPDATE/DELETE: owner path prefix only
Next.js API route or server action: uploadAvatar(file) validating image type and 2MB max.
Wire profiles.avatar_url on successful upload.Prompt 13 — Edge function stub (webhook or auth hook)
When to use: Stripe webhooks, custom email flows, or auth hooks that should not live in Next.js serverless limits.
Scaffold Supabase Edge Function `supabase/functions/stripe-webhook/index.ts`: - Deno serve handler verifying Stripe signature from STRIPE_WEBHOOK_SECRET - On checkout.session.completed: upsert subscription row for user_id from metadata (stub TODO comments for DB client with service role) - Return 400 on invalid signature, 200 on success - Include deno.json imports map and local test command: supabase functions serve stripe-webhook Do not embed service role key in client — edge function secrets only.
Prompt 14 — Typed Supabase client queries
When to use: After schema stabilizes — generates type-safe data layer the frontend actually uses.
Generate TypeScript types from Supabase schema (document: supabase gen types typescript --linked). Create `lib/db/[your-domain].ts` with: - listMine(userId): Promise<Row[]> - create(input): Promise<Row> - update(id, userId, patch): Promise<Row> - softDelete(id, userId): Promise<void> All queries use anon client — rely on RLS, not manual user_id filters in client (defense in depth: still pass user context via auth). Handle PostgrestError with user-friendly messages.
Prompt 15 — Seed data for local/staging only
When to use: Staging demos, Playwright tests, or local supabase start — never run arbitrary seed against production.
Create `supabase/seed.sql` (gitignored from production deploy pipeline): - 3 test users via auth.users insert pattern documented for LOCAL ONLY or use Supabase seed with fixed UUIDs - profiles, 2 orgs, org_members, 10 [YOUR_DOMAIN_TABLE] rows mixed draft/published - Comment at top: DO NOT RUN ON PRODUCTION — staging/local only Add package.json script `"db:reset": "supabase db reset"` note. Document how to login as test user test1@example.com / password from seed comments.
Workflow tips for database setup vibe coding
These prompts are starting points. Wrap them in project context for better output — always name your framework, table names, and route paths. Store a project-level system prompt using our system prompt templates for coding agents so every Supabase task inherits the same conventions.
| Phase | Prompts | Common mistake | Fix |
|---|---|---|---|
| Schema | 1–3 | Agent drops FK to auth.users | Prompt 1 explicitly requires cascade reference |
| Auth | 4–7 | Callback route missing PKCE | Prompt 5 + 7 together after first login failure |
| RLS | 8–11 | Policies enabled, none created → lockout | Run 8 immediately after schema; test with anon key |
| Storage/edge | 12–13 | Public bucket + sensitive files | Prompt 12 forces path-per-user pattern |
| Seed | 15 | Seed script hits production | Gitignore + CI guard; comment DO NOT RUN |
Testing RLS without guessing: After prompts 8–11, run this in Supabase SQL editor as a sanity check: set request.jwt.claim.sub = '[user-uuid]'; select * from [your_table]; — or use the anon key from a small test script. If your agent skipped policies, you will see either zero rows (too strict) or all rows (disaster — fix before launch).
Pair this pack with the right tool for your phase. Scaffolding UI in Lovable then exporting? Run prompts 1–3 on the exported repo in Cursor. Full stack in Replit? Paste one prompt per Agent turn — see Replit Agent review for limit patterns. Comparing backends? Our Bolt vs Lovable vs Replit guide helps you pick where Supabase wiring happens least painfully.
Auth is not the place to vibe loosely. These RLS prompt templates exist so you spend creativity on product features, not on recovering from a public profiles table. Iterate prompts with errors attached — the second run is almost always cheaper than the first, especially if you apply token-saving habits from the rest of your workflow.
FAQ
Should I run all 15 Supabase prompts at once?
No. One prompt per agent turn, in schema → auth → RLS → storage → seed order. Combining them produces conflicting table names and skipped RLS. Prompt 3 is a review pass — use it before every migration push.
Do these prompts work in Lovable without export?
Lovable has built-in Supabase integration, but fine-grained RLS (prompts 10–11) is more reliable after export to a local repo in Cursor or Claude Code. Use prompts 4–6 concepts in Lovable chat; paste 8–11 into your IDE.
What is the most important prompt for security?
Prompts 8 and 9 — enable RLS and owner-based policies before any real user signs up. Prompt 11 if you have public content. Skipping straight to prompt 12 (storage) without RLS on tables is how vibe-coded apps leak data.
How do I fix “new row violates row-level security policy”?
Usually INSERT policy missing or user_id not set to auth.uid() in the insert payload. Re-run prompt 9 with the exact error and table definition. Confirm the client uses the anon key, not service role, for user-facing writes.
Ship auth once, then move on
Supabase auth prompts for AI coding are not glamorous — they are the difference between a demo and a product. Save this post, run the fifteen prompts in order on your next project, and keep the vibe coding roadmap pointed at features users pay for after login works. When auth is done, grab more scaffold prompts for the fun parts — and debugging prompts for the inevitable first callback URL typo.
[REDACTED]
