Как лучше всего использовать номер телефона и код проверки в django?
Я хочу создать бэкенд аппликацию на django, и я хочу иметь аутотификацию используя только номер телефона, после того как клиент напишет номер телефона он получит код по смс, какой лучший способ решить эту задачу?
как изменить методы login и authaticate в базовой модели пользователя django
Чтобы реализовать аутентификацию по номеру телефона в Django, вы можете создать пользовательскую модель пользователя, которая наследует от AbstractUser следующим образом:
from django.db import models
from django.contrib.auth.models import AbstractUser
from .manager import UserManager
class User(AbstractUser):
phone_number = models.CharField(max_length=12, unique=True)
USERNAME_FIELD = 'phone_number'
REQUIRED_FIELDS = []
objects = UserManager()
также вам нужно что-то вроде этого:
https://pypi.org/project/django-phone-verify/
https://github.com/CuriousLearner/django-phone-verify
и это geek for geeks тоже может помочь, но с небольшими обновлениями
https://www.geeksforgeeks.org/customizing-phone-number-authentication-in-django/
Если я правильно понял, вы хотите войти в систему, выполнив следующие действия:
- Пользователь вводит номер телефона
- Отправляется запрос на OTP
- Пользователь вводит OTP
- Если совпадает, аутентификация пользователя
Похоже, вам нужно написать пользовательскую модель пользователя и пользовательский бэкэнд аутентификации. Обратитесь к ним:
Что касается собственно OTP, то мне недавно пришлось его реализовать, поэтому я просто поделюсь тем, как я это сделал. Я сделал целое приложение, так как буду часто его использовать.
otp/models.py
def generate_otp5():
random_generator = SystemRandom()
return random_generator.randint(10000, 99999)
def generate_expiry_10m():
now = datetime.now(tz=timezone.utc)
ten_minutes = timedelta(minutes=10)
return now + ten_minutes
class RegistrationOTP(BasePhoneNumberOTP):
date_created = models.DateTimeField(auto_now_add=True)
date_expired = models.DateTimeField(default=generate_expiry_10m)
date_used = models.DateTimeField(blank=True, null=True)
otp = models.IntegerField(default=generate_otp5)
phone_number = PhoneNumberField()
# from django-phonenumber-field
@property
def is_expired(self):
now = datetime.now(tz=timezone.utc)
return now >= self.date_expired
@property
def is_used(self):
if self.date_used:
return True
return False
Затем я создал службу, потому что чувствовал, что она должна быть. Поместите его внутри вашего models.py
, если хотите.
otp/services.py
class RegistrationOTPService:
def __init__(self, phone_number: phonenumber, user_provided_otp: int):
self.phone_number = phone_number
self.user_provided_otp = user_provided_otp
def resolve(self):
# Used transaction in case multiple requests are made at the same time.
with transaction.atomic():
try:
latest_record = (
RegistrationOTP.objects.select_for_update()
.filter(phone_number=self.phone_number)
.latest('date_created')
)
except exceptions.ObjectDoesNotExist as e:
raise InvalidOTPError from e
if latest_record.is_expired:
raise InvalidOTPError
if latest_record.is_used:
raise InvalidOTPError
if latest_record.otp != self.user_provided_otp:
raise InvalidOTPError
latest_record.date_used = datetime.now(tz=timezone.utc)
latest_record.save()
Последнее, что я поместил в сериализатор validate()
. В вашем случае это должно быть в бэкенде аутентификации.
try:
otp_service = RegistrationOTPService(
phone_number=data.get('phone_number'),
user_provided_otp=data.get('otp'),
)
otp_service.resolve()
except InvalidOTPError as e:
raise serializers.ValidationError({'otp': e}) from e