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:
1from typing import Self
2
3from django.conf import settings
4from typing_extensions import override
5
6from dmr import Controller
7from dmr.endpoint import Endpoint
8from dmr.security.http import HttpBasicAsyncAuth
9from dmr.serializer import BaseSerializer
10
11
12class HttpBasicAsync(HttpBasicAsyncAuth):
13 @override
14 async def authenticate(
15 self,
16 endpoint: Endpoint,
17 controller: Controller[BaseSerializer],
18 username: str,
19 password: str,
20 ) -> Self | None:
21 # Define `HTTP_BASIC_USERNAME` and `HTTP_BASIC_PASSWORD`
22 # in your settings.py file:
23 if (
24 username == settings.HTTP_BASIC_USERNAME
25 and password == settings.HTTP_BASIC_PASSWORD
26 ):
27 return self
28 return None
Let’s say we defined a horribly weak set
of username and password: admin / pass.
Let’s check that the auth works:
1from typing_extensions import TypedDict
2
3from dmr import Body, Controller
4from dmr.plugins.pydantic import PydanticFastSerializer
5from examples.auth.http_basic.auth import HttpBasicAsync
6
7
8class _RequestModel(TypedDict):
9 bill: str
10
11
12class BillController(Controller[PydanticFastSerializer]):
13 auth = (HttpBasicAsync(),)
14
15 async def post(self, parsed_body: Body[_RequestModel]) -> str:
16 return f'Processing bill: {parsed_body["bill"]}'
17
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: Fri, 16 Oct 2026 22:52:46 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_basicHeader name, default:
AuthorizationHeader value prefix (
auth_scheme), default:BasicThe
WWW-Authenticatechallenge (www_authenticateandrealm), 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.
See API reference.