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

# Hooks Reference

> All five hooks — useAuth, useUser, useSessions, useMerchant, and useRoles — with return shapes and usage examples.

Both hooks require `<AilitaProvider>` and `<AuthProvider>` ancestors. These are already
provided if you used the standard `<AilitaProvider>` setup — it wraps children with both
providers automatically. `useAuth` provides authentication state and actions. `useUser`
provides profile management for the signed-in user.

## useAuth

`useAuth` is the primary hook for authentication state and actions. It returns the current
user, authentication status, and methods to login, logout, and signup.

### Return Shape

```typescript theme={null}
interface UseAuthReturn {
  user: UserResponse | null
  isAuthenticated: boolean
  isLoading: boolean
  login: (email: string, password?: string, extraData?: Partial<LoginRequest>) => Promise<LoginResponse>
  logout: () => Promise<void>
  signup: (data: Omit<SignupRequest, 'app' | 'mid'>) => Promise<SignupResponse | null>
  error: string | null
  clearError: () => void
}
```

<ParamField body="user" type="UserResponse | null">
  The authenticated user object. `null` while loading or when not authenticated.

  <Warning>
    **This is the authoritative source for user data.** After login, `useAuth` calls `getMe()`
    to populate `user` with the full `UserResponse` from the server. Do not store the `userId`
    from `LoginForm.onSuccess` and fetch user data yourself — `useAuth().user` is already
    populated and stays in sync across components.
  </Warning>
</ParamField>

<ParamField body="isAuthenticated" type="boolean">
  `true` when the user is logged in with valid tokens.
</ParamField>

<ParamField body="isLoading" type="boolean">
  `true` during silent token refresh on mount and during login/logout operations.

  <Note>
    On app mount, `isLoading` is `true` for 200–500 ms while the library attempts silent
    refresh. This is why `AuthGuard` requires a `loadingFallback` — without it, the
    flash-redirect anti-pattern occurs (the user sees a redirect to `/login` before the
    refresh completes).
  </Note>
</ParamField>

<ParamField body="login" type="(email, password?, extraData?) => Promise<LoginResponse>">
  Initiates login. Returns the full `LoginResponse` for branch detection (e.g., checking
  whether a challenge is required). Tokens are set automatically on a successful password
  login. `extraData` passes additional fields (such as `loginMode`) to the API.
</ParamField>

<ParamField body="logout" type="() => Promise<void>">
  Calls the server logout endpoint, then clears local tokens and auth state. Logout errors
  are swallowed — local state is always cleaned up.
</ParamField>

<ParamField body="signup" type="(data: Omit<SignupRequest, 'app' | 'mid'>) => Promise<SignupResponse | null>">
  Initiates signup. The `app` and `mid` fields are injected from the merchant context
  automatically — you do not need to pass them.
</ParamField>

<ParamField body="error" type="string | null">
  Last error message from a login, signup, or logout operation. Cleared automatically
  at the start of the next attempt.
</ParamField>

<ParamField body="clearError" type="() => void">
  Manually clear the `error` state (useful when navigating away from a form).
</ParamField>

### Common Patterns

<CodeGroup>
  ```tsx Next.js App Router theme={null}
  'use client'
  import { useAuth } from 'ailita-library'

  export default function DashboardPage() {
    const { user, isAuthenticated, logout } = useAuth()

    if (!isAuthenticated) return null // AuthGuard handles redirect

    return (
      <div>
        <h1>Welcome, {user?.firstName}</h1>
        <p>{user?.email}</p>
        <button onClick={logout}>Log out</button>
      </div>
    )
  }
  ```

  ```tsx React Router theme={null}
  import { useAuth } from 'ailita-library'

  function DashboardPage() {
    const { user, isAuthenticated, logout } = useAuth()

    if (!isAuthenticated) return null // AuthGuard handles redirect

    return (
      <div>
        <h1>Welcome, {user?.firstName}</h1>
        <p>{user?.email}</p>
        <button onClick={logout}>Log out</button>
      </div>
    )
  }
  ```
