Back to Blog
8 min read

Authentication deep dive

Authentication in LaunchApp is handled by Better Auth, a framework-agnostic auth library that supports email/password, OAuth providers, magic links, and more. The server instance lives in @repo/auth and is shared across both the Hono API and the React Router frontend — one config, one source of truth.

  • auth
  • architecture
LT

LaunchApp Team

Jan 22, 2025

Authentication in LaunchApp is handled by Better Auth, a framework-agnostic auth library that supports email/password, OAuth providers, magic links, and more. The server instance lives in @repo/auth and is shared across both the Hono API and the React Router frontend — one config, one source of truth.

The API side

On the API side, the Better Auth handler is mounted under /api/auth/* in packages/api/src/app.ts. Every incoming request to that prefix is forwarded directly to Better Auth, which manages sessions via secure HTTP-only cookies.

app.on(["POST", "GET"], "/api/auth/**", (c) => auth.handler(c.req.raw));

Because the handler is mounted at a prefix rather than individual routes, adding new auth flows (social login, passkeys, 2FA) is a config-only change inside @repo/auth — the API mount stays the same.

The frontend side

The frontend uses the Better Auth client exported from @repo/auth. React Router loaders call auth.api.getSession({ headers: request.headers }) on the server to check whether the current user is authenticated before rendering a protected page.

Session data flows from the loader into the component via useLoaderData. Protected layouts like the dashboard redirect to /auth/login when no session is present, keeping the auth guard logic in one place rather than scattered across individual routes.

Adding a new provider

Adding a new OAuth provider (say GitHub) is a three-step change:

  1. Register the provider in the Better Auth config inside @repo/auth.
  2. Add the client ID and secret to .env and .env.example.
  3. Update the login page to show the new sign-in button.

The rest of the session handling — cookie setting, redirect after callback, session refresh — is automatic. The same provider then works across the Next.js, Nuxt, and SvelteKit variants because they all import from the same @repo/auth package.

What's different from DIY auth

Rolling your own auth means writing password hashing, session tokens, CSRF protection, OAuth state verification, rate limiting, and recovery flows. Each one is a subtle security footgun. Better Auth consolidates all of that behind a typed API, which is why LaunchApp ships it as the default rather than a hand-rolled middleware.