npm package: @ortha/server-identity
Description: JWT-based authentication, role/permission system, and invite code management for the Ortha server. Provides Fastify hooks for route-level auth and permission enforcement, plus the Sequelize schema for identity models.
Latest version: 0.0.4

# @ortha/server-identity

JWT-based authentication, role/permission system, and invite code management for the Ortha server. Provides Fastify hooks for route-level auth and permission enforcement, plus the Sequelize schema for identity models.

## Installation

Internal monorepo dependency — import directly:

```ts
import {
    identityServerPlugin,
    identitySchema,
    authHook,
    permissionHook
} from '@ortha/server-identity';
```

## Usage

### Registering the plugin

```ts
import { bootstrap } from '@ortha/server-platform-bootstrap';
import { databasePlugin } from '@ortha/server-platform-database';
import { identityServerPlugin, identitySchema } from '@ortha/server-identity';

await bootstrap({
    config: bootstrapConfig,
    plugins: [
        databasePlugin({
            ...dbConfig,
            schemas: [identitySchema]
        }),
        identityServerPlugin({
            jwtSecret: 'your-secret',
            accessExpiry: '15m',
            refreshExpiry: '7d',
            signupExpiry: '1h'
        })
    ]
});
```

### Using auth hooks in other plugins

```ts
import { authHook, permissionHook } from '@ortha/server-identity';

fastify.route({
    method: 'GET',
    url: '/api/my-resource',
    config: {
        auth: true,
        permissions: ['read:admin:resource']
    },
    onRequest: [authHook, permissionHook],
    handler: async (request, reply) => {
        const user = request.user; // populated by authHook
    }
});
```

### Hashing passwords

```ts
import { hashPassword } from '@ortha/server-identity';

const hashed = await hashPassword('plaintext-password');
```

## Routes

| Method | Path                          | Auth | Permissions         | Description          |
| ------ | ----------------------------- | ---- | ------------------- | -------------------- |
| POST   | `/api/identity/login`         | No   | —                   | Authenticate user    |
| POST   | `/api/identity/signup`        | No   | —                   | Register new user    |
| POST   | `/api/identity/invite/verify` | No   | —                   | Verify invite code   |
| POST   | `/api/identity/refresh`       | No   | —                   | Refresh access token |
| GET    | `/api/identity/me`            | Yes  | —                   | Get current user     |
| POST   | `/api/identity/invite/create` | Yes  | `create:admin:user` | Create invite code   |
| GET    | `/api/identity/roles`         | Yes  | `read:admin:user`   | List available roles |

## Error Contract

Identity routes return Fastify error fields plus structured metadata:

```json
{
    "statusCode": 401,
    "error": "Unauthorized",
    "message": "Invalid credentials",
    "code": "AUTH_INVALID_CREDENTIALS",
    "path": "root",
    "details": [
        {
            "code": "AUTH_INVALID_CREDENTIALS",
            "path": "root",
            "message": "Invalid credentials"
        }
    ]
}
```

Validation errors return `details[]` with one entry per issue. Paths use dot notation (e.g. `email`, `password`, `code`).

## API Reference

### Plugin & Schema

| Export                   | Kind    | Description                                      |
| ------------------------ | ------- | ------------------------------------------------ |
| `identityServerPlugin()` | factory | Fastify plugin — registers identity routes       |
| `identitySchema`         | schema  | Sequelize `SchemaDefinition` for identity models |

### Hooks

| Export           | Kind | Description                                   |
| ---------------- | ---- | --------------------------------------------- |
| `authHook`       | hook | Fastify `onRequest` — validates Bearer token  |
| `permissionHook` | hook | Fastify `onRequest` — checks user permissions |

### Utilities

| Export             | Kind     | Description                             |
| ------------------ | -------- | --------------------------------------- |
| `hashPassword()`   | function | Bcrypt hash with 12 salt rounds         |
| `getRequestUser()` | function | Extract typed user from Fastify request |

### Types

| Export                 | Kind | Description                         |
| ---------------------- | ---- | ----------------------------------- |
| `IdentityPluginConfig` | type | Plugin configuration (JWT settings) |
| `JwtConfig`            | type | JWT secret and expiry configuration |
| `AccessTokenPayload`   | type | JWT access token payload shape      |

### Configuration

| Property        | Type     | Description                        |
| --------------- | -------- | ---------------------------------- |
| `jwtSecret`     | `string` | Secret key for JWT signing         |
| `accessExpiry`  | `string` | Access token expiry (e.g. `'15m'`) |
| `refreshExpiry` | `string` | Refresh token expiry (e.g. `'7d'`) |
| `signupExpiry`  | `string` | Signup token expiry (e.g. `'1h'`)  |

## Sequelize Models

| Model        | Description                              |
| ------------ | ---------------------------------------- |
| `User`       | Core user record (email, name, isActive) |
| `Password`   | Hashed password (belongs to User)        |
| `Role`       | Named role (e.g. `admin`, `editor`)      |
| `Permission` | Action-resource pair (belongs to Role)   |
| `InviteCode` | Time-limited invite code for signup      |

## Internal Structure

```
src/lib/
├── types/index.ts                      # Shared types and interfaces
├── schemas/index.ts                    # Sequelize model definitions + associations
├── identityServerPlugin/index.ts       # Fastify plugin factory
├── controllers/                        # Route handlers
├── services/
│   ├── utils/                          # Shared service utilities
│   ├── login/index.ts                  # Login flow
│   ├── signup/index.ts                 # Signup flow
│   ├── refresh/index.ts               # Token refresh
│   ├── hashPassword/index.ts          # Bcrypt hashing
│   ├── verifyPassword/index.ts        # Bcrypt verification
│   ├── signAccessToken/index.ts       # JWT access token signing
│   ├── signRefreshToken/index.ts      # JWT refresh token signing
│   ├── signSignupToken/index.ts       # JWT signup token signing
│   ├── verifyToken/index.ts           # JWT verification
│   ├── verifyInviteCode/index.ts      # Invite code validation
│   ├── createInvite/index.ts          # Invite code creation
│   ├── serializeMe/index.ts           # Current user serialization
│   └── listRoles/index.ts            # Role listing
├── hooks/
│   ├── authHook/index.ts              # Bearer token validation
│   └── permissionHook/index.ts        # Permission enforcement
└── dto/                                # Data transfer objects
```

## Key Patterns

- Declarative auth via route `config.auth` and `config.permissions` properties.
- Auth hook validates Bearer token and populates `request.user`.
- Permission hook checks user's role permissions against required route permissions.
- Services use dependency injection: `serviceName(deps)(args)`.
- Bcrypt uses 12 salt rounds for password hashing.

## Dependencies

- `@ortha/server-platform-database` — `SchemaDefinition`, `ModelRegistry` types
- `@ortha/server-utils` — error response builders
- `@fastify/jwt` — JWT signing and verification

## Building

```bash
nx build server-identity
```