Skip to content

Authentication, sessions, and CSRF

Django request

Inject a Django-compatible request when existing services expect django.http.HttpRequest:

from typing import Annotated

from django.http import HttpRequest
from fastapi import Depends
from django_fastapi import get_django_request

DjangoRequest = Annotated[HttpRequest, Depends(get_django_request)]

The adapter constructs one request per FastAPI request and caches it. It sets up Django's configured session backend and resolves request.user.

Built-in user dependencies

  • get_current_user: authenticated user or AnonymousUser.
  • get_authenticated_user: authenticated user or HTTP 401.
  • get_current_staff_user: staff user or HTTP 401/403.

The package cannot know a project's concrete custom user class. Projects may wrap these dependencies and perform an isinstance check or cast locally.

Session authentication

The default resolver runs Django's normal session authentication. The FastAPI and Django paths must receive the same session cookie. Separate services must share the session backend, SECRET_KEY, cookie name, domain, path, security, and SameSite settings.

Session changes made through the adapted request are saved back onto the FastAPI response:

@router.post("/preferences")
def update_preferences(request: DjangoRequest) -> dict[str, bool]:
    request.session["compact_mode"] = True
    return {"saved": True}

Custom authentication resolver

A resolver receives (fastapi_request, django_request), may be sync or async, and returns a Django user, AnonymousUser, or None. The first authenticated user wins. None and anonymous users allow the next resolver to run.

from django.contrib.auth import get_user_model
from fastapi import Request


async def bearer_resolver(request: Request, _django_request):
    token = request.headers.get("authorization", "").removeprefix("Bearer ")
    if not token:
        return None

    subject = await verify_token_and_get_subject(token)
    return await get_user_model().objects.aget(external_id=subject)
"AUTH_RESOLVERS": [
    "project.auth.bearer_resolver",
    "django_fastapi.auth.session_auth_resolver",
]

The resolver owns signature, issuer, audience, expiry, revocation, and scope validation. The bridge deliberately does not implement token policy.

CSRF

CSRF is enabled by default for POST, PUT, PATCH, and DELETE. Django's normal csrftoken cookie and X-CSRFToken header work.

Keep CSRF enabled whenever a browser can authenticate with a cookie. Disabling CSRF is appropriate only when an endpoint cannot use cookie authentication and has another complete request-authentication mechanism.

Exempt one operation:

from django_fastapi import csrf_exempt


@router.post("/provider-webhook")
@csrf_exempt
def webhook():
    ...

The endpoint must then validate the provider signature itself.

Custom HTTP session hooks

Set SESSION_MIDDLEWARE to a dotted class path when your session backend needs native async I/O. Omit it to retain Django's standard SessionMiddleware. The constructor receives get_response; synchronous process_request(request) constructs the session store and must not perform blocking I/O. An optional async aprocess_request(request) runs next, before authentication.

Before response headers are sent, the bridge calls async aprocess_response(request, response) when present; otherwise it runs synchronous process_response inside database_sync_to_async. The response hook must return the response. Async hooks must be coroutine methods; invalid classes fail at application creation with ImproperlyConfigured.

The response passed to the hook is a session-only scratch response. Only changed Vary and generated cookies are merged into the outgoing response. Existing Set-Cookie headers survive, and a bodyless 204/304 response does not acquire a synthetic Content-Type. Set other response headers in application middleware. Session modifications made after headers have been sent cannot be persisted.

Hook failures propagate as errors; a cache outage must not become an anonymous session. Keep project authentication and authorization policy in your resolver. WebSocket helpers use standard Django session authentication; they do not inherit SESSION_MIDDLEWARE, its async hooks, or HTTP AUTH_RESOLVERS.