docs: add OpenAPI-first wiki (overview, components, use cases for templates/client/codegen, design, security, error handling, testing) and refresh lib docs index

This commit is contained in:
2026-09-15 22:29:05 +05:30
parent adeb02e162
commit b559323dfe
26 changed files with 1590 additions and 86 deletions

144
docs/wiki/05_security.md Normal file
View File

@@ -0,0 +1,144 @@
# Security — Auth Driven by the Spec, Not by Hand
The most distinctive thing about `openapi-first` security is that you **never write middleware or `Depends(authenticate)` calls yourself**. All authentication is *declared* in the OpenAPI document and *enforced* automatically.
---
## 🗺️ 1. Where Security Lives
Two places in the spec, both respected:
### 1.1 `components.securitySchemes` — the inventory
```yaml
components:
securitySchemes:
internalBearer:
type: http
scheme: bearer
bearerFormat: JWT
x-server-url: "{AUTH_SERVER_URL}"
x-introspect-path: "/introspect"
```
`x-server-url` and `x-introspect-path` are library extensions that point at a JWT introspection endpoint (see [Introspection](#-5-introspection)).
### 1.2 `security` — per-operation (or global) requirements
```yaml
security:
- internalBearer: [] # applied to every operation by default
paths:
/pets:
get:
operationId: list_pets
# inherits: security: [{internalBearer: []}]
post:
operationId: create_pet
security: [] # override — public endpoint
```
OpenAPI dynamic-scoping rules apply: an operation-level `security` **replaces** the global list (it does not merge).
---
## 📖 2. Reading the Spec
`security.py` exposes two functions:
| Function | Returns | Purpose |
|----------|---------|---------|
| `parse_security_schemes(spec)` | `dict[str, dict]` | Collect schemes, resolve `{ENV_VAR}` placeholders |
| `make_security_dependencies(spec, schemes)` | `dict[str, list[Depends]]` (keyed `METHOD:/path`) | Effective per-operation security deps |
Env placeholders of the form `{NAME}` are resolved **once at startup** from `os.environ`. This is how you avoid embedding credentials or auth-service URLs in the committed spec.
---
## 🔐 3. The Bearer Dependency
`make_security_dependencies` builds a FastAPI dependency for `type: http, scheme: bearer`.
Two modes, decided by whether an introspection endpoint is configured:
### 3.1 With introspection (`x-introspect-path`)
- Reads `Authorization: Bearer <token>`
- POSTs `{"token": "<token>"}` to `{x-server-url}{x-introspect-path}` synchronously via the bundled httpx client
- Expects a response with `active: true`
- On valid: `{"user": ...}` from the introspection body → `request.state.user`
- On failure: `401` (missing/invalid token) or `503` (auth service unreachable)
### 3.2 Without introspection
- Validates only that a Bearer token is present
- Stores it on `request.state.token`; no remote call
---
## 🔗 4. Wiring It Together
```python
# server-side
from openapi_first.loader import load_openapi
from openapi_first.security import (
parse_security_schemes,
make_security_dependencies,
)
from openapi_first.app import OpenAPIFirstApp
import routes
spec = load_openapi("openapi.yaml")
schemes = parse_security_schemes(spec)
security_deps = make_security_dependencies(spec, schemes)
app = OpenAPIFirstApp(
openapi_path="openapi.yaml",
routes_module=routes,
)
```
`OpenAPIFirstApp` already does this internally — the snippet above shows what it encapsulates (and what you use directly if you assemble the pieces by hand).
---
## 🧪 5. Testing Security
Because handlers are plain callables, security is the *one* place FastAPI's `TestClient` earns its keep:
```python
from fastapi.testclient import TestClient
def test_unauthenticated_is_401(app, overrides):
with TestClient(app) as client:
r = client.get("/pets")
assert r.status_code == 401
def test_invalid_token_using_fake_introspector(app):
# Point x-introspect-path at a stub uvicorn/TestServer returning active:false
with TestClient(app) as client:
r = client.get("/pets", headers={"Authorization": "Bearer nope"})
assert r.status_code == 401
```
See [Testing](07_testing.md) for the full recipe, including how tests stub the introspection server.
---
## 🛡️ 7. Common Patterns
| Pattern | How |
|---------|-----|
| Public endpoint | `security: []` on the operation |
| Whole-spec auth | top-level `security:` (applies to all) |
| Route-specific scheme | replace `security` on that operation |
| Env-driven auth URL | `x-server-url: "{AUTH_SERVER_URL}"` |
| Offline token carry | scheme without `x-introspect-path``request.state.token` |
| Auth on the client side | pass the token via `client.<operationId>(headers={"Authorization": ...})` |
---
## Related
- [02 Components](02_components.md) · [04 Design](04_design.md) · [06 Error Handling](06_error_handling.md) · [07 Testing](07_testing.md)