</CodeGroup>

<Note>
  `user` may be `null` briefly during the initial silent refresh, even inside an
  AuthGuard-protected route. Always use optional chaining (`user?.firstName`) or check
  `isAuthenticated` first.
</Note>

### Anti-pattern: userId from onSuccess

A common mistake is using the `userId` returned by `onSuccess` to fetch user data
independently. This creates a redundant network request and a separate copy of user state
that can drift from what the library manages.

<Warning>
  Do not fetch user data from `onSuccess`. `useAuth().user` is already populated after
  login completes — no additional fetch is needed.
</Warning>

```tsx theme={null}
// WRONG — do not do this
<LoginForm
  onSuccess={async (userId) => {
    const response = await fetch(`/api/users/${userId}`)
    const userData = await response.json()
    setUser(userData) // Redundant — useAuth().user already has this
  }}
/>
```

```tsx theme={null}
// CORRECT — useAuth().user is already populated after login
<LoginForm onSuccess={() => navigate('/dashboard')} />

// In your dashboard:
const { user } = useAuth()  // Already populated with full UserResponse
```

***

## useUser

`useUser` provides methods for the authenticated user to manage their own profile, password,
email, and TOTP settings. It shares the same `user` object as `useAuth` — both read from
`AuthContext`, so updates are reflected everywhere immediately.

### Return Shape

```typescript theme={null}
return {
  user,           // UserResponse | null
  isLoading,      // boolean
  error,          // string | null
  refreshProfile, // () => Promise<UserResponse>
  updateProfile,  // (data: UpdateUserRequest) => Promise<UserResponse>
  updatePassword, // (data: ChangePasswordRequest) => Promise<void>
  startEmailChange, // (newEmail: string) => Promise<EmailChangeResponse>
  initTotp,       // () => Promise<TotpSetupResponse>
  verifyTotp,     // (code: string) => Promise<void>
  removeTotp,     // (code: string) => Promise<void>
  clearError,     // () => void
}
```

<ParamField body="user" type="UserResponse | null">
  The same reference as `useAuth().user`. Reflects the latest profile state.
</ParamField>

<ParamField body="isLoading" type="boolean">
  Loading state for profile operations (not auth loading). `true` while an update,
  password change, email change, or TOTP operation is in progress.
</ParamField>

<ParamField body="error" type="string | null">
  Last error message from a profile operation. Cleared automatically on the next attempt.
</ParamField>

<ParamField body="refreshProfile" type="() => Promise<UserResponse>">
  Re-fetches `GET /users/me` and updates the shared user state. Use this after external
  changes or to force a sync with the server.
</ParamField>

<ParamField body="updateProfile" type="(data: UpdateUserRequest) => Promise<UserResponse>">
  Updates `firstName`, `lastName`, and/or `phone`. Returns the updated user.

  <Note>
    The shared user state is updated automatically — all components reading `useAuth().user`
    or `useUser().user` will reflect the change without a page refresh or manual refetch.
  </Note>
</ParamField>

<ParamField body="updatePassword" type="(data: ChangePasswordRequest) => Promise<void>">
  Changes the user's password. Requires `currentPassword` and `newPassword`. If the user
  has TOTP enabled, `totpCode` is also required.
</ParamField>

<ParamField body="startEmailChange" type="(newEmail: string) => Promise<EmailChangeResponse>">
  Initiates an email address change. Returns a `challengeId` that you pass to `ChallengeView`
  to complete verification via the OTP sent to the new address.
</ParamField>

<ParamField body="initTotp" type="() => Promise<TotpSetupResponse>">
  Starts TOTP setup. Returns a `qrUri` to render as a QR code and `backupCodes` to display
  to the user before they scan.
</ParamField>

