Authentication and Identity
Understand auth state, query auth modes, identity isolation, and client replacement.
Authentication answers “which session is valid?” Identity isolation answers “which query state is safe to keep?” They are related but not interchangeable.
Better Auth owns the session
Better Auth resolves the session, performs sign-in and sign-out, and exposes configured plugin methods. Better Convex Nuxt observes the public session state and coordinates the identity used by Convex.
useConvexAuth() exposes the current usable state:
status | Meaning |
|---|---|
disabled | The module was built with auth: false |
loading | Initial auth settlement has not completed |
authenticated | A usable signed-in identity exists |
error | Initial auth resolution failed without a usable identity |
anonymous | Auth settled without a signed-in identity |
isPending is separate. A background refresh can be pending while status remains authenticated.
Queries choose an auth policy
| Mode | During initial auth loading | Authenticated | Anonymous |
|---|---|---|---|
required | Wait | Run with identity | Stay idle |
optional | Wait | Run with identity | Run anonymously |
none | Do not wait | Run anonymously | Run anonymously |
The fixed default is optional.
const account = await useConvexQuery(
api.accounts.current,
{},
{
auth: 'required',
},
)
const marketing = await useConvexQuery(
api.pages.home,
{},
{
auth: 'none',
},
)Use none only when the function must never receive identity. In an auth-enabled app, live none queries use a dedicated client that is never authenticated.
Identity changes are hard boundaries
Consider this browser sequence:
anonymous
→ user A signs in
→ user A signs out
→ user B signs inThe runtime cannot safely reuse user A's local query and optimistic state for user B. On a stable identity change it:
- Clears identity-owned query state synchronously.
- Retires the previous primary Convex client.
- Creates and confirms a client for the new identity.
- Rebinds supported listeners to the current client.
Same-user token refresh does not create a new identity generation. It refreshes authentication without discarding valid same-user state.
Why useConvex() is a handle
A raw client reference would become stale after replacement. useConvex() instead returns a stable object with four methods:
querymutationactiononUpdate
Each call dispatches to the current client. A call that crosses an identity generation rejects with an authentication ConvexCallError carrying code IDENTITY_CHANGED.
SSR auth avoids a presentation flash
The server plugin resolves auth for the incoming request and includes the usable auth snapshot in Nuxt state. The browser hydrates that state before continuing auth observation.
This can avoid an anonymous-to-authenticated flash. It does not make an authenticated page safe to cache publicly.
Authorization is the next boundary
A valid identity is input to authorization, not the authorization decision. Convex functions still verify ownership, membership, and role rules.
Continue with backend authorization.