feat(standards): integrate auth-server via pinned spec (docs://spec) + auth recipe and template slice
- serve the pinned auth-server OpenAPI contract as the standards MCP resource docs://spec (no auth-server MCP bundle) - add Auth Integration recipe (14) and wire it into the registry, prompts, index, and nav - un-defer Auth Server in the library registry (spec/recipe pointers) - ship the opt-in template auth slice: interfaces/auth/ (placeholder + mergeable bearerAuth security.yaml) and AUTH_SERVER/JWT_SERVER env - rebuild bundle (13 modules, 44 components); template stays authless by default
This commit is contained in:
@@ -24,7 +24,7 @@ can read a library's full API by connecting to its MCP and reading
|
||||
| Mail Intake | `mail_intake` | Provider-agnostic read-only email ingestion | Gmail OAuth + MIME parsing | `:8002` |
|
||||
| Py JWT | `jwtlib` | JWT auth primitives (register/login/introspect) | password hashing + token code | `:8005` |
|
||||
| Doc Forge | `docforge` | Docstring compiler → MkDocs + MCP docs | custom doc generators | `:8001` |
|
||||
| Auth Server | *(service)* | Central identity service (register/login/me/introspect) | building auth endpoints yourself | *(deferred)* |
|
||||
| Auth Server | *(service)* | Central identity service (register/login/me/logout/introspect) | building auth endpoints yourself | spec: `docs://spec` |
|
||||
|
||||
## How to choose
|
||||
|
||||
@@ -43,7 +43,9 @@ can read a library's full API by connecting to its MCP and reading
|
||||
|
||||
A single **standards MCP** exposes `find_library(capability)`, which returns the
|
||||
matching library, its import name, its "use instead of" guidance, and its MCP
|
||||
endpoint. Query it before writing any infrastructure.
|
||||
endpoint (or, for the auth-server service, a spec pointer). It also serves the
|
||||
pinned auth-server OpenAPI contract at `docs://spec`. Query it before writing
|
||||
any infrastructure.
|
||||
|
||||
## Notes
|
||||
|
||||
@@ -51,7 +53,10 @@ endpoint. Query it before writing any infrastructure.
|
||||
`docs://index`, `docs://nav`, and `docs://modules/{module}` (e.g.
|
||||
`docs://modules/hexa.container`) to discover the DI surface — `build(cls, *,
|
||||
instances, config)` and the `PortNode` model.
|
||||
- **Auth Server** is deferred: it has no `pyproject.toml`/importable package and
|
||||
no MCP bundle. Do not reach for it yet; `py-jwt` covers token primitives.
|
||||
- **Auth Server** is a **service**, not an importable library: it has no MCP
|
||||
bundle by design. Read its client contract from the pinned OpenAPI document
|
||||
via `docs://spec` and wire it by declaring a `bearerAuth` security scheme in
|
||||
the app's own spec (see **Auth Integration**); `py-jwt` covers the
|
||||
low-level token path.
|
||||
- Library MCP servers expose **documentation resources** (not executable tools):
|
||||
they tell an agent the available API surface so it does not invent code.
|
||||
|
||||
140
standards/src/lib/14_auth_integration.md
Normal file
140
standards/src/lib/14_auth_integration.md
Normal file
@@ -0,0 +1,140 @@
|
||||
# Auth Integration
|
||||
|
||||
## Summary
|
||||
|
||||
Authentication is a **service**, not a library. Every Aetos application shares a
|
||||
single identity service — the **auth-server** — and consumes it through its HTTP
|
||||
contract. An app never implements registration, login, password hashing, or
|
||||
token issuance itself.
|
||||
|
||||
The agent does **not** need an MCP bundle for the auth-server. It reads the
|
||||
pinned OpenAPI contract (below) and declares a bearer security scheme in its own
|
||||
spec; `openapi_first` then generates the verifying dependency. **No auth code is
|
||||
written.**
|
||||
|
||||
---
|
||||
|
||||
## Read the contract
|
||||
|
||||
The auth-server OpenAPI document is pinned in the standards bundle and served as
|
||||
an MCP resource:
|
||||
|
||||
```
|
||||
docs://spec
|
||||
```
|
||||
|
||||
It is the authoritative client contract (OpenAPI 3.1). Read it to learn the
|
||||
endpoints and the request/response schemas:
|
||||
|
||||
| Endpoint | Purpose |
|
||||
|---|---|
|
||||
| `POST /register` | Create a user (201, returns `PublicUser`) |
|
||||
| `POST /login` | Authenticate and issue a JWT (`LoginResponse`) |
|
||||
| `GET /me` | Current user's `PublicUser` (bearer required) |
|
||||
| `POST /logout` | Stateless logout (`LogoutResponse`) |
|
||||
| `POST /introspect` | Internal token introspection (`active`, `user`) |
|
||||
|
||||
The pinned snapshot is versioned (currently `0.0.5`). If the caller has a live
|
||||
auth-server and needs the *current* contract, they can pass the full endpoint
|
||||
(`{AUTH_SERVER}/openapi.json`) and read it directly instead of the pin.
|
||||
|
||||
---
|
||||
|
||||
## Wire it up: declare, don't code
|
||||
|
||||
Authentication is added by editing the app's **own** `openapi.yaml` — nothing
|
||||
else. Declare a global bearer requirement and point the scheme at the auth
|
||||
server (values come from the environment):
|
||||
|
||||
```yaml
|
||||
security:
|
||||
- bearerAuth: []
|
||||
paths:
|
||||
# ...
|
||||
components:
|
||||
securitySchemes:
|
||||
bearerAuth:
|
||||
type: http
|
||||
scheme: bearer
|
||||
bearerFormat: JWT
|
||||
x-server-url: "{AUTH_SERVER}"
|
||||
x-introspect-path: "/introspect"
|
||||
x-login-path: "/login"
|
||||
x-register-path: "/register"
|
||||
x-logout-path: "/logout"
|
||||
x-me-path: "/me"
|
||||
```
|
||||
|
||||
`openapi_first` reads `securitySchemes` + `security` and generates a FastAPI
|
||||
dependency that extracts the bearer token, POSTs `{"token": <jwt>}` to
|
||||
`{AUTH_SERVER}/introspect`, and returns the user when `active == true`. It
|
||||
raises `401` on an invalid or inactive token and `503` when the auth server is
|
||||
unreachable. The generated handlers receive the authenticated user — you author
|
||||
**no** authentication code.
|
||||
|
||||
### Public operations
|
||||
|
||||
A route that must stay open opts out by overriding `security` with an empty
|
||||
list:
|
||||
|
||||
```yaml
|
||||
paths:
|
||||
/health:
|
||||
get:
|
||||
operationId: health
|
||||
security: [] # overrides the global requirement
|
||||
responses:
|
||||
"200":
|
||||
description: OK
|
||||
```
|
||||
|
||||
### Environment
|
||||
|
||||
```bash
|
||||
AUTH_SERVER=http://localhost:8000 # auth-server base URL
|
||||
JWT_SERVER=http://localhost:8000 # jwtlib client base URL (seeding/CLI)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## Path 1: `jwtlib` client (no generator)
|
||||
|
||||
Apps that do not use a generated dependency verify requests with the `py-jwt`
|
||||
client:
|
||||
|
||||
```python
|
||||
from jwtlib.introspection import authenticate_request
|
||||
|
||||
authorized = await authenticate_request(
|
||||
should_skip_authentication, # allow public paths
|
||||
method,
|
||||
path,
|
||||
authorization_token, # "Bearer <jwt>" or None
|
||||
)
|
||||
```
|
||||
|
||||
`authenticate_request` POSTs to `{JWT_SERVER}/introspect` (3 second timeout) and
|
||||
treats `{"active": true, "user": {...}}` as valid. Use this for seeding scripts
|
||||
and tests (`jwtlib.register_user`, `jwtlib.login_user`).
|
||||
|
||||
---
|
||||
|
||||
## Golden rules
|
||||
|
||||
1. **Never trust an unverified token.** Decode through the generated dependency
|
||||
or `jwtlib` before handling the request.
|
||||
2. **Treat `active: false` as unauthenticated**, even if the JWT decodes.
|
||||
3. **Do not implement your own JWT parsing.** Revocation and issuer changes must
|
||||
stay centralized in the auth-server.
|
||||
4. **Do not add auth until the app's outcome is validated.** Ship the app
|
||||
authless first; layer the security scheme once the core behaviour works.
|
||||
|
||||
---
|
||||
|
||||
## Notes
|
||||
|
||||
- The default template is **authless** on purpose. Add the `securitySchemes`
|
||||
block above only when auth is required. Pull the shared slice with
|
||||
`get_app_component("auth")`.
|
||||
- The auth-server is deployed as a service (no importable package); the client
|
||||
surface is the spec, plus `jwtlib` for the low-level path.
|
||||
@@ -15,6 +15,8 @@ patterns, and the internal library catalog that agents consult while coding.
|
||||
- **Human-in-the-Loop** — guardrails and approvals inside automations.
|
||||
- **Infra Adapters** — how infrastructure connects without leaking into domain.
|
||||
- **Genericity Matrix** — what is library-backed, template-provided, or authored.
|
||||
- **Auth Integration** — consuming the shared auth-server service (spec-driven,
|
||||
no auth code).
|
||||
- **Library Registry** — the catalog of internal libraries behind all of it.
|
||||
|
||||
Read the **Wiki** for the philosophy and rationale; these pages are the
|
||||
|
||||
Reference in New Issue
Block a user