<ParamField body="verifyTotp" type="(code: string) => Promise<void>">
  Confirms TOTP setup with a 6-digit code from the authenticator app. Automatically calls
  `refreshProfile()` — `user.totpEnabled` will be `true` after this resolves.
</ParamField>

<ParamField body="removeTotp" type="(code: string) => Promise<void>">
  Disables TOTP with a 6-digit verification code. Automatically calls `refreshProfile()` —
  `user.totpEnabled` will be `false` after this resolves.
</ParamField>

<ParamField body="clearError" type="() => void">
  Manually clear the `error` state.
</ParamField>

### Profile Update Example

```tsx theme={null}
import { useState } from 'react'
import { useUser } from 'ailita-library'

function ProfilePage() {
  const { user, updateProfile, isLoading, error } = useUser()
  const [firstName, setFirstName] = useState(user?.firstName ?? '')

  const handleSave = async () => {
    await updateProfile({ firstName })
    // user state is already updated — no need to refetch
  }

  return (
    <form onSubmit={(e) => { e.preventDefault(); handleSave() }}>
      <input value={firstName} onChange={(e) => setFirstName(e.target.value)} />
      <button disabled={isLoading}>{isLoading ? 'Saving...' : 'Save'}</button>
      {error && <p className="text-red-600">{error}</p>}
    </form>
  )
}
```

### Password Change Example

```tsx theme={null}
const { updatePassword } = useUser()

await updatePassword({
  currentPassword: 'old-password',
  newPassword: 'new-password',
  totpCode: '123456', // Only required if the user has TOTP enabled
})
```

### Key Types

```typescript theme={null}
interface UserResponse {
  id: string
  email: string
  firstName: string
  lastName: string
  phone: string | null
  status: UserStatus
  emailStatus: EmailStatus
  totpEnabled: boolean
  app: string
  mid: string
  createdAt: string
  updatedAt: string
  lastLoginAt?: string | null
  roles?: RoleResponse[]
}

interface UpdateUserRequest {
  firstName?: string
  lastName?: string
  phone?: string
}

interface ChangePasswordRequest {
  currentPassword: string
  newPassword: string
  totpCode?: string
}

interface TotpSetupResponse {
  qrUri: string
  backupCodes: string[]
}

interface EmailChangeResponse {
  challengeId: string
  devToken?: string
}
```

***

## useSessions

`useSessions` fetches the authenticated user's active sessions on mount and exposes revoke
methods. It is the hook behind the `SessionsList` profile component — use it directly when
you need programmatic session management rather than the prebuilt UI.

### Return Shape

```typescript theme={null}
return {
  sessions,   // SessionResponse[]
  isLoading,  // boolean
  error,      // string | null
  refresh,    // () => Promise<void>
  revoke,     // (sessionId: string) => Promise<void>
  revokeAll,  // () => Promise<void>
}
```

<ParamField body="sessions" type="SessionResponse[]">
  Array of active sessions for the current user. Each entry includes a `current: boolean`
  field that identifies the session belonging to this device/browser.
</ParamField>

<ParamField body="isLoading" type="boolean">
  `true` while the initial sessions fetch or a revoke operation is in progress.
</ParamField>

<ParamField body="error" type="string | null">
  Last error message from a fetch or revoke operation.
</ParamField>

<ParamField body="refresh" type="() => Promise<void>">
  Re-fetches `GET /users/me/sessions` and updates the local sessions array.
</ParamField>

<ParamField body="revoke" type="(sessionId: string) => Promise<void>">
  Revokes a single session by ID. Removes the session from local state on success. Throws
  on error — wrap in `try/catch` if you need to handle revoke failures.
</ParamField>

<ParamField body="revokeAll" type="() => Promise<void>">
  Revokes all sessions (except the current one, depending on backend behavior). Clears the
  local sessions array on success. Throws on error.
</ParamField>

<Card title="Session Management Guide" icon="clock-rotate-left" href="/ailita/session-management">
  Full useSessions usage guide including onSessionExpired contract and the complete sessions list UI example.
