Search code examples
djangodjango-rest-frameworkdjango-rest-knox

Unable to use token authentication as the only authentication class in django-rest-knox with custom user model


I am using django-rest-knox for user authentication and have knox.auth.TokenAuthentication as the only authentication class. Also, I am using custom user models by extending AbstractBaseUser. I have email and password as fields for logging in.

According to documentation, when you use token authentication as the only authentication method then you have to override the inbuilt LoginView of knox. I have followed the same method but it does not seem to work out.

views.py

class LoginInterestedUserAPI(LoginView):
    def get_user_serializer_class(self):
        return LoginInterestedUserSerializer

    def post(self, request, format=None):
        serializer = AuthTokenSerializer(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        login(request, user)

        return super(LoginInterestedUserAPI, self).post(request, format=None)

serializers.py

class LoginInterestedUserSerializer(serializers.ModelSerializer):
    class Meta:
        model = InterestedUser
        fields = ('email', 'password')

    def validate(self, data):
        user = authenticate(**data)
        if user and user.is_active:
            return user
        raise serializers.ValidationError("Unable to log in with the provided credentials")

urls.py

...

urlpatterns = [
    path('login', views.LoginInterestedUserAPI.as_view()),
]

When I go to the url /api/login, I am not getting email and password field to enter my credentials and getting the following output:-

"detail": "Authentication credentials were not provided."

Solution

  • First issue was in AuthTokenSerializer in views.py. AuthTokenSerializer uses username and password for login, whereas my custom user model was using email and password. Second issue was in LoginInterestedUserSerializer in which I had used ModelSerializer and thus subsqeuently inherited create method due to which I got the error user with this email already exists. Therefore, serializers.Serializer should be used. Following are working code snippets:-

    serializers.py

    class UserSerializer(serializers.ModelSerializer):
        class Meta:
            model = InterestedUser
            fields = ('full_name', 'email')
    
    
    class LoginInterestedUserSerializer(serializers.Serializer):
    
        email = serializers.EmailField()
        password = serializers.CharField(max_length=128)
    
        def validate(self, data):
            user = authenticate(**data)
            if user and user.is_active:
                return user
            raise serializers.ValidationError("Unable to log in with the provided credentials")
    

    views.py

    class LoginView(GenericAPIView):
        serializer_class = LoginInterestedUserSerializer
    
        def post(self, request, *args, **kwargs):
            serializer = self.serializer_class(data=request.data, context={'request': request})
            serializer.is_valid(raise_exception=True)
            user = serializer.validated_data
            return Response({
                "user": UserSerializer(user, context=self.get_serializer_context()).data, 
                "token": AuthToken.objects.create(user)[1]
            })