Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

django.contrib.auth.login() function not returning any user as logged in

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

like image 427
Peter Jordanson Avatar asked Aug 30 '20 17:08

Peter Jordanson


People also ask

What does Django contrib Auth Login do?

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.

How do I login as user in Django?

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 .


4 Answers

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:

  1. The default auth class in DRF settings
  2. When sending a request to any DRF API view you to send the Auth header as 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

like image 171
zaphod100.10 Avatar answered Nov 14 '22 23:11

zaphod100.10


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']

}

like image 31
Shadab Khan Avatar answered Nov 14 '22 22:11

Shadab Khan


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

like image 26
JPG Avatar answered Nov 14 '22 22:11

JPG


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.

like image 22
Shashank Singh Avatar answered Nov 14 '22 22:11

Shashank Singh