</Card>

***

## useMerchant

`useMerchant` is an admin hook for reading and updating the tenant merchant record. It is
used inside `MerchantSettings` — use it directly when you need to read merchant configuration
or build custom settings UI.

### Return Shape

```typescript theme={null}
return {
  merchant,     // MerchantResponse | null
  isLoading,    // boolean
  error,        // string | null
  refresh,      // () => Promise<void>
  update,       // (data: UpdateMerchantRequest) => Promise<MerchantResponse>
  addApiKey,    // (label: string) => Promise<GenerateApiKeyResponse>
  removeApiKey, // (keyId: string) => Promise<void>
  clearError,   // () => void
}
```

<ParamField body="merchant" type="MerchantResponse | null">
  The loaded merchant record. `null` while loading on mount. Contains tenant settings
  including `primaryColor`, `secondaryColor`, `loginMode`, `signupMode`, and `totpAllowed`.
</ParamField>

<ParamField body="isLoading" type="boolean">
  `true` during the initial fetch and while an `update` call is in progress.
</ParamField>

<ParamField body="error" type="string | null">
  Last error message from any operation. Clear manually with `clearError()` or automatically
  at the start of the next operation.
</ParamField>

<ParamField body="refresh" type="() => Promise<void>">
  Re-fetches `GET /merchants/me` and updates the local merchant state.
</ParamField>

<ParamField body="update" type="(data: UpdateMerchantRequest) => Promise<MerchantResponse>">
  Updates the merchant record via `PATCH /merchants/:id`. Returns the updated merchant.

  <Note>
    Calling `update()` automatically applies the updated `primaryColor` and `secondaryColor`
    to the current session via `applyTheme()`. You do not need to call any theme method
    manually — the UI updates immediately.
  </Note>
</ParamField>

<ParamField body="addApiKey" type="(label: string) => Promise<GenerateApiKeyResponse>">
  Generates a new API key for this merchant. Throws `'No merchant loaded'` if called before
  mount completes — guard with `if (!merchant) return` before calling.
</ParamField>

<ParamField body="removeApiKey" type="(keyId: string) => Promise<void>">
  Revokes an API key by its ID. Throws `'No merchant loaded'` if the merchant has not
  loaded yet.
</ParamField>

<ParamField body="clearError" type="() => void">
  Manually clear the `error` state.
</ParamField>

### Quick Example

```tsx theme={null}
import { useMerchant } from 'ailita-library'

function MerchantNameForm() {
  const { merchant, update, isLoading, error } = useMerchant()

  const handleSave = async (newName: string) => {
    await update({ name: newName })
    // merchant state is updated — no manual refetch needed
  }

  if (!merchant) return null

  return (
    <form onSubmit={(e) => { e.preventDefault(); handleSave(e.currentTarget.name.value) }}>
      <input name="name" defaultValue={merchant.name} />
      <button disabled={isLoading}>{isLoading ? 'Saving...' : 'Save'}</button>
      {error && <p className="text-red-600">{error}</p>}
    </form>
  )
}
```

***

## useRoles

`useRoles` is an admin hook for managing role definitions and assigning roles to users.
It fetches all roles for the app on mount (both SYSTEM and MERCHANT roles). Use it to
build role management UI in admin dashboards.

### Return Shape

```typescript theme={null}
return {
  roles,     // RoleResponse[]
  isLoading, // boolean
  error,     // string | null
  refresh,   // () => Promise<void>
  add,       // (data: CreateRoleRequest) => Promise<RoleResponse>
  remove,    // (roleId: string) => Promise<void>
  assign,    // (data: AssignRoleRequest) => Promise<void>
  revoke,    // (userId: string, roleId: string) => Promise<void>
}
```

<ParamField body="roles" type="RoleResponse[]">
  All roles defined for this app — both `SYSTEM` (built-in) and `MERCHANT` (custom) roles.
