Django: Basic Auth for one view (avoid middleware)
When you do a basic auth request, you're really adding credentials into the Authorization
header. Before transit, these credentials are base64-encoded, so you need to decode them on receipt.
The following code snippet presumes that there's only one valid username and password:
import base64def my_view(request): auth_header = request.META.get('HTTP_AUTHORIZATION', '') token_type, _, credentials = auth_header.partition(' ') expected = base64.b64encode(b'username:password').decode() if token_type != 'Basic' or credentials != expected: return HttpResponse(status=401) # Your authenticated code here: ...
If you wish to compare to the username and password of a User
model, try the following instead:
def my_view(request): auth_header = request.META.get('HTTP_AUTHORIZATION', '') token_type, _, credentials = auth_header.partition(' ') username, password = base64.b64decode(credentials).split(':') try: user = User.objects.get(username=username) except User.DoesNotExist: return HttpResponse(status=401) password_valid = user.check_password(password) if token_type != 'Basic' or not password_valid: return HttpResponse(status=401) # Your authenticated code here: ...
Please note that this latter version is not extremely secure. At first glance, I can see that it is vulnerable to timing attacks, for example.
You can try a custom decorator (as seems to be the recommended way here and here) instead of adding new middleware:
my_app/decorators.py
:
import base64from django.http import HttpResponsefrom django.contrib.auth import authenticatefrom django.conf import settingsdef basicauth(view): def wrap(request, *args, **kwargs): if 'HTTP_AUTHORIZATION' in request.META: auth = request.META['HTTP_AUTHORIZATION'].split() if len(auth) == 2: if auth[0].lower() == "basic": uname, passwd = base64.b64decode(auth[1]).decode( "utf8" ).split(':', 1) user = authenticate(username=uname, password=passwd) if user is not None and user.is_active: request.user = user return view(request, *args, **kwargs) response = HttpResponse() response.status_code = 401 response['WWW-Authenticate'] = 'Basic realm="{}"'.format( settings.BASIC_AUTH_REALM ) return response return wrap
Then use this to decorate your view:
from my_app.decorators import basicauth@basicauthdef my_view(request): ...
This library could be used: https://github.com/hirokiky/django-basicauth
Basic auth utilities for Django.
The docs show how to use it:
Applying decorator to CBVs
To apply @basic_auth_requried decorator to Class Based Views, use django.utils.decorators.method_decorator.
Source: https://github.com/hirokiky/django-basicauth#applying-decorator-to-cbvs