The auth decision you make in week one and live with forever
Somewhere in the first week of any build, usually before there's a single real screen to look at, someone has to answer a question that never gets its own meeting: how does this app know who someone is, and what they're allowed to touch. That's choosing authentication for a web app — and it's the second decision I put ahead of almost everything else, right behind the data model, because it's just as hard to walk back once real accounts exist inside it.
I understand why it gets rushed. Auth isn't the fun part. Nobody demos a login flow to a friend and gets a reaction. Most AI build tools will hand you something that works in about ninety seconds — a sign-up form, a session, a "logged in" state — and it's tempting to treat that as done and move on to the screens people will actually see. The problem is that "someone can log in" and "the app knows what that person is allowed to do once they're in" are two completely different problems, and the second one is the one that actually matters.
Here's the trap I see most often. The interface hides the buttons a user shouldn't see — no delete option, no other customer's data rendered on the page — and everyone treats that as the permission model. It isn't. Hiding a button is a courtesy to a well-behaved browser. It does nothing to stop a request sent directly to the API, and it does nothing to stop the next person who opens the network tab and notices the endpoint doesn't check who's asking. If authorization only lives in what the interface chooses to display, you don't have a permission model, you have a suggestion.
The decision that actually holds up is whether every request that returns or changes data confirms, on the server, that this specific user is allowed to do this specific thing — every time, not just on the requests someone remembered to protect. That's not a detail you bolt on in month three. It's a pattern that has to be true from the first protected endpoint onward, because retrofitting it means auditing every route you've already shipped and finding the ones where you trusted the client instead of checking the server.
The other reason this decision is expensive to reverse is that it doesn't stay contained. Once you pick a shape — sessions or tokens, roles or fine-grained permissions, one account per user or accounts that can hold multiple people — every table, every API route, and every screen built afterward assumes that shape is correct. Change it later and you're not editing a login page, you're touching every place that assumed the old model. I watched a client's team try to bolt "a customer can belong to more than one account" onto a system that was built assuming exactly one, months after real customer data had settled into the wrong shape. It wasn't a config change. It was a project.
And the model has to match the business, not just the org chart you had on day one. If a customer might eventually need to grant a bookkeeper read-only access, or a manager might need to see three locations instead of one, that's a fact about the business you should be modeling now — even if today there's only ever one person per account — because adding a second role later is a redesign, not a toggle.
None of this means over-building it. A small internal tool doesn't need enterprise single sign-on and six permission tiers on week one. What it needs is the discipline to check authorization on the server for every request, and a data model that isolates one account's records from another's — the two things that are non-negotiable regardless of how simple the rest of the app is allowed to stay. Everything past that — password reset flows, multi-factor, granular roles — can be staged in deliberately as real usage tells you what's actually needed, the same way logging or caching can wait. Auth itself cannot.
This is why the auth model sits second in the 13-layer build standard I use to scope and review builds — right after the schema, and ahead of any screen. Get the data model and the auth model right before the first component exists, and the rest of the build gets to move fast without dragging a migration behind it. Get either wrong, and speed on the other eleven layers just means you're further along on top of a foundation you'll eventually have to redo. If you want the deeper version of what "the server checks, not the interface" actually looks like in practice, I walked through it in more detail in the piece on the wall your AI-built app is missing.
If you're a week or two into a build and nobody's said out loud what the permission model actually is — sessions or tokens, one role or several, checked on the server or just hidden in the UI — that's worth a real conversation before more screens get built assuming an answer nobody chose on purpose. The good news is that the fix, this early, is still just a conversation. Wait until the third integration depends on the current shape, and it turns into a migration with a rollback plan and a maintenance window.
See the full 13-layer build standard →
— Kyle Tysvaer, Founder, Insightful Eye Marketing