StarterApp Docs
Authentication

Overview

Authentication with BetterAuth and Convex

StarterApp pairs BetterAuth with Convex so that sign-in, session storage, and organisation state all stay in sync. Most of the heavy lifting is hidden behind simple helpers, so product teams can focus on experience instead of wiring.

High level takeaway

Use the shared helpers (getCurrentSession, requireUser, useSession) and the stack handles cookies, redirects, and organisation state for you.

How the pieces fit together

  • BetterAuth configuration (convex/lib/auth.ts)
    Defines secure cookies, optional Google sign-in, and the username/password toggle that can be disabled with DISABLE_USERNAME_PASSWORD_AUTH.

  • Convex lifecycle (convex/auth.ts)
    Triggers keep BetterAuth records synced with Convex tables, ensure every user has an active organisation, and expose the getCurrentUser query used across the app.

  • Post-login handshake (apps/dashboard/app/auth/after/route.ts)
    After an OAuth or password sign-in, this route waits until Convex confirms the user and organisation data are ready, then redirects to the requested page.

  • Server helpers (packages/app-shell/src/lib/auth/server.ts)
    getCurrentSession() returns the current user or null. requireUser() redirects to /sign-in when the session is missing. These power pages, layouts, and server actions.

  • Client helpers (@workspace/auth/client)
    useSession() and signIn/signOut are available everywhere under the dashboard thanks to the ConvexBetterAuthProvider.

What happens after sign-in

BetterAuth stores an HttpOnly cookie and Convex triggers create or refresh the user record (including a personal workspace on first visit).

The /auth/after route checks with Convex until the user record is ready, then sends the person to their target page (for example /dashboard).

Server components call getCurrentSession() or requireUser(), while client components use useSession() or useQuery(api.auth.getCurrentUser) for live updates.

Security highlights

  • HttpOnly cookies with the production-safe __Host-session name
  • Same-origin CSRF checks on every write (API routes, Convex HTTP handlers, and dashboard pages)
  • Mandatory cache settings on protected routes: revalidate = 0, dynamic = "force-dynamic", fetchCache = "force-no-store"
  • Hardened JSON helpers (secureUserJson, secureErrorJson) that add security headers and hide sensitive details
  • /api/auth/* runs on the Node runtime so headers and caching stay under explicit control

Where to look in the repo

Prop

Type

Import with care

Always import createAuth from convex/lib/auth. Importing it from convex/http.ts brings in router side effects that break server components.

Support for AI & new teammates

  • llms/AUTH_PATTERNS.md – quick reference for token flow, cache settings, and identity helpers.
  • llms/templates/ – ready-to-use patterns for protected API routes, Convex functions, and server components.
  • llms/SECURITY.md – checklist covering CSRF enforcement, secure responses, and rate limiting.

Using these guides keeps everyone aligned with the production-ready defaults.