0

I have test function to test whether user can login or not. I am using DRF's APIClient and logging in. When I do a get request to a protected API endpoint I get 401 response.

I tried to print:

print(client.login(username='instructor', password='asdf'))

And it is returning True, which means user is found and logged in.

But still when I do:

response = client.get(reverse('recommend-list'))

I am getting 401.

Here is my test function:

def test_user_authentication(self):
    client = APIClient()
    user_data = {
        'username': 'instructor',
        'email': '[email protected]',
        'password': 'asdf'
    }
    client.post(reverse('user-list'), user_data, format='json')
    self.assertTrue(User.objects.filter(username='instructor').exists())
    print(client.login(username='instructor', password='asdf'))
    response = client.get(reverse('recommend-list'))
    self.assertEqual(response.status_code, 200)

Here is how I create a new user:

class UserViewSet(viewsets.ViewSet):
    def create(self, request):
        serializer = UserSerializer(data=request.data)
        User = get_user_model()

        if serializer.is_valid():
            validated_data = serializer.validated_data

            try:
                user = User.objects.create_user(
                    username=validated_data['username'],
                    email=validated_data['email'],
                    password=validated_data['password']
                )

                return Response({'id': user.id, 'username': user.username}, status=status.HTTP_201_CREATED)
            except IntegrityError:
                return Response({'error': 'Username is already taken.'}, status=status.HTTP_400_BAD_REQUEST)

        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Making get request to here:

class RecommendViewSet(viewsets.GenericViewSet):
    authentication_classes = [BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def list(self, request):
        user_id = request.user.id
        movies = recommend_movies_for_user(user_id)
        return Response({"message": movies}, status=status.HTTP_200_OK)

The user serializer looks like this:

class UserSerializer(serializers.ModelSerializer):
    id = serializers.IntegerField(read_only=True)
    username = serializers.CharField(max_length=150)
    email = serializers.EmailField()
    password = serializers.CharField(write_only=True)
    follower_list = serializers.SerializerMethodField()
    following_list = serializers.SerializerMethodField()

    class Meta:
        model = User
        fields = ['id', 'username', 'email', 'password',
                  'follower_list', 'following_list']

    def retrieve(self, instance):
        return instance

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

    def get_follower_list(self, obj):
        follower = obj.followers_set.all()
        return FollowSerializer(follower, many=True).data

    def get_following_list(self, obj):
        following = obj.following_set.all()
        return FollowSerializer(following, many=True).data
2
  • Share UserSerializer, please. Commented May 2, 2024 at 17:36
  • @Niko Added UserSerializer. I am using a user provided by django. Commented May 3, 2024 at 6:06

1 Answer 1

0

Alright, I thought it could have been a simple hashing problem, but I was wrong.

The problem is that you are using BasicAuthentication to authenticate credentials, which tries to find an username and a password that must be present in the request headers. Thus, you need to encode credentials and properly set the headers:

class TestUser(TestCase):

    def test_user_authentication(self):
        client = APIClient()
        user_data = {
            "username": "instructor",
            "email": "[email protected]",
            "password": "asdf",
        }

        client.post(reverse("user-list"), user_data, format="json")
        get_user_model().objects.get(username="instructor")

        # Encode credentials and set the header
        cstring = f"{user_data["username"]}:{user_data["password"]}".encode("utf-8")
        c=base64.b64encode(cstring)
        client.credentials(HTTP_AUTHORIZATION=f"Basic {c.decode()}")

        response = client.get(reverse("recommend-list"))
        self.assertEqual(response.status_code, 200)

On the other hand, what .login does is attach an User to the session with the authenticate method. Therefore, if you use SessionAuthentication your original code would work just fine.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.