UPDATE AUTH

This commit is contained in:
2026-03-23 22:24:30 +03:30
parent a98189a7e9
commit 768d5ea543
14 changed files with 390 additions and 96 deletions
+21 -1
View File
@@ -1,6 +1,27 @@
from rest_framework import serializers
# --- Register ---
class RegisterSerializer(serializers.Serializer):
"""Request body for POST /api/auth/register/."""
username = serializers.CharField(max_length=150)
email = serializers.EmailField()
phone_number = serializers.CharField(max_length=32)
password = serializers.CharField(min_length=8, write_only=True)
first_name = serializers.CharField(max_length=150, required=False, default="")
last_name = serializers.CharField(max_length=150, required=False, default="")
# --- Login ---
class LoginSerializer(serializers.Serializer):
"""Request body for POST /api/auth/login/.
identifier can be username, email, or phone_number."""
identifier = serializers.CharField()
password = serializers.CharField()
# --- RequestOTP (request-otp/) ---
class RequestOTPSerializer(serializers.Serializer):
"""Request body for POST /api/auth/request-otp/."""
@@ -26,4 +47,3 @@ class AuthUserSerializer(serializers.Serializer):
first_name = serializers.CharField()
last_name = serializers.CharField()
phone_number = serializers.CharField()
+5 -4
View File
@@ -1,9 +1,10 @@
from django.urls import path
from .views import AuthenticationView
from .views import AuthenticationView, LoginView, RegisterView
urlpatterns = [
path("request-otp/", AuthenticationView.as_view(), name="request-otp"),
path("verify-otp/", AuthenticationView.as_view(), name="verify-otp"),
path("register/", RegisterView.as_view(), name="register"),
path("login/", LoginView.as_view(), name="login"),
# path("request-otp/", AuthenticationView.as_view(), name="request-otp"),
# path("verify-otp/", AuthenticationView.as_view(), name="verify-otp"),
]
+105 -2
View File
@@ -1,15 +1,22 @@
import secrets
from django.contrib.auth import authenticate
from django.conf import settings
from django.core.cache import cache
from django.core.signing import BadSignature, SignatureExpired, TimestampSigner
from django.db import IntegrityError
from rest_framework import status
from rest_framework.response import Response
from rest_framework.views import APIView
from rest_framework_simplejwt.tokens import RefreshToken
from account.models import User
from .serializers import RequestOTPSerializer, VerifyOTPSerializer
from .serializers import (
LoginSerializer,
RegisterSerializer,
RequestOTPSerializer,
VerifyOTPSerializer,
)
from .sms_service import send_otp_sms
@@ -31,6 +38,99 @@ def _auth_user_to_data(user):
}
class RegisterView(APIView):
"""
POST /api/auth/register/
Creates a new user with username, email, phone_number, and password.
All fields are required (first_name, last_name optional).
Returns JWT tokens and user data on success.
"""
def post(self, request):
serializer = RegisterSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
data = serializer.validated_data
try:
user = User.objects.create_user(
username=data["username"],
email=data["email"],
phone_number=data["phone_number"],
password=data["password"],
first_name=data.get("first_name", ""),
last_name=data.get("last_name", ""),
)
except IntegrityError as exc:
msg = str(exc).lower()
if "username" in msg:
detail = "A user with this username already exists."
elif "email" in msg:
detail = "A user with this email already exists."
elif "phone_number" in msg:
detail = "A user with this phone number already exists."
else:
detail = "A user with these credentials already exists."
return Response(
{"code": 400, "msg": detail},
status=status.HTTP_400_BAD_REQUEST,
)
refresh = RefreshToken.for_user(user)
user_data = _auth_user_to_data(user)
return Response(
{
"code": 201,
"msg": "success",
"data": user_data,
"token": {
"access": str(refresh.access_token),
"refresh": str(refresh),
},
},
status=status.HTTP_201_CREATED,
)
class LoginView(APIView):
"""
POST /api/auth/login/
Accepts identifier (username, email, or phone_number) + password.
Returns JWT tokens and user data on success.
"""
def post(self, request):
serializer = LoginSerializer(data=request.data)
serializer.is_valid(raise_exception=True)
identifier = serializer.validated_data["identifier"]
password = serializer.validated_data["password"]
user = authenticate(request, username=identifier, password=password)
if user is None:
return Response(
{"code": 401, "msg": "Invalid credentials."},
status=status.HTTP_401_UNAUTHORIZED,
)
refresh = RefreshToken.for_user(user)
user_data = _auth_user_to_data(user)
return Response(
{
"code": 200,
"msg": "success",
"data": user_data,
"token": {
"access": str(refresh.access_token),
"refresh": str(refresh),
},
},
status=status.HTTP_200_OK,
)
class AuthenticationView(APIView):
"""
Single view for auth flows: request-otp and verify-otp.
@@ -91,7 +191,10 @@ class AuthenticationView(APIView):
user, created = User.objects.get_or_create(
phone_number=phone_number,
defaults={"username": phone_number},
defaults={
"username": phone_number,
"email": f"{phone_number}@otp.local",
},
)
refresh = RefreshToken.for_user(user)