> ## Documentation Index
> Fetch the complete documentation index at: https://docs.deep.space/llms.txt
> Use this file to discover all available pages before exploring further.

# Client auth reference

> Hooks, providers, and components for authentication in React.

The auth surface is exported from `deepspace`. The Better Auth client is wrapped in a React provider, with hooks and components for everything you'd build by hand.

```ts theme={null}
import {
  DeepSpaceAuthProvider,
  AuthGate, AuthOverlay,
  SignedIn, SignedOut, GuestBanner,
  useAuth, useAuthUser, useUser, useDisplayName,
  signIn, signOut, getAuthToken, clearAuthToken,
  authClient, useSession,
} from 'deepspace'
```

## Providers

### `DeepSpaceAuthProvider`

Wraps the tree and initializes the Better Auth client. Required as an ancestor of every auth hook and component.

```tsx theme={null}
<DeepSpaceAuthProvider>
  <App />
</DeepSpaceAuthProvider>
```

The scaffolded `_app.tsx` already mounts this provider at the root. You don't normally render it yourself.

## Hooks

### `useAuth(): AuthState`

Primary auth-state hook. Session-based; updates immediately on sign-in / sign-out.

| Field        | Type             | Description                                                                                                                                                                                                                                              |
| ------------ | ---------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `isLoaded`   | `boolean`        | `true` once the first Better Auth session check resolves, and **stays true** for the page's lifetime - does not flap back to `false` on background refetches or tab refocus. Safe to gate `RecordProvider` / data-layer mounts on without remount churn. |
| `isSignedIn` | `boolean`        | The canonical signed-in check.                                                                                                                                                                                                                           |
| `userId`     | `string \| null` | JWT subject; null when signed out.                                                                                                                                                                                                                       |
| `sessionId`  | `string \| null` | Better Auth session ID.                                                                                                                                                                                                                                  |

```tsx theme={null}
const { isLoaded, isSignedIn, userId } = useAuth()
if (!isLoaded) return <Skeleton />
if (!isSignedIn) return <SignInPrompt />
```

### `useAuthUser(): { isLoaded, isSignedIn, user }`

Returns the auth-layer user from Better Auth's session - `{ id, fullName, firstName, primaryEmailAddress, ... } | null`. Use when you need fields from the OAuth provider's profile.

Different from `useUser()`, which merges in storage-layer fields like role and karma.

### `useUser(): { user, isLoading, refetch }`

Returns the storage-layer user, merged with the room-specific role from the app's users collection. Karma and credits are loaded from the API as nested objects when available.

```ts theme={null}
type UserKarma = {
  total: number
  breakdown: { publishing: number; content: number; comment: number; curation: number }
  rank: number
  monthlyKarma: number
  monthlyRank: number
}

type UserCredits = {
  total: number
  subscription: number
  bonus: number
  purchased: number
}

type StorageUser = {
  id: string
  name: string
  email: string
  /** Free-form role string from the user-roles collection; defaults to `'viewer'`. */
  role: string
  imageUrl?: string
  isAdmin?: boolean
  publicUsername?: string | null
  subscriptionTier?: string | null
  subscriptionStatus?: string | null
  karma?: UserKarma | null
  credits?: UserCredits | null
}
```

```tsx theme={null}
const { user, isLoading } = useUser()
if (isLoading) return null
if (user?.role !== 'admin') return <p>Admins only</p>

// Karma and credits are objects, not numbers:
const totalKarma = user?.karma?.total ?? 0
const availableCredits = user?.credits?.total ?? 0
```

<Warning>
  `useUser` reads the merged profile + room-role from the record store, so it must be rendered inside a `RecordProvider` ancestor (not just `DeepSpaceAuthProvider`). Outside a `RecordProvider`, `user` will be `null`.
</Warning>

<Warning>
  Destructure as `const { user } = useUser()` and read fields off `user`. **Don't** write `const { id } = useUser()` - `id` is not at the top level of the return.
</Warning>

### `useDisplayName(): string | null`

Resolves the best available display name (full name → first name → email username). Returns `null` while loading or signed out.

### `useSession()` and `authClient`

Re-exports from Better Auth for advanced flows (custom OAuth providers, magic links, etc.).

## Components

### `<AuthOverlay providers={...} />`

Modal sign-in UI. Render without an `onClose` prop and gate on `!isSignedIn`; auto-hides when signed in.

| Prop        | Type                          | Description                                                                                                                          |
| ----------- | ----------------------------- | ------------------------------------------------------------------------------------------------------------------------------------ |
| `providers` | `Array<'github' \| 'google'>` | Which OAuth buttons to show. Defaults to `['github', 'google']`. Email/password sign-in is always available below the OAuth options. |
| `onClose`   | `() => void`                  | If provided, renders a close button. Omit for non-dismissible.                                                                       |

```tsx theme={null}
<AuthOverlay providers={['google', 'github']} />
```

### `<AuthGate fallback={...} redirectOnSignOut={...} />`

Renders `children` when signed in; renders `fallback` (default: `<AuthOverlay />`) otherwise.

| Prop                | Type        | Description                                                                  |
| ------------------- | ----------- | ---------------------------------------------------------------------------- |
| `fallback`          | `ReactNode` | UI shown to signed-out users. Defaults to non-dismissible `<AuthOverlay />`. |
| `redirectOnSignOut` | `string`    | Where the user lands on sign-out. Default `'/'`. Triggers a full reload.     |

```tsx theme={null}
<AuthGate fallback={<TeaserPage />}>
  <ProtectedContent />
</AuthGate>
```

### `<SignedIn>` and `<SignedOut>`

Conditional rendering helpers:

```tsx theme={null}
<SignedIn><UserMenu /></SignedIn>
<SignedOut><SignInButton /></SignedOut>
```

### `<GuestBanner />`

A small inline banner prompting sign-in for anonymous visitors.

## Functions

### `signIn` / `signOut`

Re-exports from Better Auth.

```ts theme={null}
import { signOut } from 'deepspace'
await signOut()
```

### `getAuthToken(): Promise<string | null>`

Returns the current JWT, refreshing it from the auth worker if necessary. Attach to outbound `fetch` calls:

```ts theme={null}
const r = await fetch('/api/premium', {
  headers: { Authorization: `Bearer ${await getAuthToken()}` },
})
```

### `clearAuthToken(): void`

Clears the cached JWT. Forces the next `getAuthToken()` call to fetch a fresh one. Useful in tests or after explicit session changes.

## Patterns

### Auth-state checks

```tsx theme={null}
const { isLoaded, isSignedIn } = useAuth()
if (!isLoaded) return <Skeleton />
return isSignedIn ? <App /> : <Landing />
```

### Profile access

```tsx theme={null}
const { user, isLoading } = useUser()
if (isLoading) return null
return user ? <Hi name={user.name} /> : <SignInPrompt />
```

### Sign-out button

```tsx theme={null}
import { signOut } from 'deepspace'

<button onClick={() => signOut()}>Sign out</button>
```

## See also

* [Authentication guide](/guides/authentication) - public, gated, and mixed configurations
* [Worker auth reference](/sdk-reference/worker/auth) - `verifyJwt` for custom routes
