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
x-server-url and x-introspect-path are library extensions that point at a JWT introspection endpoint (see Introspection).
1.2 security β per-operation (or global) requirements
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) or503(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
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:
See Testing 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": ...}) |