Auth is one of those things you can spend a month perfecting and still ship the same product.
Or you can copy a setup that works and use the saved month to build features people will actually pay for.
The cheapest hour you'll ever spend on a Supabase project is the one where you stop redesigning auth and just use the setup that already works.
Related reading from vi3ecoding:
This is the exact one I drop into every Vi3ecoding project, including foodla.eu. It's boring on purpose. Boring auth is good auth.
The defaults I never touch anymore
After enough client projects, I stopped second-guessing these.
- Email + password — yes, even in 2026.
- Google OAuth — added the same day, every time.
- Confirm email on signup — on. The 30-second friction stops 90% of garbage signups.
- Session in localStorage — Supabase's default. Don't get fancy.
- No anonymous sign-ups — off unless I have a real reason.
Every one of these defaults was something I overthought once and stopped overthinking after the second project.
Why both email and Google from day one
Most founders pick one and add the other "later." Later never comes until a user complains.
- Email-only loses you the "I'll just use Google" crowd, which is most of B2B.
- Google-only loses you the privacy-conscious crowd and anyone whose company blocks third-party OAuth.
Both takes ten extra minutes. Skip the debate.
The signup flow that just works
Three screens, no surprises.
- Sign up — email, password, Google button. That's it. No "phone number to verify you're a human."
- Check your email — one screen, one sentence, a resend link.
- First-run dashboard — empty state with one clear next action.
That third screen is where most apps lose their new users. A blank dashboard is a bounce. An empty state with one obvious next click is a conversion.
The RLS-friendly profile pattern
This is the piece most tutorials skip.
When a user signs up, Supabase creates an auth.users row. You want a public.profiles row to match, with the same id, so RLS policies in your app can reference auth.uid() against your own tables.
The pattern:
- A
profilestable withid uuid primary key references auth.users(id) on delete cascade. - A trigger that inserts a profile row on new user signup.
- RLS policies like
auth.uid() = idfor reads and updates.
Set this up on day one. Retrofitting it later is painful and error-prone.
What I don't put on the profiles table
Roles.
Storing is_admin on profiles is the security mistake I see the most often. A clever user can flip it on themselves and your entire RLS model collapses.
Roles go in a separate user_roles table, checked through a security-definer function. It's two extra files in your migration folder and it saves you from a breach.
Password reset, magic links and friends
Defaults again.
- Password reset — on, with Supabase's default email template, branded once.
- Magic links — on for the email-only flow. Useful for users who forget their password every two weeks.
- MFA — only when the project actually needs it. Don't add it because it sounds professional.
Pick the three you ship with. Don't add the rest until a user asks.
The auth choices I stopped making
These were my time sinks before I learned my lesson.
- Custom auth UI from scratch — the built-in components are fine.
- Designing my own email templates pixel-perfect — boring beats beautiful for transactional email.
- Switching session storage to cookies "for security" without a real reason.
- Building admin login flows that bypass the normal auth — always a mistake.
Every one of these was a "small detour" that became a week.
So — what's the right Supabase auth setup?
The boring one. The one you don't have to think about anymore.
Email plus Google, confirmed emails, a profiles trigger, roles in their own table, and ten extra minutes spent on the empty-state screen new users land on.
Auth is solved. Use the setup that knows it.
vi3ecoding Team