mota
mota

Reputation: 5475

Cannot Authenticate Test Requests with Both User Token and API Key in Django Rest Framework

My project requires two authentication methods for some endpoints:

  1. A global API key using this app.
  2. A user authentication token using the default one provided by DRF

All works fine when working with the API using Postman or an iOS app, but I couldn't make the authentication work in my tests. The user auth works fine, but the global key fails.

This is how the HTTP headers look in Postman:

X-Api-Key: KEY_VALUE
Authorization: Token TOKEN_VALUE

I tried to experiment with the code in the shell and authentication worked fine using the exact same code used in the test! Only in the tests it fails so I'm not really sure how to debug this further.

Edit:

You can see a complete project on github.

Test output:

Creating test database for alias 'default'...
System check identified no issues (0 silenced).
F
======================================================================
FAIL: test (app.tests.MyTests)
----------------------------------------------------------------------
Traceback (most recent call last):
  File ".../authtest/app/tests.py", line 21, in test
    self.assertEqual(response.status_code, status.HTTP_200_OK)
AssertionError: 403 != 200

----------------------------------------------------------------------
Ran 1 test in 0.112s

FAILED (failures=1)
Destroying test database for alias 'default'...

When I open the shell with python manage.py shell and copy and paste the test code:

>>> from rest_framework.test import (APITestCase, APIRequestFactory, force_authenticate)
>>> from rest_framework import status
>>> from accounts.models import User
>>> from app.views import MyView
>>> user = User.objects.create(email="[email protected]")
>>> user.set_password('1234')
>>> factory = APIRequestFactory()
>>> API_KEY = "KIkKSSz7.ziURxOZv8e66f28eMLYwPNs7eEhrNtYl"
>>> headers = {"HTTP_X_API_KEY": API_KEY}
>>> request = factory.get('/myview', **headers)
>>> force_authenticate(request, user=user)
>>> response = MyView.as_view()(request)
>>> response.status_code
200

Also making the request with postman works. Any idea what's going on here?

Upvotes: 0

Views: 649

Answers (1)

Hemant
Hemant

Reputation: 1166

The problem is the hardcoded API_KEY you are passing in your test which is not a valid key resulting in status 403. I think the reason might be django uses a separate test database when running tests and djangorestframework-api-key uses APIKey model to generate api_key.

You can confirm this by:

  1. Removing HasAPIKey from permission classes in MyView and running test again.
  2. or by passing empty string as API_KEY in your test resulting in same error you are getting now.

so what i might suggest is generating a new valid api_key and passing it instead of hardcoded key.

Here i am sharing an updated test file of how you can carry out your test

from rest_framework.test import (APITestCase, APIRequestFactory, force_authenticate)
from rest_framework import status
from accounts.models import User
from .views import MyView
from rest_framework_api_key.models import APIKey


class MyTests(APITestCase):

    def test(self):
        user = User.objects.create(email="[email protected]")
        user.set_password('1234')
        user.save()
        factory = APIRequestFactory()
        api_key, key = APIKey.objects.create_key(name="my-remote-service")
        headers = {"HTTP_X_API_KEY": key}
        request = factory.get('/myview', **headers)
        force_authenticate(request, user=user)
        response = MyView.as_view()(request)
        self.assertEqual(response.status_code, status.HTTP_200_OK)

Upvotes: 1

Related Questions