Files
responder/docs/source/runtime-contracts.rst
T
kennethreitz 428181a096 Add JWT/OAuth2 auth, Problem exception, function middleware, and a broad feature sweep for 8.1.0
Flagship: JWTAuth (HS256/RS256/ES256, exp/aud/iss, JWKS with off-loop
fetch, scope claims -> requires()) and OAuth2Auth flow descriptions that
light up Swagger UI's Authorize button, behind a new responder[jwt] extra.

Also additive: responder.Problem (raisable RFC 9457) + RFC 9457 abort();
@api.middleware function-style HTTP middleware; Pydantic bodies and
Query/Header/Cookie/Path markers on class-based and WebSocket handlers;
Form-model binding; write-side preconditions (If-Match/If-Unmodified-Since
-> 412); q-ranked content negotiation; get_list for repeated headers;
status_code= route option; OpenAPI document caching; rate-limiter key=/
fail-open/X-RateLimit-Reset; configurable metrics buckets + in-flight
gauge; RFC 8288 pagination Link headers; Redis session serializer;
unified request-ID policy; CLI --host/--port/--server/--reload; optional
orjson encode backend; AsyncTestClient + SSE test helpers; fakeredis
backend coverage. Five DeprecationWarnings start the 9.0 clock.

Every feature was implemented test-first, then the combined diff went
through a 64-agent adversarial review that caught 19 issues including two
criticals (an auth scope-claim regression that flipped authorization
decisions, and a sync-middleware threadpool deadlock) -- all fixed with
regression tests before release.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-07-03 07:08:42 -04:00

177 lines
6.8 KiB
ReStructuredText

Runtime Contracts
=================
Responder keeps a small set of runtime contracts stable across the 7.x series.
This page collects the behaviors that application code, tests, generated
clients, and OpenAPI consumers can rely on.
Error Responses
---------------
Framework-generated errors use ``application/problem+json`` by default. The
payload always includes:
- ``type``: ``"about:blank"`` unless an application ``problem_handler`` changes
it or the error was raised as a ``responder.Problem`` with an explicit
``type``.
- ``title``: the HTTP status title.
- ``status``: the numeric HTTP status code.
When extra context exists, the payload may also include:
- ``detail`` for human-readable context.
- ``errors`` for structured validation failures.
- ``request_id`` when request ID middleware or structured logging has attached
one to the request scope.
- ``instance`` (defaulting to the request path) plus any extension members when
the error was raised as a ``responder.Problem``.
The standard framework error statuses are:
.. list-table::
:header-rows: 1
* - Status
- Default title
- Notes
* - ``400``
- ``Bad Request``
- Malformed request bodies, such as invalid JSON.
* - ``401``
- ``Unauthorized``
- Authentication failed or credentials were missing.
* - ``403``
- ``Forbidden``
- Authentication succeeded, but the principal lacks required scope.
* - ``404``
- ``Not Found``
- No route matched the request path.
* - ``405``
- ``Method Not Allowed``
- The path exists but does not accept the request method.
* - ``413``
- ``Content Too Large``
- ``max_request_size`` rejected the body.
* - ``422``
- ``Validation Error``
- Path, query, header, cookie, form, file, or body validation failed.
* - ``500``
- ``Internal Server Error``
- Unhandled application errors and response-model validation failures.
* - ``504``
- ``Gateway Timeout``
- ``request_timeout`` expired before the handler finished.
Pass ``API(problem_details=False)`` to keep the legacy behavior. In that mode,
requests that ask for JSON receive ``{"error": "..."}``, while validation
failures receive ``{"errors": [...]}``. Requests that do not ask for JSON may
receive plain text, matching older Responder releases.
``API(problem_handler=...)`` can enrich or replace problem-details payloads. The
handler receives ``(payload, request, exc)``; returning ``None`` means the
payload was mutated in place. If the handler raises or returns a non-dict value,
Responder logs the failure and uses the original framework payload.
Authentication
--------------
Application-level auth configured with ``API(auth=...)`` applies to every route
by default. A route can opt out with ``auth=None`` or replace it with
``auth=...`` on the route decorator.
Auth helpers inject the authenticated principal as ``user``, ``principal``, and
``auth`` when the route declares those keyword-only parameters. They also set
``req.state.user`` and ``req.state.auth``.
``auth.optional()`` only treats missing credentials as anonymous. Malformed
credentials, wrong schemes, bad API keys, and failed verification still fail
through the wrapped auth scheme. For scoped auth, anonymous requests are allowed
when credentials are missing, but supplied credentials must still satisfy the
required scopes.
Scoped auth returns ``403`` when a principal is authenticated but does not hold
every required scope or role. If the wrapped scheme provides a challenge,
Responder adds an ``insufficient_scope`` ``WWW-Authenticate`` challenge with the
missing scopes.
``api.policy(name, auth)`` creates a named wrapper around an auth helper. The
policy name is an application label only: authentication, principal injection,
OpenAPI security schemes, optional auth, and scoped requirements all continue to
come from the wrapped helper.
Dependency Injection
--------------------
Responder resolves route dependencies after before-request hooks and auth, and
before the route handler. For HTTP requests, the order is:
1. Before-request hooks.
2. Route or application auth.
3. Route dependency guards.
4. Handler parameter dependencies.
5. Handler.
6. After-request hooks.
7. Request-scoped dependency teardown.
Each dependency is resolved at most once per request. Shared sub-dependencies
reuse the same value within that request.
Generator dependencies tear down in reverse dependency order. Teardown failures
are logged, and remaining teardowns still run.
App-scoped dependencies are resolved once for the application lifetime and are
protected by a lock during first resolution, so concurrent first users share the
same initialized value. If first initialization raises, the failed value is not
cached and a later request may retry. App-scoped teardowns run during lifespan
shutdown and clear the app-scope cache.
App-scoped dependencies may depend on other app-scoped dependencies, but they
cannot receive the request object and cannot depend on request-scoped
dependencies.
Response Models
---------------
``response_model=...`` validates the response body after the handler has run.
Valid responses are coerced and filtered through the model. Invalid responses
fail closed:
- In normal mode, the client receives a ``500`` framework error.
- In debug mode, the validation exception is raised for the developer.
Response-model validation failures are passed to ``problem_handler`` as the
``exc`` argument when problem details are enabled.
OpenAPI Defaults
----------------
When OpenAPI is enabled, Responder generates operation IDs, summaries, tags,
request bodies, response schemas, validation responses, auth security
requirements, and common framework error responses from the route contract.
Route decorators can add or override operation metadata with ``responses=``,
``examples=``, ``response_examples=``, and ``openapi_extra=``; nested response
metadata is deep-merged with the generated contract.
``status_code=`` declares the route's default success status: the generated
operation keys its success response under it instead of ``200`` (a ``204``
documents no response body), and ``resp.status_code`` is pre-seeded with it
before the handler runs. An explicit assignment in the handler — or a
before-request hook short-circuiting the route — still wins.
The generated document is cached: registering a route, schema, or security
scheme invalidates the cache, and the next request to the schema route (or
docs page) rebuilds it.
With problem details enabled, generated operations document
``application/problem+json`` responses and the reusable ``ProblemDetails``
component schema. With ``problem_details=False``, generated operations document
the legacy JSON error shape and do not register ``ProblemDetails``.
Generated OpenAPI documents are validated in the test suite for both OpenAPI
3.0.x and 3.1.x.