'"Expected a string value" on login using Django REST and pyjwt
So, I'm trying to build simple register and login functionalities into my API. I can register just fine, but when I insert the user details on the login API view page, it gives me the "Expected a string value" error. I think this is probably because one of the credentials being passed somehow doesn't get stringified? I'm really not sure and I can't find much about it. Here's my code!
views.py
from django.shortcuts import render
from rest_framework.generics import GenericAPIView
from .serializers import UserSerializer, LoginSerializer
from rest_framework import status
from rest_framework.response import Response
from django.conf import settings
from django.contrib import auth
import jwt
class RegisterView(GenericAPIView):
serializer_class = UserSerializer
def post(self, request):
serializer = UserSerializer(data=request.data)
if serializer.is_valid():
serializer.save()
return Response(serializer.data, status=status.HTTP_201_CREATED)
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
class LoginView(GenericAPIView):
serializer_class = LoginSerializer
def post(self, request):
data = request.data
username = data.get('username', '')
password = data.get('password', '')
user = auth.authenticate(username=username, password=password)
if user:
auth_token = jwt.encode({'username': user.username}, settings.JWT_SECRET_KEY, algorithm="HS256")
serializer = UserSerializer(user)
data = {"user": serializer.data, "token": auth_token}
print(auth_token)
return Response(data, status=status.HTTP_200_OK)
### SEND RES
return Response({'detail':'Invalid Credentials'}, status=status.HTTP_401_UNAUTHORIZED)
serializers.py
from rest_framework import serializers
from django.contrib.auth.models import User
class UserSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=65, min_length=8, write_only=True)
email = serializers.EmailField(max_length=220)
username = serializers.CharField(max_length=50)
class Meta:
model = User
fields = ['username', 'email', 'password']
def validate(self, attrs):
if User.objects.filter(email = attrs['email']).exists():
raise serializer.ValidationError({'email', ('email is already in use')})
return super().validate(attrs)
def create(self, validated_data):
return User.objects.create_user(**validated_data)
class LoginSerializer(serializers.ModelSerializer):
password = serializers.CharField(max_length=65, min_length=8, write_only=True)
username = serializers.CharField(max_length=220)
class Meta:
model = User
fields = ['username', 'password']
backends.py
import jwt
from django.contrib.auth.models import User
from rest_framework import authentication, exceptions
from django.conf import settings
class JWTAuth(authentication.BaseAuthentication):
def authenticate(self, request):
auth_data = authentication.get_authorization_header(request)
print(auth_data)
if not auth_data:
return None
prefix, token = auth_data.decode('utf-8').split('')
try:
payload = jwt.decode(token, settings.JWT_SECRET_KEY, algorithms='HS256')
user = User.objects.get(username = payload['username'])
return (user, token)
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed('your token is invalid.')
except jwt.DecodeError as identifier:
raise exceptions.AuthenticationFailed('your token is expired.')
return super().authenticate(request
settings.py
#JWT
JWT_SECRET_KEY = os.environ.get('JWT_SECRET_KEY')
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'api',
'rest_framework'
]
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'api.backends.JWTAuth',
)
}
Solution 1:[1]
This issue happens because os.env os.environ.get
is return None
I too faced the same issue, make sure your .env file looks like this
export JWT_SECRET_KEY='your secret key here'
and .env file should be next to or in same level to settings.py
please refer to images below [![enter image description here][1]][1]
and if you want some more fun to play with multiple environment variable check this resources
Solution 2:[2]
On your LoginView, the auth_token
cannot be encoded because you have not defined the payload parameter as string.
You also need to define your secret key.
Change your code to look like this:
if user:
key='JWT_SECRET_KEY'
auth_token = jwt.encode ({'user':"payload"}, key, algorithm="HS256")
serializer = UserSerializer(user)
data={'user':serializer.data, 'token':auth_token}
return Response(data, status=status.HTTP_200_OK)
This should work.
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
Solution | Source |
---|---|
Solution 1 | |
Solution 2 | Drice |