Reputation: 6233
I'm trying to write my basic custom authentication for Django REST Framework. I have the following auth backend:
class JoeAuth(authentication.BaseAuthentication):
def authenticate(self, request):
username = request.META.get('HTTP_X_FORWARDED_USER')
if not username:
return
try:
user = User.objects.get(krb_name=username, active=True).name
except User.DoesNotExist:
raise PermissionDenied('Unauthorized user')
return (user, None)
Accompanied with a view:
@api_view()
def hello(request):
return Response(data='hello')
And of course enabled in settings.py
:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'helloworld.auth.JoeAuth',
)
}
Now, if a request comes in and does not specify the HTTP_X_FORWARDED_USER
header, the authenticate()
function returns None
. According so DRF docs:
To implement a custom authentication scheme, subclass BaseAuthentication and override the .authenticate(self, request) method. The method should return a two-tuple of (user, auth) if authentication succeeds, or None otherwise.
In some circumstances instead of returning None, you may want to raise an AuthenticationFailed exception from the .authenticate() method.
A None
means authentication failed and should ideally return 401 or 403. However, in practice this doesn't seem to be the case. A request without the HTTP_X_FORWARDED_USER
is simply allowed and 200 is returned:
$ http http://127.0.0.1:8000/ HTTP_X_FORWARDED_USER:joe
HTTP/1.1 200 OK
"hello"
$ http http://127.0.0.1:8000/
HTTP/1.1 200 OK
"hello"
Am I misunderstanding the documentation in the sense that a None
is considered a successful authentication attempt?
Upvotes: 0
Views: 2202
Reputation: 6296
The problem is that you are confusing authentication and authorization(permissions in Django). What the authentication does is identifyy the user, however it does not in any way, restrict the user - that is the work of authorization(permission). The permission classes do the work of checking the rights a user has over a particular resource.
From what I can see, it seems you have a default global AllowAny
permission set, which allows anyone access. You need to set the permission to restrict the endpoint to only authenticated users.
In your case, you need to add a permission_classes
to the view or use global perm issions in DRF settings.
You can add permission classes to a function-based API view this way:
from rest_framework.permissions import IsAuthenticated
from rest_framework.decorators import permission_classes, api_view
@api_view()
@permission_classes([IsAuthenticated])
def hello(request):
return Response(data='hello')
Upvotes: 2