I have created a basic app using Django's built in authentication system. I successfully created a User object in the shell using
>>python manage.py createsuperuser
.
I then created a basic view, 'UserLogin' along with corresponding serializers/urls, to log an existing user in using the django.contrib.auth authenticate(), and login() functions. Upon testing with the credentials of my created user, the login function seemed to have worked successfully.
To test this, I created another view function, 'CurrentUser' which returns the username of the currently logged in user. However, this view returns the user as empty.
Why would the 'CurrentUser' view be returning no user as logged in? I have attached my code (minus imports) below.
views.py:
class UserLogin(APIView):
def post(self, request, format = None):
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
user = authenticate(username=serializer.validated_data["username"], password=serializer.validated_data["password"])
if user is not None:
login(request, user)
return Response(UserSerializer(user).data, status=status.HTTP_201_CREATED)
return Response("Invalid username/password", status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_401_UNAUTHORIZED)
class CurrentUser(APIView):
def get(self, request, format = None):
return Response(self.request.user.username)
serializers.py:
class UserSerializer(serializers.ModelSerializer):
class Meta:
model = User
fields = ['id', 'username']
class UserLoginSerializer(serializers.Serializer):
username = serializers.CharField(max_length=300, required=True)
password = serializers.CharField(required=True, write_only=True)
urls.py:
urlpatterns = [
path('login/', views.UserLogin.as_view()),
path('current/', views.CurrentUser.as_view())
]
Any guidance would be much appreciated.
Thanks
When django. contrib. auth is listed in your INSTALLED_APPS setting, it will ensure that four default permissions – add, change, delete, and view – are created for each Django model defined in one of your installed applications.
Django by default will look within a templates folder called registration for auth templates. The login template is called login. html . Create a new directory called templates and within it another directory called registration .
You have to set the default auth class as session authenticate class in DRF settings. Read more about it here [1].
Session auth uses session id to identify the user. So you have to send the cookie based session id in the request. Read about session auth here [2].
for example:
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
'rest_framework.authentication.SessionAuthentication', # <-- set this class
]
}
Use this code:
def post(self, request, format = None):
serializer = UserLoginSerializer(data=request.data)
if serializer.is_valid():
user = authenticate(username=serializer.validated_data["username"], password=serializer.validated_data["password"])
if user:
return Response(UserSerializer(user).data, status=status.HTTP_201_CREATED)
return Response("Invalid username/password", status=status.HTTP_401_UNAUTHORIZED)
return Response(serializer.errors, status=status.HTTP_401_UNAUTHORIZED)
But my recommendation is to use Token auth [3].
To use token auth 2 things will change:
Token <token-value>
Your post method and API views code will remain same.
[1] https://www.django-rest-framework.org/api-guide/authentication/#setting-the-authentication-scheme
[2] https://www.django-rest-framework.org/api-guide/authentication/#sessionauthentication
[3] https://www.django-rest-framework.org/api-guide/authentication/#tokenauthentication
I am using DRF for login. You can check this serializers and login view.
serializers.py:
class LoginSerializer(serializers.Serializer):
username = 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")
login view:
class LoginAPIView(generics.GenericAPIView):
queryset = User.objects.all()
permission_classes = [AllowAny]
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": UserSerializer(user, context=self.get_serializer_context()).data,
"token": AuthToken.objects.create(user)[1]
})
settings.py rest framework classes :
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': [
# 'rest_framework.authentication.BasicAuthentication',
# 'rest_framework.authentication.SessionAuthentication',
'knox.auth.TokenAuthentication',
],
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
],
'DEFAULT_FILTER_BACKENDS': ['django_filters.rest_framework.DjangoFilterBackend']
}
You were trying to test the authentication in the wrong way. This is API, which is stateless. That is Django doesn't have any idea about the previous request that you have made to the UserLogin
.
So, you need to attach the token and session-id (or something that can be used to identify the user) with the "second request".
Since you are using DRF, you don't have to write your own authentication mechanisms. You can use DRF's BasicAuthentication (or any other authentication scheme) to authenticate.
So, change your view as
from rest_framework.authentication import BasicAuthentication
from rest_framework.permissions import IsAuthenticated
class CurrentUser(APIView):
authentication_classes = (BasicAuthentication,)
permission_classes = (IsAuthenticated,)
def get(self, request, format=None):
return Response({
"user": {
"id": request.user.id,
"username": request.user.username
}})
Execution using requests
In [5]: import requests
In [6]: from requests import auth
In [7]: response = requests.get(url=url, auth=auth.HTTPBasicAuth("admin", "passme123@#$"))
In [8]: print(response.json())
{'user': {'id': 1, 'username': 'admin'}}
Since you are using DRF, you can make use of any of these DRF authentications schemes in your code-base
Since you are using Django rest framework, i m assuming you are developing rest apis. Rest Apis by design are meant to be stateless meaning no two requests will be dependent on each other.
What this essentially means is you have to authenticate every request and set the user in request after authentication.
There are many ways of doing it like many have suggested to use basicAuth, or token based authentication. All of which can we easily implemented by reading documentation Auth Doc
The out of the box django views uses cookies to identify logged in user. Which in this case is not helpful.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With