Mobile Registration Flow with JWT Authentication and Rate-Limited SMS Verification

  1. Frontend validates mobile number format client-side.
  2. Enitiates a GET request to the mobile validation endpoint.
  3. If the number is unregistered, enables the "Send Code" button.
  4. User submits verification code and password via POST to the registration endpoint.
  5. Backend verifies code against Redis cache, creates user, and returns token-secured response.

Mobile Number Validation Endpoint

This read-only API checks whether a given phone number is already associated with an active account.

# luffyapi/apps/user/urls.py
from django.urls import path
from . import views

urlpatterns = [
    # ...
    path('check-mobile/', views.MobileAvailabilityCheck.as_view()),
]

# luffyapi/apps/user/views.py
import re
from rest_framework.views import APIView
from rest_framework.response import Response
from . import models

class MobileAvailabilityCheck(APIView):
    def get(self, request):
        number = request.query_params.get('mobile', '').strip()

        if not number:
            return Response({'code': 1, 'message': 'Mobile number is required'}, status=400)
        if not re.fullmatch(r'1[3-9]\d{9}', number):
            return Response({'code': 1, 'message': 'Invalid mobile format'}, status=400)

        exists = models.User.objects.filter(mobile=number, is_active=True).exists()
        return Response({
            'code': 0 if not exists else 2,
            'message': 'Available' if not exists else 'Already registered'
        })

Mobile-Based User Registration

Accepts mobile number, six-digit numeric code, and password; performs validation and persists user securely.

# luffyapi/apps/user/urls.py
urlpatterns += [
    path('register-by-mobile/', views.MobileRegistrationView.as_view()),
]

# luffyapi/apps/user/views.py
from rest_framework.generics import CreateAPIView
from django.contrib.auth import get_user_model
from django.core.cache import cache
from . import my_serializers, settings

User = get_user_model()

class MobileRegistrationView(CreateAPIView):
    serializer_class = my_serializers.MobileRegistrationSerializer

    def create(self, request, *args, **kwargs):
        response = super().create(request, *args, **kwargs)
        response.data.pop('password', None)
        return response

# luffyapi/apps/user/my_serializers.py
import re
import django.core.cache as cache_module
from rest_framework import serializers
from . import models, settings

class MobileRegistrationSerializer(serializers.Serializer):
    mobile = serializers.CharField()
    code = serializers.RegexField(r'^\d{6}$', write_only=True)
    password = serializers.CharField(min_length=8)

    def validate_mobile(self, value):
        if not re.fullmatch(r'1[3-9]\d{9}', value):
            raise serializers.ValidationError('Invalid mobile number')
        return value

    def validate_code(self, value):
        mobile = self.initial_data.get('mobile')
        cached = cache_module.cache.get(settings.SMS_CODE_CACHE_KEY.format(mobile))
        if cached != value:
            raise serializers.ValidationError('Invalid or expired verification code')
        return value

    def create(self, validated_data):
        mobile = validated_data['mobile']
        user = User.objects.create_user(
            username=mobile,
            mobile=mobile,
            password=validated_data['password']
        )
        cache_module.cache.delete(settings.SMS_CODE_CACHE_KEY.format(mobile))
        return user

JWT Authentication and Per-Mobile SMS Throttling

Global JWT configuration ensures all endpoints require valid tokens by default. SMS sending is restritced per mobile number using custom throttling logic.

# luffyapi/settings/dev_settings.py
REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
    ],
    'DEFAULT_THROTTLE_RATES': {
        'sms_per_mobile': '1/min',
    }
}

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': timedelta(days=1),
}

# luffyapi/apps/user/throttles.py
from rest_framework.throttling import SimpleRateThrottle

class MobileCodeThrottle(SimpleRateThrottle):
    scope = 'sms_per_mobile'

    def get_cache_key(self, request, view):
        mobile = request.query_params.get('mobile') or request.data.get('mobile')
        if not mobile:
            return None
        return f'sms_throttle_{mobile}'

# luffyapi/apps/user/views.py
from rest_framework.views import APIView
from .throttles import MobileCodeThrottle

class SmsDispatchView(APIView):
    throttle_classes = [MobileCodeThrottle]

    def get(self, request):
        mobile = request.query_params.get('mobile')
        # ... generate & cache 6-digit code, send via SMS gateway
        return Response({'status': 'sent'})

Client-Side Cookie Management in Vue.js

Use vue-cookies to persist authentication tokens or session identifiers across page reloads.

# Install
npm install vue-cookies

# Register globally in main.js
import Vue from 'vue';
import VueCookies from 'vue-cookies';
Vue.use(VueCookies);

# Usage in components
this.$cookies.set('auth_token', jwtToken, '1d'); // expires in 1 day
const token = this.$cookies.get('auth_token');
this.$cookies.remove('auth_token');

Tags: django-rest-framework jwt-authentication rate-limiting sms-verification vuejs-cookies

Posted on Fri, 31 Jul 2026 16:33:10 +0000 by Chris_Evans