Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat[bckend-middleware]:Implemented custom middleware to check for blacklisted access tokens in Redis. #107

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions services/api/kalvi/api/views/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from rest_framework_simplejwt.views import TokenRefreshView as SimpleJWTTokenRefreshView
from django.contrib.auth import authenticate
from db.renderer import UserRenderer
from rest_framework_simplejwt.tokens import RefreshToken
from rest_framework_simplejwt.tokens import RefreshToken, AccessToken
from rest_framework_simplejwt.exceptions import TokenError
from rest_framework.permissions import AllowAny
from django.utils.encoding import smart_str
Expand Down Expand Up @@ -155,19 +155,24 @@ def post(self, request, uid, token, format=None):
except ValidationError as e:
return Response({'error': e.detail}, status=status.HTTP_400_BAD_REQUEST)

# Viewset class for blocking refresh tokens after logging out.
# Viewset class for blocking tokens after logging out.
class SignOutEndpoint(APIView):
def post(self, request):
refresh_token = request.data.get('refresh_token')
if refresh_token:
access_token = request.headers.get('Authorization').split(' ')[1] #We are catching access tokens from authorization header.
if refresh_token:
try:
# Connect to Redis
redis_conn = get_redis_connection()
token = str(RefreshToken(refresh_token))
# Blacklist the token in Redis
expiration_time = int(settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'].total_seconds())
redis_conn.set(token, 'blacklisted')
redis_conn.expire(token, expiration_time)
refresh_token_str = str(RefreshToken(refresh_token))
access_token_str = str(AccessToken(access_token))
# Blacklist tokens in Redis
refresh_exp_time = int(settings.SIMPLE_JWT['REFRESH_TOKEN_LIFETIME'].total_seconds())
access_exp_time = int(settings.SIMPLE_JWT['ACCESS_TOKEN_LIFETIME'].total_seconds())
with redis_conn.pipeline() as pipe:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a separate class for Redis in a separate file and implement the logic there.

pipe.set(refresh_token_str, 'blacklisted', ex=refresh_exp_time)
pipe.set(access_token_str, 'blacklisted', ex=access_exp_time)
pipe.execute()
return Response({'message': 'Logged out successfully'}, status=status.HTTP_200_OK)
except Exception:
return Response({'error': 'Please try after some time'}, status=status.HTTP_500_INTERNAL_SERVER_ERROR)
Expand Down
Empty file.
27 changes: 27 additions & 0 deletions services/api/kalvi/middlewares/tokenMiddleware.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
from rest_framework_simplejwt.tokens import AccessToken
from rest_framework import status
from kalvi.api.views.auth import get_redis_connection
from django.http import JsonResponse

class CustomOutstandingTokenMiddleware:
"""
This middleware checks for blacklisted tokens in Redis. It does not perform any token validation.
"""
def __init__(self, get_response=None):
self.get_response = get_response

def __call__(self, request):
try:
auth_header = request.headers.get('Authorization')
if not auth_header:
return self.get_response(request)
token = auth_header.split()[1]
token_obj = str(AccessToken(token))
redis_conn = get_redis_connection()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Create a separate class for Redis in a separate file and implement the logic there.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

there should be separate file for redis config too and blacklisting refresh token too, I am thinking raising seperate PR for this , kinda chore/refactor tasks. I will put everything related to redis at one place.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what is your view on this?

# Check if the token is blacklisted in Redis
if redis_conn.exists(token_obj):
return JsonResponse({'error': "Access token is blacklisted"}, status=status.HTTP_401_UNAUTHORIZED)
else:
return self.get_response(request)
except Exception:
return self.get_response(request)
1 change: 1 addition & 0 deletions services/api/kalvi/settings.py
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"django.middleware.common.CommonMiddleware",
"django.middleware.csrf.CsrfViewMiddleware",
"django.contrib.auth.middleware.AuthenticationMiddleware",
"kalvi.middlewares.tokenMiddleware.CustomOutstandingTokenMiddleware",
"django.contrib.messages.middleware.MessageMiddleware",
"django.middleware.clickjacking.XFrameOptionsMiddleware",
]
Expand Down
Loading