Vineet
Vineet

Reputation: 783

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."

Upvotes: 0

Views: 2137

Answers (2)

Ibbad Ullah
Ibbad Ullah

Reputation: 9

I have fixed that issue in a professional way. The above method will work for you which I have mentioned but now I'm gonna share a professional method with you. So, you have to follow the steps.

Step 1: First of all, create a serializer for login. But inherit Serializer class, not ModelSerialzer class.

class LoginSerializer(serializers.Serializer):
# We're using custom model fields
email = serializers.CharField()
password = serializers.CharField()

def validate(self,data):
    user = authenticate(**data)
    if user and user.is_active:
        return user
    raise serializers.ValidationError("Incorrect credentials!")

Step 2: Now create a login API in your views.py file.

# Login API
class LoginView(generics.GenericAPIView):
    serializer_class = LoginSerializer
def post(self,request,*args,**kwargs):
    serializer = self.get_serializer(data=request.data)
    serializer.is_valid(raise_exception=True)
    user = serializer.validated_data

    return Response({
        "user": AccoutSerilizer(user,context=self.get_serializer_context()).data,
        "token": AuthToken.objects.create(user)[1]
    })

Step 3: Now just create the endpoint of the login API in the urls.py file. But keep in mind I'm using the Knox framework of Django. So, if you use any other library/framework your method might be different.

Upvotes: 0

Vineet
Vineet

Reputation: 783

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]
        })

Upvotes: 1

Related Questions