---
title: "@playstack/auth-react"
description: "React session state, client auth actions, revalidation, and cross-tab synchronization."
tags: ["package","identity","auth","react","expo","secure-store","free"]
---

{/* package-access:start */}

> **Free.** MIT licensed. Check preview availability before installing. See [package access](/docs/packages#access-policy).

{/* package-access:end */}

`@playstack/auth-react` provides headless session state, client actions, revalidation, and cross-tab synchronization around a server-owned `@playstack/auth` session.

## Install and compose

```sh
npm install @playstack/auth @playstack/auth-react react
```

```tsx
import {
  PlaystackAuthProvider,
  createFetchAuthTransport,
} from '@playstack/auth-react'

const transport = createFetchAuthTransport({
  baseUrl: 'https://api.example.com',
  headers: () => ({ 'x-csrf-token': readCsrfCookie() }),
})

<PlaystackAuthProvider transport={transport} initialSession={initialSession}>
  <App />
</PlaystackAuthProvider>
```

Omit `initialSession` to fetch on mount, pass `null` when SSR proved the request unauthenticated, or pass a validated session to render authenticated immediately.

## Provider properties

| Property | Default | Purpose |
| --- | --- | --- |
| `transport` | Required | Server communication bridge. |
| `initialSession` | `undefined` | Initial safe projection; controls the first loading state. |
| `revalidateOnFocus` | `true` | Refresh state when the window regains focus. |
| `refreshBeforeMs` | `60000` | Refresh this long before session expiry. |
| `sync` | Browser sync | Custom `AuthSync`, default cross-tab sync, or `false`. |
| `onError` | None | Isolated error observer. |

`useSession()` returns status, session, user, error, and `revalidate`. `useAuth()` returns `signIn`, `signOut`, `refresh`, and `replaceSession`. `refresh` re-establishes the session through the transport's refresh path and applies the result; call it after the server rotated the credential, such as an account switch with the bearer transport. Every replacement is validated against `AuthSessionView`.

## Transport bridge

```ts
interface AuthTransport {
  client: unknown
  getSession(): Promise<AuthSessionView | null>
  refresh(): Promise<AuthSessionView | null>
  signIn(input?: unknown): Promise<AuthSessionView>
  signOut(): Promise<void>
}
```

The included fetch transport defaults to `GET /api/auth/session` and `POST /api/auth/refresh`, `/api/auth/sign-in`, and `/api/auth/sign-out`, always including cookies. Configure `fetch`, `baseUrl`, any endpoint path, and a sync/async headers callback. A session 401 receives exactly one refresh attempt.

## Split-origin browser applications

Use the bearer profile when the browser application and API are hosted on
different origins:

```ts
import { createBearerAuthTransport } from '@playstack/auth-react'

const transport = createBearerAuthTransport({
  baseUrl: 'https://api.example.com',
})
```

The API keeps the rotating refresh credential in a secure HTTP-only cookie and
returns exactly `{ session, accessToken, accessExpiresAt }` from sign-in and
refresh. The short-lived opaque access token stays in transport memory and is
sent as a bearer credential. A reload bootstraps a new access token through the
refresh cookie; responses that expose a refresh token or unknown credential
fields fail closed.

A cross-origin page cannot read a CSRF cookie set on the API origin, so there
is no synchronizer header in this profile. The API protects its cookie-bearing
routes with `Origin` and `Sec-Fetch-Site` checks (`PlaystackOriginGuard` in
`@playstack/nest-auth`) and enables CORS with credentials for the
application's origins only; the application still owns those allowlists, the
routes, and the cookie attributes. Those checks apply to browser requests
only: a client that sends no `Origin`, no Fetch Metadata and no cookie has
no ambient credential, so a CLI, a mobile app or a server can reach the same
routes with its bearer token, and the API may hand such a client the whole
token pair in the body instead of a cookie. `baseUrl` must be
HTTPS except for loopback hosts, which browsers treat as secure contexts, so
local development can point at `http://127.0.0.1:<port>` without a TLS proxy.

An account switch that issues a new session on the server also rotates the
refresh cookie. Pass `refresh` from `useAuth()` as the accounts provider's
`onSessionChange` so the transport adopts the cookie and obtains the access
token for the new session:

```tsx
const { session } = useSession()
const { refresh } = useAuth()

<PlaystackAccountsProvider client={accounts} session={session} onSessionChange={refresh}>
  {children}
</PlaystackAccountsProvider>
```

## Sync and token-storage bridges

`AuthSync` exposes `publish`, `subscribe`, and `close` plus its native `client`. The browser implementation uses `BroadcastChannel` with a storage-event fallback.

`TokenStorage` exposes `get`, `set`, and `clear` plus its native `client` for non-cookie applications. `createExpoTokenStorage(SecureStore, { key? })` stores only opaque tokens and numeric expiries and rejects expanded or malformed records.

## Guards and boundary

`RequireSession` accepts `loadingFallback`, unauthenticated `fallback`, and children. It is navigation UX only—not a server security or account authorization boundary. React and all Playstack peers remain application-controlled at the same fixed version.

{/* package-reference:start */}

## API entry points and requirements

Reference snapshot: `@playstack/auth-react@0.1.0-beta.1`. Import only the entry point your runtime needs. Paths below are relative to the installed package; use **Go to Definition** in your editor to inspect exact parameters, return types and overloads. Do not import the declaration-file paths directly.

| Public entry point | Declaration file |
| --- | --- |
| `@playstack/auth-react` | `./dist/index.d.ts` |
| `@playstack/auth-react/errors` | `./dist/errors.d.ts` |
| `@playstack/auth-react/expo` | `./dist/expo.d.ts` |
| `@playstack/auth-react/package.json` | No TypeScript declaration (asset or metadata export). |

Node.js engine requirement: `>=20`. This is not a claim that every entry point works in browsers or Workers.

### Peer dependencies

Keep existing framework versions that satisfy these ranges. Install optional peers only when using the corresponding adapter. The package manager resolves ordinary dependencies separately.

| Peer | Compatible range | When needed |
| --- | --- | --- |
| `@playstack/auth-contracts` | `0.1.0-beta.1` | Required by the package. |
| `@playstack/core` | `0.1.0-beta.1` | Required by the package. |
| `react` | `^18.2.0 \|\| ^19.0.0` | Required by the package. |

For a complete first program, start with [Getting started](/docs/getting-started). For API lookup and partial-example conventions, see [Reading the reference](/docs/packages#reading-the-reference). Provider failures, lifecycle requirements and application responsibilities remain described in the guide above; types alone do not establish production safety.

{/* package-reference:end */}
