Why build your own auth?

There are services and libraries that handle authentication for you — Auth0, Clerk, Better Auth, Supabase Auth, Firebase Auth. You add their SDK, configure a dashboard, and get login pages without writing auth code yourself.

So why does this starter ship its own auth instead?

It starts with email

The one thing every app needs is a way to reach the user. When someone forgets their password, you need a channel to help them — and that channel is email.

But anyone can type your email into a signup form. So you verify it. Until verified, the email is considered invalid. That verification step is what makes the rest of auth trustworthy.

Once you accept that, the feature list writes itself: signup, login, verify email, forgot password, reset password. That's quite a few pages, but that's basically all of auth.

The other approach is OAuth — let a provider like Google handle the password entirely. The user's email is already verified by Google, so you skip verification and password management altogether. The starter supports both: email/password for the baseline, OAuth for convenience.

It's not as scary as it sounds

Auth has a reputation for being scary. But the core is just a few ideas:

  • A users table with hashed passwords
  • A signed cookie that remembers who's logged in
  • A guard function that checks the cookie before showing a page
  • For OAuth, a library like Arctic that handles the provider handshake in a few lines

The starter wires these together for you. Once you walk through the code, you'll see there's no magic — just routes, a database, and a cookie.

Simplicity is security

"Complexity is the worst enemy of security." — Bruce Schneier

Auth services add layers: SDKs, dashboards, token exchanges, webhook callbacks, version upgrades you don't control. Each layer is a place where things can break — or be exploited. The more moving parts, the harder it is to reason about what's actually happening when a user logs in.

The starter's auth is simple on purpose. A users table, a hashed password, a signed cookie. No black box — you can read every line. When something breaks, you debug your own code, not a third-party service's status page.

Auth services are a valid choice for teams that need them. But for most apps, the simpler path is also the safer one.