feat: add 4-hour token expiration handling in ExpiringToken model
This commit is contained in:
parent
7da5f0693d
commit
fee493191e
2 changed files with 53 additions and 13 deletions
|
|
@ -3,14 +3,30 @@ from rest_framework.views import APIView
|
||||||
from rest_framework.response import Response
|
from rest_framework.response import Response
|
||||||
from rest_framework import status, authentication
|
from rest_framework import status, authentication
|
||||||
from rest_framework.authtoken.models import Token
|
from rest_framework.authtoken.models import Token
|
||||||
from rest_framework.authtoken.views import ObtainAuthToken
|
|
||||||
from django.contrib.auth import authenticate
|
from django.contrib.auth import authenticate
|
||||||
|
from join.settings import TOKEN_EXPIRATION_TIME
|
||||||
from .serializer import LoginSerializer
|
from .serializer import LoginSerializer
|
||||||
from rest_framework.permissions import IsAuthenticated
|
from rest_framework.permissions import IsAuthenticated
|
||||||
|
from django.db import models
|
||||||
|
from django.utils import timezone
|
||||||
|
|
||||||
|
|
||||||
class LoginView(APIView):
|
class LoginView(APIView):
|
||||||
serializer_class = LoginSerializer
|
serializer_class = LoginSerializer
|
||||||
|
|
||||||
|
def _create_token_response(self, user):
|
||||||
|
token, created = ExpiringToken.objects.get_or_create(user=user)
|
||||||
|
|
||||||
|
if not created and token.is_expired():
|
||||||
|
token.delete()
|
||||||
|
token = ExpiringToken.objects.create(user=user)
|
||||||
|
|
||||||
|
if created or not token.expires_at:
|
||||||
|
token.expires_at = timezone.now() + TOKEN_EXPIRATION_TIME
|
||||||
|
token.save()
|
||||||
|
|
||||||
|
return Response({'token': token.key, 'user_id': user.id}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
serializer = self.serializer_class(data=request.data)
|
serializer = self.serializer_class(data=request.data)
|
||||||
|
|
||||||
|
|
@ -27,26 +43,44 @@ class LoginView(APIView):
|
||||||
|
|
||||||
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
|
||||||
|
|
||||||
def _create_token_response(self, user):
|
|
||||||
token, created = Token.objects.get_or_create(user=user)
|
|
||||||
return Response({'token': token.key, 'user_id' : user.id}, status=status.HTTP_200_OK)
|
|
||||||
|
|
||||||
class LogoutView(APIView):
|
class LogoutView(APIView):
|
||||||
authentication_classes = [authentication.TokenAuthentication]
|
authentication_classes = [authentication.TokenAuthentication]
|
||||||
permission_classes = [IsAuthenticated]
|
permission_classes = [IsAuthenticated]
|
||||||
|
|
||||||
def post(self, request):
|
def post(self, request):
|
||||||
try:
|
token = getattr(request.user, 'auth_token', None)
|
||||||
request.user.auth_token.delete()
|
|
||||||
return Response({"message": "Successfully logged out"}, status=status.HTTP_200_OK)
|
if token is None:
|
||||||
except Exception as e:
|
return Response({"error": "No active session or token already expired"}, status=status.HTTP_400_BAD_REQUEST)
|
||||||
return Response({"error": "Logout failed"}, status=status.HTTP_400_BAD_REQUEST)
|
|
||||||
|
token.delete()
|
||||||
|
return Response({"message": "Successfully logged out"}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
class AuthView(APIView):
|
class AuthView(APIView):
|
||||||
authentication_classes = [authentication.TokenAuthentication]
|
authentication_classes = [authentication.TokenAuthentication]
|
||||||
|
|
||||||
def get(self, request):
|
def get(self, request):
|
||||||
if request.user.is_authenticated:
|
if not request.user.is_authenticated:
|
||||||
return Response({"user_id": request.user.id}, status=status.HTTP_200_OK)
|
return Response({"error": "User is not logged in"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
return Response({"error": "User is not logged in"}, status=status.HTTP_401_UNAUTHORIZED)
|
|
||||||
|
|
||||||
|
try:
|
||||||
|
token = ExpiringToken.objects.get(user=request.user)
|
||||||
|
except ExpiringToken.DoesNotExist:
|
||||||
|
return Response({"error": "Token does not exist, please log in again"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
if token.is_expired():
|
||||||
|
token.delete()
|
||||||
|
return Response({"error": "Token expired, please log in again"}, status=status.HTTP_401_UNAUTHORIZED)
|
||||||
|
|
||||||
|
return Response({"user_id": request.user.id}, status=status.HTTP_200_OK)
|
||||||
|
|
||||||
|
class ExpiringToken(Token):
|
||||||
|
expires_at = models.DateTimeField(null=True, blank=True)
|
||||||
|
|
||||||
|
def is_expired(self):
|
||||||
|
return self.expires_at and self.expires_at < timezone.now()
|
||||||
|
|
||||||
|
def save(self, *args, **kwargs):
|
||||||
|
if not self.expires_at:
|
||||||
|
self.expires_at = timezone.now() + TOKEN_EXPIRATION_TIME
|
||||||
|
super().save(*args, **kwargs)
|
||||||
|
|
@ -11,6 +11,7 @@ https://docs.djangoproject.com/en/5.1/ref/settings/
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
|
from datetime import timedelta
|
||||||
|
|
||||||
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
# Build paths inside the project like this: BASE_DIR / 'subdir'.
|
||||||
BASE_DIR = Path(__file__).resolve().parent.parent
|
BASE_DIR = Path(__file__).resolve().parent.parent
|
||||||
|
|
@ -150,6 +151,11 @@ STATIC_URL = 'static/'
|
||||||
|
|
||||||
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
DEFAULT_AUTO_FIELD = 'django.db.models.BigAutoField'
|
||||||
|
|
||||||
|
# Token
|
||||||
|
|
||||||
|
AUTH_TOKEN_MODEL = 'auth_app.ExpiringToken'
|
||||||
|
|
||||||
|
TOKEN_EXPIRATION_TIME = timedelta(hours=4)
|
||||||
|
|
||||||
# Authentication
|
# Authentication
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue