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
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
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.
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
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',
),
}