Skip to content

Commit

Permalink
login using otp
Browse files Browse the repository at this point in the history
  • Loading branch information
dori-dev committed Mar 18, 2023
1 parent e7fb754 commit e1a65b5
Show file tree
Hide file tree
Showing 11 changed files with 285 additions and 53 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,5 @@ cert.key
.env
posts.json
users.json
add_fake_data.py
add_fake_data.py
.vscode
40 changes: 20 additions & 20 deletions src/account/authentication.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,28 +49,28 @@ def pipeline(self, pipeline, pipeline_index=0, *args, **kwargs):


class PhoneBackend(ModelBackend):
def authenticate(self, request, username=None, password=None, **kwargs):
try:
otp = models.OTP.objects.get(phone=username)
if not utils.check_otp_expiration(otp):
messages.error(
request,
_('Your verification code has expired.'),
extra_tags='danger',
)
return None
if not password.isdigit() or otp.otp != int(password):
messages.error(
request,
_('You entered the wrong verification code.'),
extra_tags='danger',
)
return None
except models.OTP.DoesNotExist:
def authenticate(
self,
request,
username=None,
password=None,
otp_obj=None,
**kwargs
):
if otp_obj is None:
return None
if not utils.check_otp_expiration(otp_obj):
messages.error(
request,
_('Your verification code has expired.'),
extra_tags='danger',
)
return None
if isinstance(password, str) or otp_obj.otp != int(password):
messages.error(
request,
_('First apply to get the verification code.'),
_('You entered the wrong verification code.'),
extra_tags='danger',
)
return None
return otp.user
return otp_obj.user
72 changes: 71 additions & 1 deletion src/account/forms.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from django.contrib.auth import get_user_model
from jalali_date.fields import JalaliDateField
from jalali_date.widgets import AdminJalaliDateWidget
from .models import Profile
from .models import Profile, OTP


User = get_user_model()
Expand Down Expand Up @@ -360,3 +360,73 @@ def __init__(self, *args, **kwargs):
bio.error_messages = {
'max_length': 'توضیحات ات خیلی زیاد شد!',
}


class OtpForm(forms.ModelForm):
class Meta:
model = OTP
fields = (
'phone',
)
labels = {
'phone': 'شماره تلفن',
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].error_messages = {
'required': 'پر کردن این فیلد ضروری است!',
'invalid': 'این فیلد رو به درستی وارد کن!',
}
phone = self.fields['phone']
phone.widget.attrs[
'class'
] = 'form-control mt-2 mb-2 ltr'
phone.widget.attrs[
'placeholder'
] = 'مثال: 09133352042'
phone.error_messages = {
'max_length': 'طول شماره تلفن ات بیش از حد مجازه.',
}

def clean_phone(self):
phone: str = self.cleaned_data['phone']
if not phone.isdigit():
raise ValidationError('شماره تلفنی که وارد کردی اشتباهه.')
return phone


class OtpLoginForm(forms.ModelForm):
class Meta:
model = OTP
fields = (
'otp',
)
labels = {
'otp': 'کد تایید',
}

def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
for field in self.fields:
self.fields[field].error_messages = {
'required': 'پر کردن این فیلد ضروری است!',
'invalid': 'این فیلد رو به درستی وارد کن!',
}
otp = self.fields['otp']
otp.widget.attrs[
'class'
] = 'form-control mt-2 mb-2 direction-change rtl'
otp.widget.attrs[
'placeholder'
] = 'کد تاییدی که به تلفنت پیامک شد رو وارد کن...'
otp.error_messages = {
'max_length': 'طول کد تایید بیش از حد مجازه.',
}

