Reputation: 23
I'm successfully creating a token upon a user's login (using a CustomUser
model that replaces the username
field with email
), but when using this token in subsequent requests, the response is "Invalid token". Below are excerpts from the relevant files:
settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'users',
'goals',
'rest_framework',
'rest_framework.authtoken',
]
...
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.TokenAuthentication',
],
}
Views to register and login users in users/views.py
class UserRegistrationView(generics.CreateAPIView):
serializer_class = UserSerializer
permission_classes = [permissions.AllowAny]
class UserLoginView(APIView):
def post(self, request):
user = authenticate(username=request.data['email'], password=request.data['password'])
if user:
token, created = Token.objects.get_or_create(user=user)
return Response({'token': token.key})
else:
return Response({'error': 'Invalid credentials'}, status=401)
The view I'm testing in goals/views.py:
@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def create_topic(request):
data = JSONParser().parse(request)
serializer = TopicSerializer(data=data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=201)
else:
return Response(serializer.errors, status=400)
The test that fails in goals/tests.py
class SerializerTests(TestCase):
def setUp(self):
email = "[email protected]"
pw = "foo"
self.user = CustomUser.objects.create_user(email=email, password=pw)
self.user_data = {'email':email, 'password':pw}
login_response = self.client.post('/users/login/', data=json.dumps(self.user_data), content_type="application/json")
print(login_response.data)
self.token = login_response.data['token']
self.headers = {'Authorization': 'Token {self.token}'}
def test_create_topic(self):
self.topic = {'name':'topic_1', 'created_by':self.user.pk}
response = self.client.post('/goals/topic/create/', data=json.dumps(self.topic), content_type="application/json", headers=self.headers) #should this be replaced by some DRF components?
self.assertEqual(201, response.status_code)
Unfortunately response.data
simply states {'detail': ErrorDetail(string='Invalid token.', code='authentication_failed')}
. This does not appear to be an issue with the header, since changing the header format yields an error message saying no authentication credentials were provided. So it seems the token is submitted succesfully, yet somehow it is not accepted. I have checked and confirmed that the token created upon login is saved properly so it should be available for the requests - still it doesn't work...
What am I doing wrong here?
Upvotes: 1
Views: 60
Reputation: 503
While the @Antoliny's answer is true, it might not be enough.
In your settings, I can't see AUTH_USER_MODEL
setting. If you use your own, custom user model, you should set:
# settings.py
AUTH_USER_MODEL = "users.CustomUser" # Use real path for your model.
Docs: https://docs.djangoproject.com/en/5.1/topics/auth/customizing/#substituting-a-custom-user-model
It's needed for Django and all other packages to treat this model as a default for user model. Token
model from rest_framework.authtoken
uses this setting for token's relation to user.
Upvotes: 0
Reputation: 571
It's a simple issue.
class SerializerTests(TestCase):
def setUp(self):
email = "[email protected]"
pw = "foo"
self.user = CustomUser.objects.create_user(email=email, password=pw)
self.user_data = {'email':email, 'password':pw}
login_response = self.client.post('/users/login/', data=json.dumps(self.user_data), content_type="application/json")
print(login_response.data)
self.token = login_response.data['token']
self.headers = {'Authorization': 'Token {self.token}'}
...
If you look at the SerializerTests setUp method, you set the header through the token you received.
self.headers = {'Authorization': 'Token {self.token}'}
However, the above sentence does not apply formatting, so self.token is applied as a string rather than the value of the self.token variable.
For self.token to be applied as a variable value, I used f-string in that code.
self.headers = {'Authorization': f'Token {self.token}'}
Upvotes: 1