npm package: @iron-stack/auth
Description: Full-stack authentication framework with JWT access tokens, rotating refresh tokens, and role-based access control -- built for tRPC + Fastify + Drizzle.
Latest version: 1.0.0

# @iron-stack/auth

Full-stack authentication framework with JWT access tokens, rotating refresh tokens, and role-based access control -- built for tRPC + Fastify + Drizzle.

## Installation

```bash
npm install @iron-stack/auth
```

## Quick Start

### Server

```ts
import { createAuthRouter, extractAuthContext, authRequired, requireRole, createSocketAuthMiddleware } from '@iron-stack/auth/server';

// 1. Create the auth tRPC router (register, login, refresh, logout)
const authRouter = createAuthRouter({
  router: t.router,
  publicProcedure,
  db,
  tables: { users, refreshTokens },
  jwtSign: (payload, opts) => fastify.jwt.sign(payload, opts),
});

// 2. Extract auth context from requests
function createContext({ req }: CreateFastifyContextOptions) {
  return { db, ...extractAuthContext(fastify, req) };
}

// 3. Protect routes
const protectedProcedure = t.procedure.use(authRequired);
const adminProcedure = t.procedure.use(requireRole(['admin']));

// 4. Authenticate Socket.IO connections
io.use(createSocketAuthMiddleware(fastify));
```

### Client

```ts
import { create } from 'zustand';
import { createAuthStore } from '@iron-stack/auth/client';
import type { AuthState } from '@iron-stack/auth/client';

export const useAuthStore = create<AuthState>((set, get) =>
  createAuthStore({
    apiUrl: 'http://localhost:3000',
    storage: {
      get: async () => { /* read refresh token from secure storage */ },
      set: async (token) => { /* persist refresh token */ },
      remove: async () => { /* delete refresh token */ },
    },
  })(set, get)
);
```

## API Reference

### `@iron-stack/auth` (shared)

| Export | Description |
|---|---|
| `RegisterSchema` | Zod schema for registration input (phone, displayName, password) |
| `LoginSchema` | Zod schema for login input (phone, password) |
| `RefreshSchema` | Zod schema for token refresh input (refreshToken) |
| `AuthResponseSchema` | Zod schema for auth response (tokens + user) |
| `RoleSchema` | Zod enum schema for roles (`user`, `admin`, `moderator`) |
| `DEFAULT_ROLE` | Default role assigned to new users (`"user"`) |
| `RegisterInput` | Type inferred from `RegisterSchema` |
| `LoginInput` | Type inferred from `LoginSchema` |
| `RefreshInput` | Type inferred from `RefreshSchema` |
| `AuthResponse` | Type inferred from `AuthResponseSchema` |
| `Role` | Type union: `"user" \| "admin" \| "moderator"` |
| `RolePermissions` | Interface for defining resource/action role mappings |

### `@iron-stack/auth/server`

| Export | Description |
|---|---|
| `createAuthRouter(config)` | Creates a tRPC router with `register`, `login`, `refresh`, and `logout` mutations |
| `authRequired` | tRPC middleware that enforces authentication |
| `requireRole(roles)` | Factory that returns a tRPC middleware enforcing role-based access |
| `extractAuthContext(fastify, req)` | Extracts `userId` and `userRole` from a Fastify request's JWT |
| `createSocketAuthMiddleware(fastify)` | Socket.IO middleware that verifies JWT from handshake auth |
| `generateRefreshToken()` | Generates a cryptographically random refresh token |
| `hashToken(token)` | SHA-256 hashes a token for safe storage |
| `AuthRouterConfig` | Configuration interface for `createAuthRouter` |
| `AuthContext` | Interface: `{ userId: string \| null, userRole: Role \| null }` |
| `JwtPayload` | Interface: `{ sub: string, role?: Role }` |
| `SocketAuthPayload` | Interface for socket JWT payload |

### `@iron-stack/auth/client`

| Export | Description |
|---|---|
| `createAuthStore(config)` | Creates platform-agnostic auth state logic for Zustand |
| `AuthUser` | Interface for the authenticated user object |
| `AuthState` | Interface for the full auth store state and actions |
| `AuthStoreConfig` | Configuration interface for the auth store |

## Configuration

### `AuthRouterConfig`

| Option | Type | Default | Description |
|---|---|---|---|
| `router` | `function` | **required** | tRPC `t.router` function |
| `publicProcedure` | `object` | **required** | tRPC public procedure builder |
| `db` | `DrizzleDb` | **required** | Drizzle database instance |
| `tables` | `{ users, refreshTokens }` | **required** | Drizzle table references |
| `jwtSign` | `function` | **required** | JWT signing function |
| `bcryptRounds` | `number` | `12` | Bcrypt hashing rounds |
| `accessTokenExpiry` | `string` | `"15m"` | Access token expiration |
| `refreshTokenDays` | `number` | `30` | Refresh token validity in days |
| `defaultRole` | `Role` | `"user"` | Default role for new registrations |

### `AuthStoreConfig`

| Option | Type | Default | Description |
|---|---|---|---|
| `apiUrl` | `string` | **required** | API base URL |
| `refreshPath` | `string` | `"auth.refresh"` | tRPC refresh procedure path |
| `storage` | `{ get, set, remove }` | **required** | Secure storage adapter for refresh tokens |

## License

MIT