Skip to main content

Sign in and sign out

Run integrated Better Auth operations and handle their identity lifecycle.

useConvexAuth().client is the Better Auth client. The module wraps it, so the Convex client follows every session change that the client makes.

client is always present. Call its methods from browser event handlers. During server rendering, a method call throws a ConvexCallError with code CLIENT_UNAVAILABLE. For GitHub and Google, see Social sign-in.

Email sign-in

ts
const { client, pending } = useConvexAuth()

const result = await client.signIn.email({
  email: email.value,
  password: password.value,
})

if (result.error) {
  formError.value = 'Sign in could not be completed'
}

Do not display raw credential errors if they reveal whether an account exists.

Sign-up

ts
const { client } = useConvexAuth()

const result = await client.signUp.email({
  name: name.value,
  email: email.value,
  password: password.value,
})

Sign-up does not sign the user in, because createBetterConvexAuth always sets autoSignIn: false. Send the user to the sign-in page next. With emailAndPassword.requireEmailVerification, the user must verify the email address first. Together, email verification and autoSignIn determine the next product step. Passwords need at least 15 characters.

Sign-out

ts
const { client } = useConvexAuth()

await client.signOut()
await navigateTo('/')

Your application decides where to navigate. The module orders the session operations, clears the previous user's query data, and replaces the Convex client.

Wait for the auth state

Queries already wait for auth. Use ready() only when other code must wait until the first auth state is known:

ts
const status = await useConvexAuth().ready({ timeoutMs: 5_000 })

Do not call ready() before every query.

Pending work and auth status

pending can be true while status stays authenticated. Keep the signed-in UI visible during a background refresh.

Failures across identity changes

A mutation, action, or useConvex() call that is still running when the user changes rejects with IDENTITY_CHANGED. Do not show its result to the new user. Do not retry a write automatically, because it may already have succeeded.