# Auth

```ts
import { bearerTokenAuth, issueBearerToken } from "@nizhal/server";

const auth = bearerTokenAuth({ secret: process.env.JWT_SECRET! });

const token = issueBearerToken({
  userId: "user-1",
  ownerId: "owner-1",
  shopId: "shop-1", // optional extra claims → actor.*
  secret: process.env.JWT_SECRET!,
});
```

## NizhalAuth

`auth.resolve(req)` returns `{ userId, ownerId, ...claims }` or `null`. Unauthenticated pull/push/stream requests receive 401.

Pass resolved `actor` into mutator context and sync-rule parameter evaluation.

## Client wiring

```ts
createNizhalClient({
  auth: {
    getHeaders: () => ({ Authorization: `Bearer ${token}` }),
    refresh: async () => { /* rotate token */ },
  },
});
```

`refresh` runs on 401 so long-lived sessions can recover without wedging the outbox.

## WebSocket auth

Browser PartySocket sends the bearer token on the upgrade. React Native uses `nitroWebSocketSource({ token })` for header auth (with `?token=` fallback).

## Admin stats

`GET /nizhal/stats` accepts `Authorization: Bearer <ADMIN_PASSWORD>` when `adminPassword()` env is set.