</ParamField>

<ParamField body="isLoading" type="boolean">
  `true` during the initial roles fetch.
</ParamField>

<ParamField body="error" type="string | null">
  Last error message from a fetch operation.
</ParamField>

<ParamField body="refresh" type="() => Promise<void>">
  Re-fetches `GET /roles` and updates the local roles array.
</ParamField>

<ParamField body="add" type="(data: CreateRoleRequest) => Promise<RoleResponse>">
  Creates a new MERCHANT-owned role and appends it to the local roles array. Returns the
  created role including its generated `id`.
</ParamField>

<ParamField body="remove" type="(roleId: string) => Promise<void>">
  Deletes a MERCHANT-owned role and removes it from the local array.

  <Note>
    Only MERCHANT-owned roles (`role.owner === 'MERCHANT'`) can be deleted. SYSTEM roles
    are built-in and cannot be removed.
  </Note>
</ParamField>

<ParamField body="assign" type="(data: AssignRoleRequest) => Promise<void>">
  Assigns a role to a user. Accepts an optional `expiresAt` ISO 8601 string for
  time-limited role grants. Does NOT update the local roles array — the role list is
  app-level, not user-level.
</ParamField>

<ParamField body="revoke" type="(userId: string, roleId: string) => Promise<void>">
  Removes a role from a user. Does NOT update the local roles array.
</ParamField>

### SYSTEM vs MERCHANT Role Hierarchy

| Owner      | Description                                  | Can be deleted      | Example                |
| ---------- | -------------------------------------------- | ------------------- | ---------------------- |
| `SYSTEM`   | Built-in roles defined by the ailita backend | No                  | `ADMIN`, `USER`        |
| `MERCHANT` | Custom roles created by your tenant          | Yes, via `remove()` | `MODERATOR`, `PREMIUM` |

<Note>
  The `priority` field determines role precedence when conflicts occur — lower number =
  higher priority. SYSTEM roles typically have priority 1–10; assign MERCHANT roles
  priorities starting at 100 to avoid conflicts.
</Note>

### Role Checking vs Role Management

<Warning>
  Do not use `useRoles` to check whether the current user has a specific role. `useRoles`
  is for managing role definitions and assignments — not for access control decisions.

  For route protection, use `AuthGuard` with `requiredRoles`:

  ```tsx theme={null}
  <AuthGuard fallback={<Navigate to='/login' />} requiredRoles={['ADMIN']}>
    <AdminPanel />
  </AuthGuard>
  ```

  `requiredRoles` uses AND logic — the user must have ALL listed roles. For programmatic
  role checks, read `useAuthContext().roles` directly.
</Warning>

### Quick Example: Assigning a Role

```tsx theme={null}
import { useMerchant, useRoles } from 'ailita-library'

function AssignRoleButton({ userId, roleId }: { userId: string; roleId: string }) {
  const { merchant } = useMerchant()
  const { assign } = useRoles()

  const handleAssign = async () => {
    if (!merchant) return
    await assign({
      userId,
      roleId,
      app: merchant.app,
      mid: merchant.mid,
    })
  }

  return <button onClick={handleAssign}>Assign role</button>
}
```

<Note>
  `assign()` requires `app` and `mid` values from `useMerchant().merchant`. Do not hardcode
  these — always read them from the loaded merchant record to ensure you are assigning to
  the correct tenant.
</Note>

***

## Next steps

<Card title="Session Management" icon="clock-rotate-left" href="/ailita/session-management">
  onSessionExpired contract, useSessions full guide, and active sessions list example.
</Card>

<Card title="Merchant & Roles Reference" icon="building" href="/ailita/merchant-roles">
  Deep-dive reference for useMerchant and useRoles with API key management and role assignment patterns.
</Card>

<Card title="Auth Flows" icon="key" href="/ailita/auth-flows">
  Complete login state machine and ChallengeView dual-response contract.
</Card>
