Skip to main content

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:

statusMeaning
disabledThe module was built with auth: false
loadingInitial auth settlement has not completed
authenticatedA usable signed-in identity exists
errorInitial auth resolution failed without a usable identity
anonymousAuth settled without a signed-in identity

isPending is separate. A background refresh can be pending while status remains authenticated.

Queries choose an auth policy

ModeDuring initial auth loadingAuthenticatedAnonymous
requiredWaitRun with identityStay idle
optionalWaitRun with identityRun anonymously
noneDo not waitRun anonymouslyRun anonymously

The fixed default is optional.

ts
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 in

The runtime cannot safely reuse user A's local query and optimistic state for user B. On a stable identity change it:

  1. Clears identity-owned query state synchronously.
  2. Retires the previous primary Convex client.
  3. Creates and confirms a client for the new identity.
  4. 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:

  • query
  • mutation
  • action
  • onUpdate

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.