Verification Checklist
Confirm that Auth, RLS, Storage, Edge Functions, and Realtime are configured correctly before you start building.
Go through every item before you close this section. Each unchecked item is a gap that will cause a confusing failure later — usually at the worst moment, when you are trying to demo something to the client.
Authentication
Skipping the redirect URL step is the most common setup mistake. Test magic link login before you start building any protected screens.
- Email provider is enabled — Authentication → Providers → Email → toggle is ON
- "Confirm email" is ON — so users must verify their email before logging in
- Redirect URLs are added — Authentication → URL Configuration, confirm these URLs exist:
http://localhost:5173(or your local dev port)- Your Vercel deployment URL (if deployed)
- Templates are reviewed — the "From" name is your app name, not "Supabase"
- Test login works — create a test account, receive the confirmation email, click the link, confirm you are redirected to your app (not an error page)
- (If using Google OAuth) Callback URL is added to Google Cloud Console — Provider → Google → copy the Supabase Callback URL → paste into Google Cloud Console → Authorised redirect URIs
Row Level Security
Run the SQL query below. Any table showing rowsecurity = false that contains user data must have RLS enabled before you deploy.
Run this in the SQL Editor to check every table's RLS status: SELECT tablename, rowsecurity FROM pg_tables WHERE schemaname = 'public';
rowsecurity = true. Any false is a security gap to fix before moving on.- RLS is enabled on every user-data table —
rowsecurity = truefor:profiles,students,fee_records,attendance, or whichever tables you have created - At least one SELECT policy exists on each RLS-enabled table — otherwise all data is blocked from everyone
- Policies use
auth.uid()— not a hardcoded user ID or any client-side value - Admin policy is present on tables admins need to access — using the
profiles.role = 'admin'pattern - Policy tested — used the "Test policy" button in Authentication → Policies to confirm at least one policy works as expected
- No policy allows access to data that belongs to another user — a parent querying
fee_recordsshould only receive rows for their own children
Confirm with this query (should return zero rows for any policy that has a logic error — if you get unexpected results, review the USING expression):
Storage
- At least one bucket created — Storage → Buckets shows your bucket(s)
- Bucket type is correct — public buckets for non-sensitive files only (profile photos, service images); private buckets for KYC, invoices, personal documents
- Storage policies are set — click each bucket → Policies tab → at least one SELECT and one INSERT policy exist for
authenticatedusers - Test upload works — upload a test image via the dashboard, confirm it appears in the bucket, copy its URL and verify it loads in a browser
- Private bucket signed URL works — for private buckets, use the dashboard to generate a signed URL and confirm it opens in a browser, then wait for it to expire and confirm it no longer works
Edge Functions
- Edge Functions panel is accessible — Edge Functions in sidebar loads without error
- Secrets are added for functions you will deploy — Settings → Edge Functions → Secrets — confirm
RAZORPAY_KEY_SECRET,RESEND_API_KEY(and any others) are entered. You will not see the values after saving — that is correct, they are encrypted. - At least one function is deployed (if you have reached the backend module) — Edge Functions panel shows your function, Logs tab shows invocations
Realtime
- Realtime enabled on at least one table — Database → Replication → your table has INSERT (and UPDATE if needed) toggled ON
- Inspector verified — Database → Realtime → open inspector → insert a row via SQL Editor → confirm the event appears in the inspector within a few seconds
- RLS is active on the Realtime-enabled table — a logged-in user should only receive Realtime events for rows they are permitted to see
Run the Advisors
Supabase ships two automated audits. Run both before you call this section done.
Security Advisor
Database → Advisors → Security. Scans your project for the most common RLS and exposure mistakes — tables with RLS off, public tables that shouldn't be, policies that always return true, missing primary keys.
Security Advisor — every finding links to the exact table or policy you need to fix.
- Security Advisor shows zero red findings (or every red finding is documented and intentional)
Performance Advisor
Database → Advisors → Performance. Suggests indexes for slow queries, flags unused indexes, surfaces RLS policies that scan whole tables.
Performance Advisor — recommended indexes, slow queries, RLS performance tips.
- Performance Advisor reviewed; any "high" severity finding is either fixed or has a tracked TODO
Run both advisors after every schema change. Five minutes here catches mistakes that would otherwise be found by your client, your users, or worst — by an attacker.
Security Final Check
Answer these four questions. Every answer should be "Yes" before you build further.
| Question | Expected answer |
|---|---|
Can a logged-out user query your fee_records table and get data back? | No — RLS blocks anonymous access |
| Can Parent A log in and query Parent B's child's fee records? | No — RLS auth.uid() = user_id blocks cross-user access |
Can a user access /admin routes in your app without the admin role? | No — ProtectedRoute checks role, RLS confirms on every query |
Are any secret API keys (Razorpay, Resend, Sentry) in your .env file or React code? | No — they are in Supabase Secrets for Edge Functions only |
If any answer is currently "I'm not sure" — go back to the relevant page in this section and configure it before continuing.
You have finished the Supabase Advanced section. The next step is the Supabase API & CLI guide, where you learn to connect your React app to everything you have just configured — writing the actual TypeScript code that uses Auth, queries with RLS, uploads to Storage, calls Edge Functions, and subscribes to Realtime channels.