Authentication

Authentication is handled by Better Auth, configured in server/utils/auth.ts.

Supported sign-in methods

  • Email + password, with forgot-password and email verification
  • Magic link (passwordless)
  • Google OAuth (set GOOGLE_CLIENT_ID/GOOGLE_CLIENT_SECRET to enable)
  • TOTP two-factor authentication with backup codes
  • Passkeys (WebAuthn)

Reading the session

Always read the session with useCurrentSession() (app/utils/auth-client.ts), not authClient.useSession() directly — it dedupes the request across middleware, layout, and page so a single navigation only hits /api/auth/get-session once.

Teams and roles

Users can belong to multiple organizations, switch between them, and invite teammates with owner/admin/member roles. Granular permissions are defined in shared/utils/permissions.ts using Better Auth's real access-control system, not a hardcoded role-name comparison.

Protecting a route

definePageMeta({
  layout: 'dashboard',
  middleware: 'auth'
})

Server routes call requireSession(event) (server/utils/session.ts), which also accepts a valid x-api-key header — see Public API.