Search code examples
pythondjangoauthenticationcachingredis

Django Caching Token based Authentication


I am using DRF for Token authentication as: while a user login, i am creating a token and for every user request Django is hitting database (for verifying token and get user info). So i want to cache the tokens of currently logged IN users into my Redis cache (running on same server). So that i can avoid a lot of Database hits and my request/response cycle will be faster.
I am new to django and got struct to implement this. Please help me with code or should i change from tokenbased to session based authentication?. (My frontend is mobile app, So i should use token based auth only.)

I am currently using DRF token based authentication for every view as:-

@api_view(['GET','POST'])
@permission_classes([IsAuthenticated])        #checks for token and gets the user object
def Test_view(request):
    #some db queries
    Return Response({'test':'returned'})

created login view as follows:-

from rest_framework.authtoken.models import Token
from django.contrib.auth import authenticate

@api_view(['POST'])
@permission_classes([AllowAny])
def UserLogin(request):
    if request.method=='POST':
        username = request.data.get('username')
        password = request.data.get('password')
        user = authenticate(username=username, password=password)
        if user is not None:
            token, created = Token.objects.get_or_create(user=user)
            return Response({'token': token.key,'name':user.fname+' '+user.lname,
                             'phone':user.phone,'username':user.username,'email':user.email})
        else:
            return Response({'error': 'Invalid credentials'})

in my settings.py:-

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES' : (
        'rest_framework.authentication.TokenAuthentication',
    ),
}

and i am currently using redis cache:-

CACHES = {
   'default': {
      'BACKEND': 'django_redis.cache.RedisCache',
      'LOCATION': "redis://127.0.0.1:6379",
      'OPTIONS': {
            'CLIENT_CLASS': 'django_redis.client.DefaultClient',
            'IGNORE_EXCEPTIONS': True,
        },
      'TIMEOUT': None,
   }
}

Solution

  • If you want stateless auth that will not affect your database in the long run, consider JWT.

    Install simplejwt:

    pip install djangorestframework-simplejwt
    

    Use JWTStatelessUserAuthentication:

    REST_FRAMEWORK = {
        ...
        'DEFAULT_AUTHENTICATION_CLASSES': (
            ...
            'rest_framework_simplejwt.authentication.JWTStatelessUserAuthentication',
        )
        ...
    }
    

    Generate token:

    from django.contrib.auth import authenticate
    from rest_framework_simplejwt.tokens import RefreshToken
    
    def get_tokens_for_user(user):
        refreshToken = RefreshToken.for_user(user)
        return {
            'accessToken': str(refreshToken.access_token),
            'refreshToken': str(refreshToken)
        }
    
    @api_view(['POST'])
    @permission_classes([AllowAny])
    def UserLogin(request):
        if request.method=='POST':
            username = request.data.get('username')
            password = request.data.get('password')
            user = authenticate(username=username, password=password)
            if user is not None:
                tokens = get_tokens_for_user(user)
                return Response(
                            tokens | {
                                'name': f"{user.fname} {user.lname}",
                                'phone': user.phone,
                                'username': user.username,
                                'email': user.email
                            }
                        )
            else:
                return Response({'error': 'Invalid credentials'})
    

    Reference: simplejwt.