Token Authentication for RESTful API: should the token be periodically changed?

后端 未结 10 1259
庸人自扰
庸人自扰 2020-12-02 03:20

I\'m building a RESTful API with Django and django-rest-framework.

As authentication mechanism we have chosen \"Token Authentication\" and I have already implemented

相关标签:
10条回答
  • 2020-12-02 04:03

    If anyone wants to expire the token after certain time of inactivity, below answer would help. I am tweaking one of the answers given here. I have added comments to the code I added

    from rest_framework.authentication import TokenAuthentication
    from datetime import timedelta
    from datetime import datetime
    import datetime as dtime
    import pytz
    
    class ExpiringTokenAuthentication(TokenAuthentication):
    
        def authenticate_credentials(self, key):
            model = self.get_model()
            try:
                token = model.objects.get(key=key)
            except model.DoesNotExist:
                raise exceptions.AuthenticationFailed('Invalid token')
    
            if not token.user.is_active:
                raise exceptions.AuthenticationFailed('User inactive or deleted')
    
            # This is required for the time comparison
            utc_now = datetime.now(dtime.timezone.utc)
            utc_now = utc_now.replace(tzinfo=pytz.utc)
    
            if token.created < utc_now - timedelta(minutes=15):  # TOKEN WILL EXPIRE AFTER 15 MINUTES OF INACTIVITY
                token.delete() # ADDED THIS LINE SO THAT EXPIRED TOKEN IS DELETED
                raise exceptions.AuthenticationFailed('Token has expired')
            else: 
                token.created = utc_now #THIS WILL SET THE token.created TO CURRENT TIME WITH EVERY REQUEST
                token.save() #SAVE THE TOKEN
    
            return token.user, token
    
    0 讨论(0)
  • 2020-12-02 04:05

    If you notice that a token is like a session cookie then you could stick to the default lifetime of session cookies in Django: https://docs.djangoproject.com/en/1.4/ref/settings/#session-cookie-age.

    I don't know if Django Rest Framework handles that automatically but you can always write a short script which filters out the outdated ones and marks them as expired.

    0 讨论(0)
  • 2020-12-02 04:05

    just to keep adding to @odedfos answer, I think there have been some changes to the syntax so the code of ExpiringTokenAuthentication needs some adjusting:

    from rest_framework.authentication import TokenAuthentication
    from datetime import timedelta
    from datetime import datetime
    import datetime as dtime
    import pytz
    
    class ExpiringTokenAuthentication(TokenAuthentication):
    
        def authenticate_credentials(self, key):
            model = self.get_model()
            try:
                token = model.objects.get(key=key)
            except model.DoesNotExist:
                raise exceptions.AuthenticationFailed('Invalid token')
    
            if not token.user.is_active:
                raise exceptions.AuthenticationFailed('User inactive or deleted')
    
            # This is required for the time comparison
            utc_now = datetime.now(dtime.timezone.utc)
            utc_now = utc_now.replace(tzinfo=pytz.utc)
    
            if token.created < utc_now - timedelta(hours=24):
                raise exceptions.AuthenticationFailed('Token has expired')
    
            return token.user, token
    

    Also, don't forget to add it to DEFAULT_AUTHENTICATION_CLASSES instead of rest_framework.authentication.TokenAuthentication

    0 讨论(0)
  • 2020-12-02 04:07

    If someone is interested by that solution but wants to have a token that is valid for a certain time then gets replaced by a new token here's the complete solution (Django 1.6):

    yourmodule/views.py:

    import datetime
    from django.utils.timezone import utc
    from rest_framework.authtoken.views import ObtainAuthToken
    from rest_framework.authtoken.models import Token
    from django.http import HttpResponse
    import json
    
    class ObtainExpiringAuthToken(ObtainAuthToken):
        def post(self, request):
            serializer = self.serializer_class(data=request.DATA)
            if serializer.is_valid():
                token, created =  Token.objects.get_or_create(user=serializer.object['user'])
    
                utc_now = datetime.datetime.utcnow()    
                if not created and token.created < utc_now - datetime.timedelta(hours=24):
                    token.delete()
                    token = Token.objects.create(user=serializer.object['user'])
                    token.created = datetime.datetime.utcnow()
                    token.save()
    
                #return Response({'token': token.key})
                response_data = {'token': token.key}
                return HttpResponse(json.dumps(response_data), content_type="application/json")
    
            return HttpResponse(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
    
    obtain_expiring_auth_token = ObtainExpiringAuthToken.as_view()
    

    yourmodule/urls.py:

    from django.conf.urls import patterns, include, url
    from weights import views
    
    urlpatterns = patterns('',
        url(r'^token/', 'yourmodule.views.obtain_expiring_auth_token')
    )
    

    your project urls.py (in the urlpatterns array):

    url(r'^', include('yourmodule.urls')),
    

    yourmodule/authentication.py:

    import datetime
    from django.utils.timezone import utc
    from rest_framework.authentication import TokenAuthentication
    from rest_framework import exceptions
    
    class ExpiringTokenAuthentication(TokenAuthentication):
        def authenticate_credentials(self, key):
    
            try:
                token = self.model.objects.get(key=key)
            except self.model.DoesNotExist:
                raise exceptions.AuthenticationFailed('Invalid token')
    
            if not token.user.is_active:
                raise exceptions.AuthenticationFailed('User inactive or deleted')
    
            utc_now = datetime.datetime.utcnow()
    
            if token.created < utc_now - datetime.timedelta(hours=24):
                raise exceptions.AuthenticationFailed('Token has expired')
    
            return (token.user, token)
    

    In your REST_FRAMEWORK settings add ExpiringTokenAuthentication as an Authentification class instead of TokenAuthentication:

    REST_FRAMEWORK = {
    
        'DEFAULT_AUTHENTICATION_CLASSES': (
            'rest_framework.authentication.SessionAuthentication',
            #'rest_framework.authentication.TokenAuthentication',
            'yourmodule.authentication.ExpiringTokenAuthentication',
        ),
    }
    
    0 讨论(0)
提交回复
热议问题