def clean_otp(self):
otp: str = self.cleaned_data['otp']
if isinstance(otp, str) and not otp.isdigit():
raise ValidationError('کد تایید باید فقط شامل عدد باشه.')
return otp
1 change: 0 additions & 1 deletion src/account/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,6 @@ class OTP(models.Model):
phone = models.CharField(
_("Phone"),
max_length=15,
unique=True,
)
otp = models.PositiveIntegerField(
_("OTP"),
Expand Down
8 changes: 2 additions & 6 deletions src/account/templates/account/login.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
{% block content %}
<h2 class="mt-4 bold-font">ورود به حساب کاربری</h2>
<div class="form mx-md-3">
<a class="mt-4 btn btn-primary google-login bold-font" href="">
<a class="mt-4 btn btn-primary google-login bold-font" href="{% url 'account:otp_auth' %}">
ورود با شماره موبایل
</a>
<a class="mt-4 btn btn-danger google-login bold-font" href="{% url 'social:begin' 'google-oauth2' %}">
Expand All @@ -14,11 +14,7 @@ <h2 class="mt-4 bold-font">ورود به حساب کاربری</h2>
<path d="M386.061 228.496c1.834 9.692 3.143 19.384 3.143 31.956C389.204 370.205 315.599 448 204.8 448c-106.084 0-192-85.915-192-192s85.916-192 192-192c51.864 0 95.083 18.859 128.611 50.292l-52.126 50.03c-14.145-13.621-39.028-29.599-76.485-29.599-65.484 0-118.92 54.221-118.92 121.277 0 67.056 53.436 121.277 118.92 121.277 75.961 0 104.513-54.745 108.965-82.773H204.8v-66.009h181.261zm185.406 6.437V179.2h-56.001v55.733h-55.733v56.001h55.733v55.733h56.001v-55.733H627.2v-56.001h-55.733z"/>
</svg>
</a>
{% if form.errors %}
<p class="mt-4"></p>
{% else %}
<p class="mt-4">نام کاربری و رمز عبور ات رو وارد کن:</p>
{% endif %}
<p class="mt-4"></p>
<form action="{% url 'account:login' %}" method="post">
<div class="d-grid gap-2 col-md-6">
{{ form.as_p }}
Expand Down
30 changes: 30 additions & 0 deletions src/account/templates/account/otp/auth.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{% extends "base.html" %}

{% block title %}ورود{% endblock %}

{% block content %}
<h2 class="mt-4 bold-font">احراز هویت با شماره تلفن</h2>
<div class="form mx-md-3">
<p class="mt-4"></p>
<form action="" method="post">
<div class="d-grid gap-2 col-md-6">
{{ form.as_p }}
</div>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<div class="d-grid gap-2 col-md-6">
<input class="btn btn-success" type="submit" value="ارسال کد تایید">
</div>
</form>
<p class="mt-5 mb-2">
<p class="mb-2">احراز هویت با ایمیل:</p>
<a href="{% if request.GET.next %}{% url 'account:register' %}?next={{ request.GET.next }}{% else %}{% url 'account:register' %}{% endif %}" class="btn btn-primary btn-sm">
ثبت نام با ایمیل
</a>
<a href="{% if request.GET.next %}{% url 'account:login' %}?next={{ request.GET.next }}{% else %}{% url 'account:login' %}{% endif %}" class="btn btn-primary btn-sm">
ورود با ایمیل
</a>
</p>
</div>
<div class="py-5"></div>
{% endblock %}
33 changes: 33 additions & 0 deletions src/account/templates/account/otp/login.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
{% extends "base.html" %}

{% block title %}ورود{% endblock %}

{% block content %}
<h2 class="mt-4 bold-font">ورود به حساب کاربری</h2>
<div class="form mx-md-3">
<a class="mt-4 btn btn-primary google-login bold-font" href="{% url 'account:otp_auth' %}">
ارسال دوباره کد تایید
</a>
<p class="mt-4"></p>
<form action="" method="post">
<div class="d-grid gap-2 col-md-6">
{{ form.as_p }}
</div>
{% csrf_token %}
<input type="hidden" name="next" value="{{ next }}" />
<div class="d-grid gap-2 col-md-6">
<input class="btn btn-success" type="submit" value="ورود">
</div>
</form>
<p class="mt-5 mb-2">
<p class="mb-2">احراز هویت با ایمیل:</p>
<a href="{% if request.GET.next %}{% url 'account:register' %}?next={{ request.GET.next }}{% else %}{% url 'account:register' %}{% endif %}" class="btn btn-primary btn-sm">
ثبت نام با ایمیل
</a>
<a href="{% if request.GET.next %}{% url 'account:login' %}?next={{ request.GET.next }}{% else %}{% url 'account:login' %}{% endif %}" class="btn btn-primary btn-sm">
ورود با ایمیل
</a>
</p>
</div>
<div class="py-5"></div>
{% endblock %}
9 changes: 4 additions & 5 deletions src/account/templates/account/register.html
Original file line number Diff line number Diff line change
Expand Up @@ -5,17 +5,16 @@
{% block content %}
<h2 class="mt-4 bold-font">ساخت حساب کاربری</h2>
<div class="form mx-md-3">
<a class="mt-4 btn btn-primary google-login bold-font" href="{% url 'account:otp_auth' %}">
ثبت نام با شماره موبایل
</a>
<a class="mt-4 btn btn-danger google-login bold-font" href="{% url 'social:begin' 'google-oauth2' %}">
ثبت نام با گوگل
<svg xmlns="https://www.w3.org/2000/svg" viewBox="0 0 640 512">
<path d="M386.061 228.496c1.834 9.692 3.143 19.384 3.143 31.956C389.204 370.205 315.599 448 204.8 448c-106.084 0-192-85.915-192-192s85.916-192 192-192c51.864 0 95.083 18.859 128.611 50.292l-52.126 50.03c-14.145-13.621-39.028-29.599-76.485-29.599-65.484 0-118.92 54.221-118.92 121.277 0 67.056 53.436 121.277 118.92 121.277 75.961 0 104.513-54.745 108.965-82.773H204.8v-66.009h181.261zm185.406 6.437V179.2h-56.001v55.733h-55.733v56.001h55.733v55.733h56.001v-55.733H627.2v-56.001h-55.733z"/>
</svg>
</a>
{% if form.errors %}
<p class="mt-4"></p>
{% else %}
<p class="mt-4">با پر کردن فیلد های زیر ثبت نام کن:</p>
{% endif %}
<p class="mt-4"></p>
<form action="{% url 'account:register' %}" method="post">
<div class="d-grid gap-2 col-md-6">
{{ form.as_p }}
Expand Down
3 changes: 3 additions & 0 deletions src/account/urls.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,4 +25,7 @@
views.ResetPasswordConfirm.as_view(),
name='reset_password_confirm'
),
# OTP
path('otp/auth/', views.OtpAuth.as_view(), name='otp_auth'),
path('otp/login/', views.OtpLogin.as_view(), name='otp_login'),
]
40 changes: 40 additions & 0 deletions src/account/utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
from random import choices
from django.conf import settings
from django.utils.timezone import now
from kavenegar import (
KavenegarAPI,
APIException,
HTTPException,
)
from background_task import background

from . import models


@background(schedule=0)
def send_otp(phone, otp):
try:
api = KavenegarAPI(settings.API_KEY)
params = {
'receptor': phone,
'template': 'verify',
'token': otp,
'type': 'sms',
}
response = api.verify_lookup(params)
except APIException:
response = None
except HTTPException:
response = None
return response


def generate_otp(length=4):
return "".join(choices("123456789", k=length))


def check_otp_expiration(otp: models.OTP):
diff = now() - otp.created
if diff.seconds > 120:
return False
return True
Loading

0 comments on commit e1a65b5

Please sign in to comment.