From d2d17d8068dd49865e44d4e4ffafb4b06b6f5231 Mon Sep 17 00:00:00 2001 From: vickytechkey Date: Thu, 1 Oct 2026 15:55:09 +0530 Subject: [PATCH] feat: add authentication, registration, and OTP API views --- api/views.py | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) diff --git a/api/views.py b/api/views.py index 66ef5ce..dda1e06 100644 --- a/api/views.py +++ b/api/views.py @@ -115,14 +115,13 @@ class SendEmailOtpView(APIView): def post(self, request): email = request.data.get('email') - otp_code = request.data.get('otp_code') template_name = request.data.get('template_name', 'verification') if not email: return Response({'error': 'email is required'}, status=status.HTTP_400_BAD_REQUEST) - if not otp_code: - otp_code = str(random.randint(100000, 999999)) + # Always generate a fresh, unique 6-digit OTP code for every dispatch + otp_code = str(random.randint(100000, 999999)) user = request.user if (request.user and request.user.is_authenticated) else None if not user: @@ -380,18 +379,34 @@ class VerifyOtpView(APIView): 'user': UserSerializer(user_obj, context={'request': request}).data }) - if not user_obj: - return Response({'verified': False, 'error': 'No active user or OTP found'}, status=status.HTTP_400_BAD_REQUEST) - - record = OTPRecord.objects.filter( - user=user_obj, - type=otp_type, - used=False - ).order_by('-created_at').first() + # Smart Record Lookup: + # 1. Match by user_obj AND otp_hash + record = None + if user_obj: + record = OTPRecord.objects.filter( + user=user_obj, + type=otp_type, + otp_hash=h, + used=False, + expires_at__gte=timezone.now() + ).order_by('-created_at').first() + # 2. Match by type AND otp_hash across all unexpired records if not record: - # Fallback to check record across all active unexpired records matching hash - record = OTPRecord.objects.filter(type=otp_type, otp_hash=h, used=False, expires_at__gte=timezone.now()).order_by('-created_at').first() + record = OTPRecord.objects.filter( + type=otp_type, + otp_hash=h, + used=False, + expires_at__gte=timezone.now() + ).order_by('-created_at').first() + + # 3. Fallback to latest unexpired record for user_obj + if not record and user_obj: + record = OTPRecord.objects.filter( + user=user_obj, + type=otp_type, + used=False + ).order_by('-created_at').first() if not record: return Response({'verified': False, 'error': 'No active OTP found'}, status=status.HTTP_400_BAD_REQUEST)