Auth State and User Data
Read session state and query application profile data from the correct owner.
useConvexAuth() answers whether a usable identity exists and exposes the session user. Product profile data remains an ordinary typed Convex query.
Auth state
const { status, isPending, user, error, client, ready } = useConvexAuth()status describes the current usable identity. isPending describes auth work in flight. Keep them separate in UI logic.
Use these app-lifetime refs for route identity. On the pinned Better Auth tuple, do not mount client.useSession() in route components; remounting its auth-query atom can leave session state stale. client is the one integrated Better Auth client for sign-in, sign-out, and inferred plugin methods. It is null during SSR, so call it from browser-owned handlers after checking availability.
<template>
<p v-if="status === 'loading'">Checking session…</p>
<p v-else-if="status === 'error'">{{ error?.message ?? 'Authentication failed' }}</p>
<p v-else-if="status === 'authenticated'">Welcome, {{ user?.name ?? user?.email }}</p>
<SignInLink v-else-if="status === 'anonymous'" />
</template>Choose a user-data source
| Data | Source |
|---|---|
| Name/email needed immediately during SSR | useConvexAuth().user |
| Product profile, preferences, searchable display data | Explicit application projection query |
| Small identity fields needed in every Convex function | JWT claims |
| Roles and memberships used for authorization | Canonical backend data checked per function |
Do not put frequently changing permissions only in JWT claims. Their value can remain stale until token refresh.
Query a product profile directly
const auth = useConvexAuth()
const profile = useConvexQuery(api.users.getCurrentProfile, {}, { auth: 'required' })
const displayName = computed(
() => profile.data.value?.displayName ?? auth.user.value?.name ?? 'Account',
)The session remains the immediate auth source. The projection remains application-owned Convex data with its own schema, indexes, synchronization, and rebuild story. undefined means the profile query has no value yet; a backend null result is a successful “no profile” result.