Email verification in django python
I try to make email verification in django, everyting works correctly, but if user creates account with someone other's email and if user will not confirm email, owner of this email will not be able to register because account is alredy registered and is_active is False. Here my views.py with reg form '''
from django.shortcuts import render, redirect
from django.http.request import HttpRequest
from django.http import JsonResponse
from .forms import UserRegisterForm, UserLoginForm
from .models import User
from .tokens import accound_activate_token
from django.contrib.sites.shortcuts import get_current_site
from django.template.loader import render_to_string
from django.utils.http import urlsafe_base64_decode, urlsafe_base64_encode
from django.utils.encoding import force_bytes, force_str
from django.core.mail import send_mail
# from django.contrib.auth.forms import AuthenticationForm
from django.contrib.auth import authenticate, login, logout
def send_email_validation(request: HttpRequest, user: User, form: UserRegisterForm):
current_site = get_current_site(request)
mail_subject = "Activate your account"
message = render_to_string(
"users/emails/email_activation.html",
{
"user": user,
"domain": current_site.domain,
"uid": urlsafe_base64_encode(force_bytes(user.pk)),
'token': accound_activate_token.make_token(user)
}
)
from_email = "xxx"
to_email = form.cleaned_data["email"]
send_mail(
mail_subject, message, from_email,
[to_email]
)
def register_page(request: HttpRequest):
if request.method == "GET":
reg_form = UserRegisterForm()
if request.method == "POST":
reg_form = UserRegisterForm(request.POST)
# create user and redirect to login
if reg_form.is_valid():
user: User = reg_form.save(commit=False)
user.is_active = False
user.save()
# sending email for validation
send_email_validation(request, user, reg_form)
return redirect("users_login")
context = {
"form": reg_form
}
return render(request, "users/register.html", context)
def activate_email(reqest: HttpRequest, uidb64, token):
try:
uid = force_str(urlsafe_base64_decode(uidb64))
user = User.objects.get(pk=uid)
except (TypeError, ValueError, OverflowError, User.DoesNotExist):
user = None
if user and accound_activate_token.check_token(user, token):
user.is_active = True
user.save()
return redirect("main_page")
'''
tokens.py '''
from django.contrib.auth.tokens import PasswordResetTokenGenerator
class AccoundActivationTokenGenerator(PasswordResetTokenGenerator):
def _make_hash_value(self, user, timestamp):
return f"{user.pk}{timestamp}{user.is_active}"
accound_activate_token = AccoundActivationTokenGenerator()
'''
I think I need to save user only after email verified successfully, but I don't know if it will be correct
Based on the described scenario, one effective approach is to add a created_at
field to the User model
to track account creation time. Upon user registration, a verification email should be sent, and the account should remain inactive until the email is verified. If the user does not verify their email within a defined time window (e.g., 1 hour), the unverified account can be automatically deleted. This allows the same email address to be used again for registration after the expiration period, ensuring legitimate users are not blocked by inactive, unverified accounts.
I think I got it, I just need to check user while registration and delete it if account is not active
class UserRegisterForm(UserCreationForm):
email = forms.EmailField(
label=_("Email"),
max_length=100,
required=True,
)
first_name = forms.CharField(
label=_("First Name"),
max_length=100,
required=True,
)
last_name = forms.CharField(
label=_("Last Name"),
max_length=100,
required=True,
)
def __init__(self, *args, **kwargs):
super(UserRegisterForm, self).__init__(*args, **kwargs)
# clear hepl texts
for field in self.fields.values():
field.help_text = None
def clean_email(self):
email = self.cleaned_data['email']
try:
# get and delete user with not active account
user = User.objects.get(email=email)
if not user.is_active:
user.delete()
return email
# user exists and acount is active
raise forms.ValidationError(_("Email already exists"))
except User.DoesNotExist:
return email
class Meta:
model = User
fields = [
"email",
"first_name",
"last_name",
"password1",
"password2"
]