Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

last_login field is not updated when authenticating using Tokenauthentication in Django Rest Framework

I'm working in a project which relies in a Django User model and TokenAuthentication under DjangoRestFramework

I was requested to get last login datetime for each user and I've realized that this field is not getting updated when I call the authentication REST endpoint.

Is this a known fact? Am I missing something I must do in order to get that field updated each time the token authentication is called?

Thanks

like image 825
F.D.F. Avatar asked Feb 19 '15 17:02

F.D.F.


6 Answers

Well, at the end I inherited from the REST Framework TokenAuthentication, pointing to it in the urls file

url(r'^api-token-auth/', back_views.TokenAuthenticationView.as_view()),

and the View handles the request and manually calls the update_last_login like this:

from django.contrib.auth.models import update_last_login

class TokenAuthenticationView(ObtainAuthToken):
    """Implementation of ObtainAuthToken with last_login update"""

    def post(self, request):
        result = super(TokenAuthenticationView, self).post(request)
        try:
            request_user, data = requests.get_parameters(request)
            user = requests.get_user_by_username(data['username'])
            update_last_login(None, user)            
        except Exception as exc:
            return None
        return result
like image 126
F.D.F. Avatar answered Oct 16 '22 17:10

F.D.F.


@F.D.F. answer is great. Here is another way of doing it.

We send user_logged_in signals that will call update_last_login:

user_logged_in.send(sender=user.__class__, request=request, user=user)

Here is a working view (based on a custom User model that uses email as USERNAME_FIELD) :

from rest_framework import parsers, renderers
from rest_framework.authtoken.models import Token
from rest_framework.response import Response
from rest_framework.views import APIView

from django.contrib.auth.signals import user_logged_in
from emailauth.serializers import AuthTokenSerializer, UserSerializer


class ObtainAuthToken(APIView):
    throttle_classes = ()
    permission_classes = ()
    parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
    renderer_classes = (renderers.JSONRenderer,)
    serializer_class = AuthTokenSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)
        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)
        user_logged_in.send(sender=user.__class__, request=request, user=user)
        return Response({'token': token.key, 'user': UserSerializer(user).data})


obtain_auth_token = ObtainAuthToken.as_view()

You can find the full source code here : Api View with last_login updated

Hope this helps.

like image 24
Charlesthk Avatar answered Oct 16 '22 17:10

Charlesthk


A cleaner way to do it:

from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.models import Token
from rest_framework.authtoken.views import ObtainAuthToken

class LoginToken(ObtainAuthToken):
    def post(self, request, *args, **kwargs):
        result = super().post(request, *args, **kwargs)
        token = Token.objects.get(key=result.data['token'])
        update_last_login(None, token.user)
        return result

And then setup urls.py:

url(r'^api-token-auth/', views.LoginToken.as_view()),
like image 6
Guto Marrara Marzagao Avatar answered Oct 16 '22 19:10

Guto Marrara Marzagao


My answer for Django==2.0.5, django-rest-framework-social-oauth2==1.1.0

from django.contrib.auth import user_logged_in
from oauth2_provider.models import AccessToken
from rest_framework import status
from rest_framework_social_oauth2.views import TokenView

class MyTokenView(TokenView):
    def post(self, request, *args, **kwargs):
        response = super().post(request, *args, **kwargs)
        if response.status_code == status.HTTP_200_OK:
            token = AccessToken.objects.get(token=response.data['access_token'])
            user = token.user
            user_logged_in.send(sender=type(user), request=request, user=user)
        return response

urls.py:

from django.urls import path

urlpatterns = [
    path('token', MyTokenView.as_view(), name='token'),
]
like image 2
dtatarkin Avatar answered Oct 16 '22 18:10

dtatarkin


Here is my solution using a ViewSet:

views.py:

from rest_framework.authtoken.views import ObtainAuthToken
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework import viewsets
from django.contrib.auth.models import update_last_login

class LoginViewSet(viewsets.ViewSet):
    """Checks email and password and returns an auth token."""

    serializer_class = AuthTokenSerializer

    def create(self, request):
        """Use the ObtainAuthToken APIView to validate and create a token."""

        ##update last_login
        try:
            user = models.User.objects.get(email = request.data['username'])
            update_last_login(None, user)
        except:
            pass

        return ObtainAuthToken().post(request)

Now just add this viewset to the urls.py:

router.register('login', views.LoginViewSet, base_name="login")
like image 1
Sapnesh Naik Avatar answered Oct 16 '22 17:10

Sapnesh Naik


This is the newest code for django 3.0.8.
:)
thx F.D.F.!

from django.contrib.auth.models import update_last_login
from rest_framework.authtoken.views import ObtainAuthToken
from django.contrib.auth import get_user_model


class TokenAuthenticationView(ObtainAuthToken):
    """Implementation of ObtainAuthToken with last_login update"""
    
    def post(self, request):
        result = super(TokenAuthenticationView, self).post(request)
        currentUserModel = get_user_model()
        try:
            user = currentUserModel.objects.get(username=request.data['username'])
            update_last_login(None, user)
        except Exception as exc:
            return None
        return result
like image 1
minsu kim Avatar answered Oct 16 '22 17:10

minsu kim