# Client-side authentication

## Choose an API

| Need | API |
| --- | --- |
| Session state and lifecycle actions | `useUserSession()` |
| Direct Better Auth client or plugin methods | `useAuthClient()` |
| Sign-in or sign-up form state | `useSignIn()` or `useSignUp()` |
| Action state for a client or plugin method | `useAuthClientAction()` |
| Action state for any async function | `useAction()` |
| Refresh session state after a custom auth endpoint | `runWithSessionRefresh()` |
| Request-scoped auth fetch with typed endpoints | `useAuthRequestFetch()` |
| SSR-safe auth-bound data | `useAuthAsyncData()` |

`useUserSessionState()` is a deprecated alias for `useUserSession()`.

## Session state and direct client access

```ts
const { user, session, loggedIn, ready, fetchSession, signOut, updateUser } = useUserSession()
const client = useAuthClient()
```

`useUserSession()` is safe to return from a Pinia setup store. `useAuthClient()` returns the client in the browser and `null` during SSR.

## Sign-in and sign-up forms

Use action composables for form flows that need loading, error, and success state.

```ts
const signInEmail = useSignIn('email')

await signInEmail.execute({
  email: 'user@example.com',
  password: 'password123',
})
```

```ts
const signUpEmail = useSignUp('email')
await signUpEmail.execute({ email, password, name })
```

If no `onSuccess` callback is passed, sign-in and sign-up can redirect to a safe local `?redirect=...` target or the configured authenticated redirect.

## Plugin client actions

Use `useAuthClientAction()` for Better Auth client/plugin methods that should expose action state.

```ts
const openPortal = useAuthClientAction(client => client.customer.portal)
await openPortal.execute()
```

Use `useAction()` for the same action state around an arbitrary async function.

## Custom auth endpoints

Use `runWithSessionRefresh()` around custom endpoints that create or change the current session.

```ts
await runWithSessionRefresh(() =>
  $fetch('/api/custom-login', {
    method: 'POST',
    body: { email, password },
  }),
)
```

The helper awaits your request, then refreshes local session state unless the result is a Better Auth action error result.

## Auth-bound data

Use `useAuthRequestFetch()` for low-level request control. During SSR it forwards the request context, including cookies.

The endpoint below comes from a configured custom customer plugin. Replace it with an endpoint generated by the consumer's Better Auth config.

```ts
const requestFetch = useAuthRequestFetch()
const customerState = await requestFetch('/api/auth/customer/state')
```

Use `useAuthAsyncData()` for keyed SSR data with Nuxt loading and error state. It requires an authenticated user by default and resolves to `null` without calling the endpoint when the user is signed out.

```ts
const { data, pending, error } = await useAuthAsyncData(
  'customer-state',
  requestFetch => requestFetch('/api/auth/customer/state'),
)
```

## Force refresh

```ts
await fetchSession({ force: true })
```

Use `force: true` when the server-side session payload changed and Better Auth's cookie cache should be bypassed for this fetch.

## Redirect rule

If you read `route.query.redirect`, validate it before navigating. Only allow local paths.

## BetterAuthState

`<BetterAuthState>` renders once session hydration completes (`ready === true`) and supports a loading placeholder.

```vue
<BetterAuthState>
  <template #default="{ loggedIn, user, signOut }">
    <p v-if="loggedIn">Hi {{ user?.name }}</p>
    <button v-else @click="navigateTo('/login')">Sign in</button>
  </template>
  <template #placeholder>
    <p>Loading...</p>
  </template>
</BetterAuthState>
```
