HTTP Basic Auth#

Docs: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/Authentication

Warning

HTTP Basic Auth is very insecure. It is better than nothing, but is not enough for nearly all real use-cases. Please, consider using JWT Auth instead.

The only reasonable use-case for this auth is to use HTTP Basic auth for protecting some public things like API status dashboards.

Note that this auth type does not set request.user by design. Because many users might use the same auth parameters.

Requiring auth#

To work with HTTP Basic auth you would need to subclass either HttpBasicSyncAuth or HttpBasicAsyncAuth and override its sync or async (respectively) authenticate method, which will decide whether or not passed username and password are correct.

Here’s how to do it:

Let’s say we defined a horribly weak set of username and password: admin / pass. Let’s check that the auth works:

Run result

$ curl http://127.0.0.1:8000/api/username/ -D - -X POST -d '{"bill": "parking"}' -H 'Content-Type: application/json'
HTTP/1.1 401 Unauthorized
date: Mon, 05 Oct 2026 11:16:12 GMT
server: uvicorn
WWW-Authenticate: Basic realm="api", charset="UTF-8"
Content-Type: application/json
X-Frame-Options: DENY
Vary: Accept-Language
Content-Language: en
Content-Length: 58
X-Content-Type-Options: nosniff
Referrer-Policy: same-origin
Cross-Origin-Opener-Policy: same-origin

{"detail":[{"msg":"Not authenticated","type":"security"}]}

$ curl http://127.0.0.1:8000/api/username/ -X POST -d '{"bill": "parking"}' -H 'Content-Type: application/json' -H 'Authorization: Basic YWRtaW46cGFzcw=='
"Processing bill: parking"

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"
      },
      "_RequestModel": {
        "properties": {
          "bill": {
            "title": "Bill",
            "type": "string"
          }
        },
        "required": [
          "bill"
        ],
        "title": "_RequestModel",
        "type": "object"
      }
    },
    "securitySchemes": {
      "http_basic": {
        "description": "Http Basic auth",
        "scheme": "basic",
        "type": "http"
      }
    }
  },
  "info": {
    "title": "Django Modern Rest",
    "version": "0.1.0"
  },
  "openapi": "3.2.0",
  "paths": {
    "/api/billcontroller/": {
      "post": {
        "operationId": "postBillControllerApiBillcontroller",
        "requestBody": {
          "content": {
            "application/json": {
              "schema": {
                "$ref": "#/components/schemas/_RequestModel"
              }
            }
          },
          "required": true
        },
        "responses": {
          "201": {
            "content": {
              "application/json": {
                "schema": {
                  "type": "string"
                }
              }
            },
            "description": "Created"
          },
          "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": "Raised when auth was not successful",
            "headers": {
              "WWW-Authenticate": {
                "description": "Challenges that the client can use to authenticate this request",
                "required": true,
                "schema": {
                  "type": "string"
                }
              }
            }
          },
          "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": [
          {
            "http_basic": []
          }
        ]
      }
    }
  }
}

Any other authentication method will be better than the one above. Consider using JWT Auth instead.

Customization#

Added in version 0.15.0.

You can customize:

  • Security scheme name, default: http_basic

  • Header name, default: Authorization

  • Header value prefix (auth_scheme), default: Basic

  • The WWW-Authenticate challenge (www_authenticate and realm), see How authentication works

The header value must always start with the auth_scheme prefix, which is matched exactly. So, with the default settings only Authorization: Basic <base64(username:password)> is accepted, while Authorization: basic <base64(username:password)> and a bare Authorization: <base64(username:password)> are not.

Pass auth_scheme='' if you need to read the credentials without any prefix at all, this is how older versions worked.

Headers with a different prefix are left to the next auth in the chain. But, when the prefix does match and the credentials still cannot be decoded, NotAuthenticatedError is raised right away and no other auth classes are tried.

API Reference#

class dmr.security.http.HttpBasicSyncAuth(*, security_scheme_name: str = 'http_basic', header: str = 'Authorization', auth_scheme: str = 'Basic', www_authenticate: bool = True, realm: str = 'api')[source]#

Uses HTTP Basic Auth.

Subclass this type to provide actual username/password check according to your needs. This class is used for sync endpoints.

Note that this class does not set request.user by design. Because many users might use the same auth parameters.

Warning

HTTP Basic Auth is not really secure and should not be used for anything serious. Consider using JWT instead.

Changed in version 0.15.0: The auth_scheme prefix is now required and configurable, it is Basic by default. Header values without a prefix are only accepted with auth_scheme=''.

__call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) → Self | None[source]#

Does the login routine.

abstractmethod authenticate(endpoint: Endpoint, controller: Controller[BaseSerializer], username: str, password: str) → Self | None[source]#

Override this method to provide an actual user/password check.

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.

security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[dict[str, list[str]]]#

Provides a security schema usage requirement.

security_schemes(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, SecurityScheme | Reference]#

Provides a security schema definition.

validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) → None#

Validate auth configuration at import time.

Added in version 0.16.0.

property www_authenticate_challenge: str | None#

Challenge for the Basic scheme.

Returns None for a custom header or auth_scheme, because a challenge can only ask the client for the Basic prefix in the Authorization header.

class dmr.security.http.HttpBasicAsyncAuth(*, security_scheme_name: str = 'http_basic', header: str = 'Authorization', auth_scheme: str = 'Basic', www_authenticate: bool = True, realm: str = 'api')[source]#

Uses HTTP Basic Auth.

Subclass this type to provide actual username/password check according to your needs. This class is used for async endpoints.

Note that this class does not set request.user by design. Because many users might use the same auth parameters.

Warning

HTTP Basic Auth is not really secure and should not be used for anything serious. Consider using JWT instead.

Changed in version 0.15.0: The auth_scheme prefix is now required and configurable, it is Basic by default. Header values without a prefix are only accepted with auth_scheme=''.

async __call__(endpoint: Endpoint, controller: Controller[BaseSerializer]) → Self | None[source]#

Does the login routine.

abstractmethod async authenticate(endpoint: Endpoint, controller: Controller[BaseSerializer], username: str, password: str) → Self | None[source]#

Override this method to provide an actual user/password check.

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.

security_requirements(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → list[dict[str, list[str]]]#

Provides a security schema usage requirement.

security_schemes(metadata: EndpointMetadata, controller_cls: type[Controller[BaseSerializer]]) → dict[str, SecurityScheme | Reference]#

Provides a security schema definition.

validate(controller_cls: type[Controller[BaseSerializer]], metadata: EndpointMetadata) → None#

Validate auth configuration at import time.

Added in version 0.16.0.

property www_authenticate_challenge: str | None#

Challenge for the Basic scheme.

Returns None for a custom header or auth_scheme, because a challenge can only ask the client for the Basic prefix in the Authorization header.

dmr.security.http.basic_auth(username: str, password: str, *, prefix: str = 'Basic ') → str[source]#

Return a header value for basic auth for a given username and password.

The prefix must match the auth_scheme of the auth class that will read this header, including the trailing space.

>>> basic_auth('admin', 'pass')
'Basic YWRtaW46cGFzcw=='

>>> basic_auth('admin', 'pass', prefix='Custom ')
'Custom YWRtaW46cGFzcw=='