Just want one protected view? Start with the [Django quickstart](/docs/quickstarts?sdk=django).

`authdog.django` is middleware plus a `require_auth` view decorator. Other Python frameworks: [FastAPI](/docs/backend/fastapi), [Flask](/docs/backend/flask), [Starlette](/docs/backend/starlette), [aiohttp](/docs/backend/aiohttp). Hub: [Python](/docs/backend/python).

## Availability and install

**Unreleased on PyPI.** Source-only in [`packages/python`](https://github.com/authdog/web-sdk/tree/main/packages/python). Python 3.10+, Django 4.2+, `httpx` 0.27+.

```bash
python -m pip install "./packages/python[django]"
```

`import authdog.django` is lazy: Django does not need to be configured until a binding is called.

## Configure

```python
# auth.py
import os
from authdog.django import Authdog

authdog = Authdog(public_key=os.environ["PK_AUTHDOG"])
```

```python
# settings.py
MIDDLEWARE = [
    # ...
    "myapp.auth.authdog.middleware",
]
```

The public key is publishable. Construction rejects malformed keys and identity hosts outside the trusted HTTPS allowlist.

## Session and gate

Middleware puts a resolved context on `request.authdog_context`. It never raises. `session(request)` reads that context from any view. `@require_auth` is the 401 boundary (`JsonResponse({"error": "Unauthorized"}, status=401)`).

Django drives `userinfo` with `asyncio.run` on the sync view path.

```python
from django.http import JsonResponse

@authdog.require_auth
def me(request):
    return JsonResponse(authdog.session(request).user)

def logout(request):
    return authdog.logout(request)
```

Middleware, decorator, and `session()` share one resolved context per request.

## Shared rules

The resolver prefers `authdog-session`, then `Authorization: Bearer <token>`. `fetch_user=False` leaves `is_authenticated` false, so the decorator rejects. Apply [authorization](/docs/concepts/authorization) after the gate.

Self-hosted identity hosts need `AUTHDOG_ALLOWED_IDENTITY_HOSTS`.
