Django Session Auth

Docs: https://docs.djangoproject.com/en/dev/topics/auth

For this auth to work, you will need to:

  1. Properly add and configure django.contrib.sessions.middleware.SessionMiddleware middleware

  2. Properly add and configure django.contrib.auth.middleware.AuthenticationMiddleware middleware

  3. Set correct auth settings, like enabling django.contrib.auth app

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:

Example, how to use the auth class and how to get self.request.user:

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:

  1. DjangoSessionSyncController for sync controllers

  2. DjangoSessionAsyncController for async controllers

To use them, you will need to:

  1. Provide actual types for serializer, request model, and response body

  2. Redefine convert_auth_payload() to convert your request model into the kwargs of django.contrib.auth.authenticate() to authenticate your request

  3. Redefine make_api_response() to return the response in the format of your choice

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, 07 May 2026 12:57:16 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=Wh9CmB7qcZSXcQENtS3WOFDl8r9kfINO; expires=Thu, 06 May 2027 12:57:16 GMT; Max-Age=31449600; Path=/; SameSite=Lax
Set-Cookie: sessionid=ib1376m21raijre4f6f4l63w3kr79ld7; expires=Thu, 21 May 2026 12:57:16 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.

See also:

https://docs.djangoproject.com/en/stable/ref/settings/#std-setting-CSRF_USE_SESSIONS

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_requirement: dict[str, list[str]]

Provides a security schema usage requirement.

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_requirement: dict[str, list[str]]

Provides a security schema usage requirement.

property security_schemes: dict[str, SecurityScheme | Reference]

Provides a security schema definition.