103 lines
3.1 KiB
Markdown
103 lines
3.1 KiB
Markdown
---
|
|
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>"
|
|
```
|