---
name: openclaw-m2m-openapi
description: Implement and maintain OpenAPI plus machine-to-machine access for FEDEO/OpenClaw-style Fastify backends. Use when creating tenant-bound API keys, authenticating M2M callers, exchanging API keys for short-lived JWTs, exposing OpenAPI docs/spec endpoints, and ensuring req.user is populated for impersonated tenant/user execution.
---

# OpenClaw M2M + OpenAPI

Implement OpenAPI publication and M2M authentication with tenant+user impersonation.

## Define Data Model

Create a DB table for M2M API keys with at least:
- Key identity: `id`, `name`, `key_prefix`, `key_hash`
- Scope: `tenant_id`, `user_id`
- Lifecycle: `active`, `expires_at`, `last_used_at`, `created_at`, `updated_at`
- Auditing: `created_by`

Store only a hash (`sha256`) of the key, never plaintext.

## Publish OpenAPI

Configure Fastify Swagger dynamically and expose:
- UI endpoint: `/docs`
- Raw spec endpoint: `/openapi.json`

Define OpenAPI metadata and bearer auth security schema (`bearerAuth`).

## Implement M2M Authentication

In M2M auth plugin:
1. Read API key from `x-api-key`.
2. Hash and compare against `key_hash` in DB.
3. Reject missing/invalid/inactive/expired keys with 401.
4. Load mapped tenant/user.
5. Populate `req.user` with:
   - `user_id`
   - `email`
   - `tenant_id`
6. Update key usage metadata (`last_used_at`, `updated_at`).

Always keep `req.user` compatible with existing JWT-authenticated route expectations.

## Implement API Key Management

Expose tenant-scoped management endpoints on authenticated API routes:
- `GET /api/tenant/api-keys`
- `POST /api/tenant/api-keys`
- `PATCH /api/tenant/api-keys/:id`
- `DELETE /api/tenant/api-keys/:id`

For create:
- Generate random plaintext key once.
- Return plaintext only in create response.
- Persist only hash + prefix.
- Enforce that selected `user_id` belongs to current tenant.

## Add Token Exchange (Preferred M2M Flow)

Expose internal route:
- `POST /internal/auth/m2m/token`

Behavior:
1. Require M2M-authenticated request.
2. Revalidate tenant membership of impersonated user.
3. Issue short-lived JWT signed with app JWT secret.
4. JWT payload must include `user_id`, `email`, `tenant_id`.

Use this JWT for normal `/api/*` requests via `Authorization: Bearer <token>`.

## Security Rules

- Never log full API keys.
- Enforce TTL bounds for exchanged JWTs (e.g. 60..3600 seconds).
- Treat expired keys as unauthorized.
- Keep key hash comparison deterministic and normalized.
- Keep management endpoints tenant-isolated.

## Verification Checklist

Run and verify:
1. Backend build passes.
2. OpenAPI UI and raw spec are reachable.
3. API key create/list/update/delete works per tenant.
4. Exchange endpoint returns JWT.
5. JWT can call `/api/me` and shows impersonated tenant/user.

## Quick Commands

```bash
# Build backend
npm run build

# Exchange key for JWT
curl -X POST http://localhost:3000/internal/auth/m2m/token \
  -H "x-api-key: <M2M_API_KEY>" \
  -H "Content-Type: application/json" \
  -d '{"expires_in_seconds":900}'

# Use exchanged JWT on normal API
curl http://localhost:3000/api/me \
  -H "Authorization: Bearer <ACCESS_TOKEN>"
```
