Django Session Auth#
Docs: https://docs.djangoproject.com/en/dev/topics/auth
For this auth to work, you will need to:
Properly add and configure
django.contrib.sessions.middleware.SessionMiddlewaremiddlewareProperly add and configure
django.contrib.auth.middleware.AuthenticationMiddlewaremiddlewareSet correct auth settings, like enabling
django.contrib.authapp
Make sure to follow the default Django’s guide on setting up the regular Django auth before going further.
Requiring auth#
Note
Current user will always be accessible as self.request.user.
Read more: https://docs.djangoproject.com/en/stable/topics/auth/default/
We provide two classes to require Django session auth in your API:
DjangoSessionSyncAuthfor sync viewsDjangoSessionAsyncAuthfor async views
Example, how to use the auth class and how to get self.request.user:
1from django.contrib.auth.models import User
2
3from dmr import Controller
4from dmr.plugins.pydantic import PydanticSerializer
5from dmr.security import AuthenticatedHttpRequest
6from dmr.security.django_session import DjangoSessionSyncAuth
7
8
9class APIController(Controller[PydanticSerializer]):
10 request: AuthenticatedHttpRequest[User]
11 auth = (DjangoSessionSyncAuth(),)
12
13 def get(self) -> str:
14 # Let's test that `User` has the correct type:
15 assert self.request.user.is_authenticated
16 return 'authed'
17
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"title": "Loc",
"type": "array"
},
"msg": {
"title": "Msg",
"type": "string"
},
"type": {
"title": "Type",
"type": "string"
}
},
"required": [
"msg"
],
"title": "ErrorDetail",
"type": "object"
},
"ErrorModel": {
"description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ErrorDetail"
},
"title": "Detail",
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
}
},
"securitySchemes": {
"csrf": {
"description": "CSRF protection",
"in": "cookie",
"name": "csrftoken",
"type": "apiKey"
},
"django_session": {
"description": "Reusing standard Django auth flow for API",
"in": "cookie",
"name": "sessionid",
"type": "apiKey"
}
}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/apicontroller/": {
"get": {
"deprecated": false,
"operationId": "getApicontrollerApiApicontroller",
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"type": "string"
}
}
},
"description": "OK"
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when auth was not successful"
},
"403": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when CSRF check failed"
},
"406": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when provided `Accept` header cannot be satisfied"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when returned response does not match the response schema"
}
},
"security": [
{
"csrf": [],
"django_session": []
}
]
}
}
}
}
Note
Using any of these classes would also automatically enable CSRF checks
for this view. Because it is not secure to use Django
session auth without CSRF checks.
Reusing pre-existing views#
We provide several pre-existing views to get Django session cookie. So, users won’t have to write tons of boilerplate code.
We provide two Reusable controllers to obtain Django session cookie:
DjangoSessionSyncControllerfor sync controllersDjangoSessionAsyncControllerfor async controllers
To use them, you will need to:
Provide actual types for serializer, request model, and response body
Redefine
convert_auth_payload()to convert your request model into the kwargs ofdjango.contrib.auth.authenticate()to authenticate your requestRedefine
make_api_response()to return the response in the format of your choice
1from typing_extensions import override
2
3from dmr.plugins.pydantic import PydanticSerializer
4from dmr.security.django_session.views import (
5 DjangoSessionPayload,
6 DjangoSessionResponse,
7 DjangoSessionSyncController,
8)
9
10
11class SessionSyncController(
12 DjangoSessionSyncController[
13 PydanticSerializer,
14 DjangoSessionPayload,
15 DjangoSessionResponse,
16 ],
17):
18 @override
19 def convert_auth_payload(
20 self,
21 payload: DjangoSessionPayload,
22 ) -> DjangoSessionPayload:
23 return payload
24
25 @override
26 def make_api_response(self) -> DjangoSessionResponse:
27 return {'user_id': str(self.request.user.pk)}
28
Run result
$ curl http://127.0.0.1:8000/api/auth/ -D - -X POST -d '{"username": "test_user", "password": "password"}' -H 'Content-Type: application/json'
HTTP/1.1 200 OK
date: Thu, 16 Jul 2026 09:34:30 GMT
server: uvicorn
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language, Cookie
Content-Language: en
Content-Length: 15
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin
Set-Cookie: csrftoken=ueFww6AzcX7x1YdpKXUSrfFSMqQloO8n; expires=Thu, 15 Jul 2027 09:34:31 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Set-Cookie: sessionid=gncvjlpzdsw9t8937i878pwi6v0ojrh5; expires=Thu, 30 Jul 2026 09:34:31 GMT; HttpOnly; Max-Age=1209600; Path=/; SameSite=Lax
{"user_id":"1"}
OpenAPI Schema
Preview openapi.json
{
"components": {
"schemas": {
"DjangoSessionPayload": {
"description": "Payload for default version of a django session request body.\n\nIs also used as kwargs for :func:`django.contrib.auth.authenticate`.",
"properties": {
"password": {
"title": "Password",
"type": "string"
},
"username": {
"title": "Username",
"type": "string"
}
},
"required": [
"username",
"password"
],
"title": "DjangoSessionPayload",
"type": "object"
},
"DjangoSessionResponse": {
"description": "Default response type for django session endpoint.",
"properties": {
"user_id": {
"title": "User Id",
"type": "string"
}
},
"required": [
"user_id"
],
"title": "DjangoSessionResponse",
"type": "object"
},
"ErrorDetail": {
"description": "Base schema for error details description.",
"properties": {
"loc": {
"items": {
"anyOf": [
{
"type": "integer"
},
{
"type": "string"
}
]
},
"title": "Loc",
"type": "array"
},
"msg": {
"title": "Msg",
"type": "string"
},
"type": {
"title": "Type",
"type": "string"
}
},
"required": [
"msg"
],
"title": "ErrorDetail",
"type": "object"
},
"ErrorModel": {
"description": "Default error response schema.\n\nCan be customized.\nSee :ref:`customizing-error-messages` for more details.",
"properties": {
"detail": {
"items": {
"$ref": "#/components/schemas/ErrorDetail"
},
"title": "Detail",
"type": "array"
}
},
"required": [
"detail"
],
"title": "ErrorModel",
"type": "object"
}
},
"securitySchemes": {}
},
"info": {
"title": "Django Modern Rest",
"version": "0.1.0"
},
"openapi": "3.2.0",
"paths": {
"/api/sessionsynccontroller/": {
"post": {
"deprecated": false,
"operationId": "postSessionsynccontrollerApiSessionsynccontroller",
"requestBody": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DjangoSessionPayload"
}
}
},
"description": "Payload for default version of a django session request body.\n\nIs also used as kwargs for :func:`django.contrib.auth.authenticate`.",
"required": true
},
"responses": {
"200": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/DjangoSessionResponse"
}
}
},
"description": "OK",
"headers": {
"Set-Cookie: csrftoken": {
"description": "CSRF protection.",
"required": true,
"schema": {
"example": "csrftoken=123",
"type": "string"
}
},
"Set-Cookie: sessionid": {
"required": true,
"schema": {
"example": "sessionid=123",
"type": "string"
}
}
}
},
"400": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when request components cannot be parsed"
},
"401": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Unauthorized"
},
"406": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when provided `Accept` header cannot be satisfied"
},
"422": {
"content": {
"application/json": {
"schema": {
"$ref": "#/components/schemas/ErrorModel"
}
}
},
"description": "Raised when returned response does not match the response schema"
}
},
"summary": "By default cookies are acquired on post."
}
}
}
}
Any further customizations are also possible.
CSRF#
When a user logs in through these controllers, Django automatically rotates
the CSRF token and includes a csrftoken cookie in the login response
(alongside the session cookie).
Clients making subsequent non-safe requests (POST, PUT, PATCH,
DELETE) to session-protected endpoints must send this token back via the
X-CSRFToken request header.
Note
When CSRF_USE_SESSIONS is True, Django stores the CSRF token
in the session instead of a cookie. In that case no csrftoken cookie
appears in the login response — the token is already embedded in the
session used for authentication.
API Reference#
- class dmr.security.django_session.auth.DjangoSessionSyncAuth(security_scheme_name: str = 'django_session', csrf_scheme_name: str = 'csrf')[source]#
Reuses Django’s regular session auth for the API.
This class is used for sync endpoints.
- __call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) Self | None[source]#
Does check for the existing request user.
- authenticate(endpoint: Endpoint, controller: Controller[BaseSerializer]) Self | None[source]#
Override this method to provide other authentication logic.
For example: checking that user is staff / superuser.
- provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) list[ResponseSpec]#
Provides responses that can happen when user is not authed.
- property security_schemes: dict[str, SecurityScheme | Reference]#
Provides a security schema definition.
- class dmr.security.django_session.auth.DjangoSessionAsyncAuth(security_scheme_name: str = 'django_session', csrf_scheme_name: str = 'csrf')[source]#
Reuses Django’s regular session auth for the API.
This class is used for async endpoints.
- async __call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) Self | None[source]#
Does check for the existing request user.
- async authenticate(endpoint: Endpoint, controller: Controller[BaseSerializer]) Self | None[source]#
Override this method to provide other authentication logic.
For example: checking that user is staff / superuser.
- provide_response_specs(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]], existing_responses: Mapping[HTTPStatus, ResponseSpec]) list[ResponseSpec]#
Provides responses that can happen when user is not authed.
- property security_schemes: dict[str, SecurityScheme | Reference]#
Provides a security schema definition.