How I Handle Auth, Security and User Data as a Solo Builder
Solo SaaS authentication done right: don't roll your own auth, collect the least data you can, and do the boring custodial parts deliberately.
The advice you'll read is that security is something you "get to" once you have users and revenue to justify it. In my experience it's the reverse: the moment the first real person signs up, you're already the custodian of someone else's data — alone, with no security team, and no one coming to build one. The Nuxt and Supabase stack I run every product on gives me a lot of this for free, but free defaults only help if you act like a custodian from day one. The boring parts — auth, data handling, access rules — aren't something you graduate to; they're what you owe the first user who trusts you with a row.
You are the custodian, from user one
There's no security review at a one-person company, no team that owns "data protection" while I own features. The person shipping the login box and the person responsible when it leaks are the same person. Eighteen years in enterprise finance taught me one durable lesson: the organizations that got burned weren't the ones with sophisticated attackers, they were the ones who treated the boring control as optional until the day it wasn't. Solo, that gets sharper — there's no one else to absorb the miss.
So I don't think of it as security work but as custody: someone handed me their data on the strength of a signup form, and the whole job is not betraying that.
Don't roll your own auth
The single highest-leverage decision here is also the least original: I do not build authentication. I use Supabase auth, the platform's own, exactly as it comes.
Rolling your own auth is how solo builders get breached. Not because any one piece is hard, but because there are so many — password hashing, session lifetimes, token rotation, reset flows, rate limiting on the login endpoint — and a single one done wrong is the whole door open. A funded team can staff that surface; I can't, and pretending otherwise is how you end up with a clever homemade login that quietly leaks. The login box is not where a solo builder gets to be original. It's the most solved problem in the stack, so I take the solved version and spend my originality on the product nobody else is building.
The login box is the most solved problem in the stack. Being original there isn't ambition — it's how a one-person team ships its own breach. Take the platform's auth and spend the originality on the product.
Collect the least you can
The second habit is refusing to hoard. Every field a form asks for is a field I'm now responsible for protecting, and the safest data is the data I never stored — you can't leak what isn't there.
So the default is least data: collect only what the product genuinely needs, and stop. It's tempting to grab more "in case it's useful later" — the instinct that makes companies sit on years of data they never look at and then have to explain when it escapes. That's not a privacy nicety bolted on for the policy page; it's the cheapest security control there is, because it shrinks the thing I have to keep safe. When I onboard someone, the goal is the fastest path to value, not the longest form — and those turn out to be the same thing.
Turn on the sensible defaults
Past those two decisions, the rest is defaults — the unglamorous settings a managed stack hands you and most people never switch on:
- Row-level security. Postgres enforces, at the database layer, that a user can only ever read or write their own rows. The rule lives at the bottom, not scattered through application code I have to remember to filter every time. When I'm tired and write a lazy query at 11pm, the database still refuses to hand back someone else's data — which is the whole point of putting the rule where it can't be forgotten.
- Secrets in the environment, not the code. Keys, tokens and connection strings live in environment variables, never committed. A secret in source is a secret one push away from being public forever.
- Managed over hand-built. The same logic as auth, everywhere: the platform's storage, database and session handling. Every piece I don't hand-build is a piece I don't have to keep hardened myself.
None of these is clever. That's the feature. Clever is what you regret at 3am.
Honesty is part of the security
Custody isn't only about keeping data from leaking. It's about not quietly doing more with it than I said I would. So the privacy policy is written in plain language a person can actually read, describing what the product really does — not a template pasted to look compliant.
The clearest example is on this very site: analytics only fire after the visitor consents. Tracking first and asking later, the way most sites do, would go unnoticed — which is exactly the reasoning I don't want to run on my own users. Honesty here isn't a compliance checkbox; it's a feature, the same trust the whole build-in-public model rests on. If I'll tell you in public where my product decisions went wrong, I'm not going to be sneaky about your data in private.
The boring part is the whole company
Here's the docs-versus-reality gap that catches people. The product is the exciting part — the thing you demo, the reason anyone shows up. Auth and data handling are the unglamorous part underneath, and the asymmetry is brutal: you win slowly, over weeks, and a single mishandled password field loses you every user in an afternoon, along with the trust no relaunch buys back. It's the same reason most AI-built apps feel like demos — the shiny surface got all the attention and the load-bearing parts got none.
This is also why I keep AI firmly in the workflow and never in the decision. It helps me write, scaffold and move faster, but it doesn't get to decide what data I collect, how access rules are shaped, or what the product tells a user about their own information. Those are custody calls, and they stay mine — the same way support stays personal rather than getting handed to a bot.
So I do the boring thing deliberately, and early — before the first real user, not after the first incident. Not because it's satisfying, but because the alternative isn't "cut a corner." It's being the sole custodian who cut it, alone, with the receipts pointing one direction. Do it once, do it boring, let it hold. That's the part of building AI products nobody demos and everybody needs.
Part of Building AI Products. See also the Nuxt and Supabase stack this sits on and why most AI-built apps feel like demos. The newsletter sends one practical build lesson every two weeks.
Frequently asked questions
Should a solo builder roll their own auth?
No. Rolling your own authentication is one of the most common ways a solo builder gets breached, because a login system has to get dozens of unglamorous things right — password hashing, session handling, token expiry, reset flows, rate limiting — and a single miss is enough. Use the auth your platform already ships: I use Supabase auth, which handles sessions, providers and password storage for me. The login box is not where a one-person team should be original. Spend that originality on the product; take the solved, hardened thing for the parts where a mistake ends the company.
How do you keep user data safe as a one-person team?
By treating yourself as a custodian from the first real user, not once there's a security team — because there is never going to be one. In practice that means three habits: don't build your own auth, take the platform's; collect only the data the product actually needs, because the safest data is the data you never stored; and turn on sensible defaults like row-level security, secrets in environment variables rather than code, and managed pieces over hand-built ones. None of it is clever. All of it is the boring work that a single mistake turns into the end of the product.
What is row-level security?
Row-level security (RLS) is a database feature — in my stack, Postgres through Supabase — that enforces, at the data layer, which rows a given user is allowed to see or change. Instead of trusting your application code to remember to filter every query by user, the database itself refuses to return another user's rows. For a solo builder that's exactly the right place to put the rule: one policy at the bottom that holds even when you're tired and write a sloppy query at the top. A user can only ever reach their own data, by default, because the database won't do otherwise.