---
title: "ASGI Adapter"
url: "https://docs.caracal.run/v1.0/sdks/adapters/asgi/"
markdown_url: "https://docs.caracal.run/markdown/v1.0/sdks/adapters/asgi.md"
description: "Pure ASGI middleware that verifies Caracal mandates for FastAPI, Starlette, and Quart resource servers."
page_type: "page"
concepts: []
requires: []
---

# ASGI Adapter

Canonical URL: https://docs.caracal.run/v1.0/sdks/adapters/asgi/
Markdown URL: https://docs.caracal.run/markdown/v1.0/sdks/adapters/asgi.md
Description: Pure ASGI middleware that verifies Caracal mandates for FastAPI, Starlette, and Quart resource servers.
Page type: page
Concepts: none
Requires: none

---

The ASGI adapter protects any Python ASGI application - FastAPI, Starlette, Quart, Django ASGI - with fail-closed Caracal mandate verification. It is pure ASGI: it imports no web framework and delegates every check to [Verify Package](/v1.0/sdks/verify/).

`CaracalASGIAuth` is the only public package entry point. Use it for inbound ASGI HTTP or WebSocket authentication, not outbound SDK calls.

## Install

```bash
pip install caracalai-asgi
```

## Add middleware

```python
from caracalai_asgi import CaracalASGIAuth
from caracalai_revocation import InMemoryRevocationStore
from fastapi import FastAPI

app = FastAPI()
app.add_middleware(
    CaracalASGIAuth,
    audience="resource://pipernet",
    revocations=InMemoryRevocationStore(),
    required_scopes=["pipernet:read"],
    routes={
        "/payouts": {"required_scopes": ["pipernet:payout"], "require_delegation": True},
    },
    exclude=["/healthz"],
)


@app.get("/balances")
async def balances(request):
    principal = request.state.caracal
    return {"sub": principal.sub, "scopes": principal.scope}
```

`issuer` defaults to `CARACAL_STS_URL` and `expected_zone_id` to `CARACAL_ZONE_ID`, so a provider deployed with the standard Caracal workload variables only states its own audience and revocation store. Construction fails if no issuer or zone can be resolved.

## Options

| Option                                                   | Use it for                                                                                                      |
| -------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------- |
| `audience`                                               | The provider's own resource identifier; mandates minted for other resources are rejected.                       |
| `revocations`                                            | Revocation store consulted on every request. Use [Redis Revocation Store](/v1.0/sdks/backends/redis/) in production. |
| `required_scopes` / `required_targets`                   | Default scope and target requirements for every route.                                                          |
| `require_session` / `require_delegation` / `max_hop_count` | Session identity, delegated authority, and Delegation-depth requirements.                                      |
| `routes`                                                 | Per-route overrides by path prefix; the longest matching prefix wins. Any option above can be overridden.       |
| `exclude`                                                | Path prefixes served without verification (health and readiness probes).                                        |

`required_use` defaults to `resource`; `require_session` and `require_delegation` default to false. Exclusions and route prefixes match path-segment boundaries, and the longest route prefix wins.

## Behavior

* Verified claims are stored as `scope["state"]["caracal"]`, surfaced as `request.state.caracal` in Starlette and FastAPI.
* Failed verification answers with the shared status mapping (`http_status_for_auth_error`): 401 for credential failures, 403 for insufficient authority, with the standard `error`/`error_description` JSON body.
* WebSocket connections that fail verification are closed with policy code `1008`; lifespan events pass through.
* Call `await middleware.warmup()` at startup to prefetch the zone JWKS before the first request.

## Boundary

The adapter verifies inbound mandates; it does not create Sessions or Delegations. Use the [Python SDK](/v1.0/sdks/python/) to create Caracal context before making outbound calls.

## Related Pages

* [Protect a FastAPI App](/v1.0/guides/protect-fastapi/)
* [Verify Package](/v1.0/sdks/verify/)
* [Sessions and Revocation](/v1.0/concepts/sessions-revocation/)
