← Back to overview
Status

Status: adopted identity boundary and mandatory release gates  |  Evaluated: 2026-07-16  |  Primary sources: Better Auth official docs, Zero official auth docs, Better Auth GitHub, Better Auth security advisories

Executive Take

Better Auth is Boja's identity/session infrastructure for the TypeScript-owned Postgres backend and Rocicorp Zero architecture, subject to the mandatory integration and security release gates below.

It covers the identity boundary for a React Native + Web app, issues short-lived service JWTs for the sync/backend layer, caches the native session in SecureStore for spinner-free startup, and provides session, passkey and enterprise-identity primitives.

Boja app tables own product servers, memberships, invitations, owners, roles and their complete lifecycle. Discord-like channel permissions, role precedence, per-channel overrides, entitlements and feature/server gates use the application-owned capability model, enforced in server queries/mutators and reflected in Zero read filters. Better Auth owns identity and session mechanics only.

Fit Against Requirements

RequirementBetter Auth fitRecommendation
React Native + Web Strong. Official Expo integration supports native and web. Native cookies/session state are stored through SecureStore. Use the official Expo client and a shared auth package. Test iOS, Android and web OAuth/deep-link flows independently.
Near-zero loading startup Strong. Native useSession data is cached in SecureStore specifically to avoid a reload spinner. Web session cookie caching is available. Render from cached identity immediately, mark it provisionally authenticated, then revalidate in the background. Do not block local app data rendering on a session round trip.
Offline-first Partial. Cached identity works offline, but authentication and credential refresh remain online/server-authoritative. Treat offline identity as a local UX capability, not proof of current authorization. Previously synced data can render offline; new privileged access waits for server validation.
Zero integration Strong. Zero officially documents Better Auth cross-subdomain cookie support; Zero also accepts bearer tokens. On web, prefer secure HttpOnly cookies forwarded by zero-cache. On native, use a short-lived Better Auth JWT as Zero's auth token and refresh it through zero.connection.connect({auth}).
Multiple servers Identity sessions can represent users who belong to many product servers. Organization plugin rows are not the product tenancy model. App tables own servers, memberships, invitations, owners and roles; Zero filters use those canonical rows.
Independent admins Better Auth establishes administrator identity but does not grant product administration. App-owned roles and capability checks remain authoritative for every server.
Role/channel access Insufficient as the sole model. Organization roles are organization/team scoped, not a complete Discord-style channel override system. Use the application-owned TRW-derived calculator with rank-ordered roles, first-class attributes, layered allow/deny overrides and entitlement inputs.
Login methods Excellent breadth: email/password, OAuth, magic link, email OTP, phone, passkeys, 2FA, anonymous users and multi-session. Launch with Apple, Google and email magic link or OTP; add password only if product needs it. Add passkeys after mobile/web flows are proven.
Enterprise identity Strong plugin coverage for SSO and SCIM. Defer until demanded. Security advisories show these surfaces need aggressive patching and dedicated tests.
Billing Identity/session data can identify billing actors, but billing authority is outside the auth boundary. The in-repo billing service writes verified billing_events, subscription projections and entitlements. Better Auth has no billing authority.
Feature/server gating Not an identity concern. The app-owned billing and authorization domains derive durable entitlements from verified billing events, sync the resulting rows, and enforce them server-side.
Self-host/control Strong. Framework is open source and free; managed infrastructure is optional. Self-host core auth in the same regional stack as the API/database.

Recommended Boundary

Better Auth owns

Application domain owns

The Key Rule

Authentication establishes who the actor is; the application decides what that actor can read or do.

Session and Sync Design

Web + Zero

  1. Better Auth establishes a secure HttpOnly session cookie.
  2. Configure cross-subdomain cookies only for the required app/zero-cache domains.
  3. Configure Zero query/mutate forwarding for cookies.
  4. Query and mutate endpoints validate the Better Auth session and build a minimal Zero context (userId, possibly server membership version or global admin flag).
  5. Do not embed the user's full dynamic role/permission set in a long-lived session/JWT. Query authoritative membership and entitlement rows inside server query/mutator authorization.
  6. On 401/403, Zero enters needs-auth; refresh the Better Auth session and reconnect.

React Native + Zero

  1. Better Auth's Expo client stores native cookie/session data in SecureStore and returns cached session data immediately.
  2. Exchange the valid Better Auth session for a short-lived JWT from the JWT plugin.
  3. Pass the token to Zero as auth and use the stable user ID as userID.
  4. Refresh the JWT in the background and call zero.connection.connect({auth: nextToken}) without recreating local state for the same user.
  5. On logout or account switch, recreate the Zero instance. Explicitly choose whether to retain or delete local synced data; default to deletion on shared/high-risk devices.

Instant-Start Implications

Better Auth helps remove one specific loading screen: resolving the current user on native startup. It does not provide application data offline. The startup sequence should be:

  1. Open the local sync database and cached auth identity in parallel.
  2. Render the last known server/channel immediately.
  3. Display offline/stale state non-modally if disconnected.
  4. Revalidate auth and reconnect sync in the background.
  5. Apply authorization changes/removals as server deltas arrive.
Do Not Do This

Avoid mounting the whole application behind if (sessionPending) <Spinner />. Cached session data is a startup hint; the locally persisted sync database is what makes the rest of the app instant.

Security and Operational Review

Strengths

Risks

Mandatory Release Gates

Before release, build a narrow integration spike and require these to pass:

  1. Apple and Google login on physical iOS and Android devices plus web.
  2. Cold launch in airplane mode shows cached identity and locally synced app shell without a spinner.
  3. Session/JWT refresh reconnects Zero without discarding or rebuilding local storage.
  4. Logout clears auth and, according to policy, deletes user-scoped local sync data.
  5. Account switching cannot expose the previous user's local rows.
  6. Revoked server membership removes future sync visibility and blocks mutations.
  7. App-owned product invite acceptance cannot be replayed or accepted by another account.
  8. Cross-subdomain cookie configuration works without SameSite=None and without broad wildcard trust.
  9. In-repo billing_events replay is idempotent and entitlement changes propagate through sync.
  10. Pinned stable and scoped package versions have no known unpatched advisory in the deployment lockfile.

Adopted Boundary and Release Gates

Decision

Adopt Better Auth for identity and sessions. Production release remains gated on the checks above.

Recommended stack pairing:

Critical Boundary

Better Auth Organization rows are not canonical for product servers, memberships, invitations, owners or roles. Better Auth remains an identity/session boundary; app-owned authorization and the in-repo billing projection determine synced-data visibility.

Sources