Better Auth Evaluation
Fit analysis, recommended boundaries, security caveats, and plugin risks.
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
| Requirement | Better Auth fit | Recommendation |
|---|---|---|
| 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
- User identity and linked provider accounts.
- Credential verification and login flows.
- Browser/native sessions, revocation and device/session listing.
- OAuth state/PKCE and trusted-origin handling.
- Optional passkeys, 2FA, SSO and SCIM.
- Service JWT minting/JWKS for Zero, WebSocket/live-stream services and other backend services.
Application domain owns
- Servers/communities as product entities.
- Memberships, invitations, owners and their complete lifecycle.
- Product roles and ordered role assignment.
- Channel permissions and overrides.
- Course enrollment and learning progression.
- Stream access and moderation capabilities.
- Plans, products, entitlements, server gates and feature gates.
- Audit events required for product moderation/administration.
- Sync visibility filters and mutation authorization.
Authentication establishes who the actor is; the application decides what that actor can read or do.
Session and Sync Design
Web + Zero
- Better Auth establishes a secure HttpOnly session cookie.
- Configure cross-subdomain cookies only for the required app/zero-cache domains.
- Configure Zero query/mutate forwarding for cookies.
- Query and mutate endpoints validate the Better Auth session and build a minimal Zero context (
userId, possibly server membership version or global admin flag). - 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.
- On
401/403, Zero entersneeds-auth; refresh the Better Auth session and reconnect.
React Native + Zero
- Better Auth's Expo client stores native cookie/session data in SecureStore and returns cached session data immediately.
- Exchange the valid Better Auth session for a short-lived JWT from the JWT plugin.
- Pass the token to Zero as
authand use the stable user ID asuserID. - Refresh the JWT in the background and call
zero.connection.connect({auth: nextToken})without recreating local state for the same user. - 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:
- Open the local sync database and cached auth identity in parallel.
- Render the last known server/channel immediately.
- Display offline/stale state non-modally if disconnected.
- Revalidate auth and reconnect sync in the background.
- Apply authorization changes/removals as server deltas arrive.
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
- Passwords use scrypt by default.
- Database-backed sessions support expiry, refresh, revocation and device/session management.
- Secure cookie defaults, origin validation, Fetch Metadata checks, PKCE/state and built-in rate limiting are documented.
- Non-destructive secret rotation is supported.
- Short-lived JWTs plus JWKS enable service verification without a database call on every request.
- Strong project activity: roughly 29k GitHub stars, 14k+ dependents, hundreds of contributors and thousands of commits at evaluation time.
- The company announced a $5M seed round in June 2025, reducing — but not eliminating — maintenance/abandonment risk.
Risks
- Better Auth remains fast-moving. Plugin schemas, migrations and defaults increase upgrade surface.
- The June 2026 security cycle included multiple high/critical advisories across organization invitations, SSO, OAuth provider, device authorization and other plugins. Prompt patching is mandatory.
- Some complete fixes require a beta/next channel or workaround because secure changes are breaking. Do not assume
better-auth@latestalone fixes every scoped package. - Cookie caching improves latency but delays revocation visibility until the cache expires. Use a very short cache or bypass it for sensitive actions.
- Native cookie, OAuth callback, deep-link and passkey behavior has more moving parts than web. It needs real-device E2E coverage.
- Do not follow the Bearer plugin documentation's
localStorageexample for browser session tokens. Prefer HttpOnly cookies on web and SecureStore/Keychain-backed storage on native. - The Organization plugin has already had invitation-ownership security issues. Product authorization must have independent regression tests even if plugin endpoints are used.
Mandatory Release Gates
Before release, build a narrow integration spike and require these to pass:
- Apple and Google login on physical iOS and Android devices plus web.
- Cold launch in airplane mode shows cached identity and locally synced app shell without a spinner.
- Session/JWT refresh reconnects Zero without discarding or rebuilding local storage.
- Logout clears auth and, according to policy, deletes user-scoped local sync data.
- Account switching cannot expose the previous user's local rows.
- Revoked server membership removes future sync visibility and blocks mutations.
- App-owned product invite acceptance cannot be replayed or accepted by another account.
- Cross-subdomain cookie configuration works without
SameSite=Noneand without broad wildcard trust. - In-repo
billing_eventsreplay is idempotent and entitlement changes propagate through sync. - Pinned stable and scoped package versions have no known unpatched advisory in the deployment lockfile.
Adopted Boundary and Release Gates
Adopt Better Auth for identity and sessions. Production release remains gated on the checks above.
Recommended stack pairing:
- Better Auth + Postgres for identity/session persistence.
- Secure cookies on web; SecureStore-backed native session handling.
- Better Auth JWT/JWKS for native Zero and service authentication.
- Zero remains responsible for application data synchronization.
- App-owned authorization and entitlement engine remains authoritative.
- The in-repo billing service owns verified billing events and subscription/entitlement projections.
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
- https://better-auth.com/docs/introduction
- https://better-auth.com/docs/integrations/expo
- https://better-auth.com/docs/concepts/session-management
- https://better-auth.com/docs/concepts/database
- https://better-auth.com/docs/reference/security
- https://better-auth.com/docs/plugins
- https://better-auth.com/docs/plugins/organization
- https://better-auth.com/docs/plugins/jwt
- https://better-auth.com/docs/plugins/passkey
- https://zero.rocicorp.dev/docs/auth
- https://better-auth.com/blog/security-update-june-2026
- https://github.com/better-auth/better-auth