Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Django Rest Framework requesting authentication on AllowAny setting

I've created a JWT-Authorised back end for an app. Login, logout, token retrieval and refresh all work fine, and as expected. I added a registration view this morning, which is throwing the usual "detail": "Authentication credentials were not provided. error you'd expect for non-authenticated requests, as that's the default (see below).

However, because this is a registration endpoint, I don't want it to only allow authorised requests. (Having checked with a valid token, the rest of the view works as expected when you supply authentication.) Looking at the permissions section of the DRF docs, I thought that using the permission_classes wrapper with AllowAny would work here, but it hasn't.

What am I missing? I feel like the permission_classes decorator should override the default setting of 'IsAuthenticated'?

I'm testing on localhost from curl:

curl -X POST -H "Content-Type: application/json" -d '{"email":"[email protected]", "first_name": "boba", "last_name": "fett" "password":"xyz"}' http://localhost:8000/account/register/

View is:

@permission_classes(AllowAny)
@api_view(['POST'])
def register_user(request):
    from django.contrib.auth.models import User
    from rest_framework_jwt.views import obtain_jwt_token
    if request.user.is_authenticated():
        return Response ({"already_registered": "User with that username has already registered"}, status=status.HTTP_701_ALREADY_REGISTERED)
    data = request.data

    user, created = User.objects.get_or_create(username=data["email"],
                                               email=data["email"],
                                               first_name=data["first_name"],
                                               last_name=data["last_name"],
                                               password=data["password"])
    if created:
        token = obtain_jwt_token(data["email"],data["password"] )
        return Response ({"token": token}, status=status.HTTP_200_OK)
    else:
        return Response ({"already_registered": "User with that username has already registered"}, status=status.HTTP_701_ALREADY_REGISTERED)

Permissions in settings.py are:

REST_FRAMEWORK = {
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ),
}

Related questions: Django Rest Framework - Authentication credentials were not provided - I think the default permissions are correct, I just want to override them in this instance.

Django Rest Framework - DELETE ajax call failure due to incorrect CSFR token - CSRF not being used as JWT Based auth.

Django: Rest Framework authenticate header - Apache specific issue (currently still on devserver localhost)

Django Rest Framework Authentication credentials were not provided - Not yet answered!

like image 514
Withnail Avatar asked Mar 29 '15 14:03

Withnail


People also ask

Which authentication is best in Django REST Framework?

TokenAuthentication. Note: The token authentication provided by Django REST framework is a fairly simple implementation. For an implementation which allows more than one token per user, has some tighter security implementation details, and supports token expiry, please see the Django REST Knox third party package.

How does token authentication work in Django REST Framework?

Token authentication refers to exchanging username and password for a token that will be used in all subsequent requests so to identify the user on the server side.


2 Answers

The order of the decorators matter. There's also some problems with your code.

I recommend using a serializer, maybe something like below. If you want to use emails as username, I would make a custom User model. Django's default authentication system's username field has max_length of 30, and people's email addresses easily surpass that.

class UserSerializer(serializers.ModelSerializer):
    first_name = serializers.CharField(required=False, allow_null=True)
    last_name = serializers.CharField(required=False, allow_null=True)
    class Meta:
        model = User
        fields = ('id', 'username', 'first_name', 'last_name', 'email', 'password')

    def create(self, validated_data):
        return User.objects.create_user(**validated_data)

@api_view(['POST'])
@permission_classes([permissions.AllowAny,])
def register_user(request):
    if request.user.is_authenticated():
        return Response({"already_registered": "User with that username has already registered"}, status=701)
    data = request.data
    serializer = UserSerializer(data=data, partial=True)
    if serializer.is_valid():
        serializer.save(username=serializer.validated_data['email'])
        token = #call the url to get your tokens, use urllib or something similar
        return Response({"token": token}, status=status.HTTP_201_CREATED)
    else:
        return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Edit The ordering of decorators goes like this:

@decorator
@decorator2
def func():
    print('hello world')

Is the same as decorator(decorator2(func)))

like image 173
Jaakko Avatar answered Oct 30 '22 20:10

Jaakko


You have disabled permissions using @permission_classes, but that's only the "authorization" part of "authentication and authorization". You need to disable the authentication handlers as well using @authentication_classes in order to stop receiving a 401/403 error.

like image 23
Kevin Brown-Silva Avatar answered Oct 30 '22 19:10

Kevin Brown-Silva