Thinking back to where I started, I wanted to share something I learned early on. Well known to all experiences software developers of course, but if you are starting out, maybe this will be helpful :)
I try not to treat the auth user as the actual business/domain user anymore.
In the beginning it feels totally fine. You have a user from Supabase or Firebase/whatever, so it’s tempting to just use that ID everywhere.
And for a while, it works.
But then the product grows a little:
Suddenly the auth provider’s idea of a user and your product’s idea of a user are not quite the same thing.
Separate them early:
auth identity = “who logged in?”
domain user/customer/member = “who exists in my product?”
It adds a tiny bit of friction upfront, but it keeps the business model from being glued too tightly to the auth provider.
I wrote up the longer version here:
https://kapsdevelopment.com/blog/why-i-separate-auth-identity-from-domain-identity-from-day-one/
Curious how others handle this — do you separate these from the start, or only when the app starts getting messy?
The user shares insights on separating authentication identity from domain identity to prevent issues as a product scales. They emphasize the importance of distinguishing between 'who logged in' and 'who exists in the product' to avoid complications with subscriptions, invitations, and account management. Another user adds examples of potential issues when these identities are conflated, offering a tool to audit Supabase security policies.
Two failure modes I see most often when these get conflated:1. After an account merge, the orphan auth.uid() leaves rows orphaned in the data — but the RLS policy still passes for the original UID if it lingers in any other claim/session. The data is reachable; the dashboard says it's not.2. AI codegen LOVES `(select auth.uid()) = user_id` — works in single-tenant, becomes a cross-tenant leak the second user_id is treated as the org membership pivot instead of the personal-row pivot. Domain layer would have caught it; pure auth-layer policies don't.I built an open-source Supabase auditor that codes for these (active anonymous probe confirms each leak by fetching rows with the anon key, not just inferring from the policy text): github.com/Perufitlife/supabase-security-skill — there's a no-install Apify version too: https://apify.com/renzomacar/supabase-security-auditor