Skip to content

Commit c83b178

Browse files
committed
fix: [auth] Enforce unique email addresses on user registration
Add unique constraint to User.email field at model, form, and API levels to prevent multiple accounts with the same email address. - Add unique=True to User.email with migration - Add clean_email() to SignUpForm with case-insensitive duplicate check - Add clean_email() to UserUpdateForm (excludes current user) - Change UserInputSerializer.email to EmailField with uniqueness validation - Mark password field as write_only in UserInputSerializer Fixes #45
1 parent 2e33521 commit c83b178

File tree

4 files changed

+52
-4
lines changed

4 files changed

+52
-4
lines changed

api/serializers.py

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,8 @@ class Meta:
2929

3030
class UserInputSerializer(serializers.ModelSerializer):
3131
username = serializers.CharField(max_length=200, required=True)
32-
email = serializers.CharField(max_length=200, required=True)
33-
password = serializers.CharField(max_length=200, required=True)
32+
email = serializers.EmailField(max_length=254, required=True)
33+
password = serializers.CharField(max_length=200, required=True, write_only=True)
3434
company_name = serializers.CharField(max_length=200, required=True)
3535
address = serializers.CharField(max_length=200, required=True)
3636
post_code = serializers.CharField(max_length=200, required=True)
@@ -50,6 +50,13 @@ class Meta:
5050
"vat_number",
5151
]
5252

53+
def validate_email(self, value):
54+
if User.objects.filter(email__iexact=value).exists():
55+
raise serializers.ValidationError(
56+
"A user with this email address already exists."
57+
)
58+
return value
59+
5360

5461
#
5562
# Model: TLS Scan History

authentication/forms.py

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -39,14 +39,24 @@ class Meta(UserCreationForm.Meta):
3939
def __init__(self, *args, **kwargs):
4040
super().__init__(*args, **kwargs)
4141
for visible in self.visible_fields():
42-
visible.field.widget.attrs["class"] = "form-control multi-purpose-gray-200_border"
42+
visible.field.widget.attrs["class"] = (
43+
"form-control multi-purpose-gray-200_border"
44+
)
45+
46+
def clean_email(self):
47+
email = self.cleaned_data.get("email")
48+
if User.objects.filter(email__iexact=email).exists():
49+
raise ValidationError("An account with this email address already exists.")
50+
return email
4351

4452

4553
class LoginForm(AuthenticationForm):
4654
def __init__(self, *args, **kwargs):
4755
super().__init__(*args, **kwargs)
4856
for visible in self.visible_fields():
49-
visible.field.widget.attrs["class"] = "form-control multi-purpose-gray-200_border"
57+
visible.field.widget.attrs["class"] = (
58+
"form-control multi-purpose-gray-200_border"
59+
)
5060

5161

5262
class UserUpdateForm(forms.ModelForm):
@@ -67,6 +77,16 @@ def __init__(self, *args, **kwargs):
6777
for visible in self.visible_fields():
6878
visible.field.widget.attrs["class"] = "form-control"
6979

80+
def clean_email(self):
81+
email = self.cleaned_data.get("email")
82+
if (
83+
User.objects.filter(email__iexact=email)
84+
.exclude(pk=self.instance.pk)
85+
.exists()
86+
):
87+
raise ValidationError("An account with this email address already exists.")
88+
return email
89+
7090

7191
class ChangePasswordForm(PasswordChangeForm):
7292
def __init__(self, *args, **kwargs):
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
# Generated by Django 5.2.11 on 2026-02-23 14:14
2+
3+
from django.db import migrations, models
4+
5+
6+
class Migration(migrations.Migration):
7+
8+
dependencies = [
9+
("authentication", "0005_user_ldih_uuid"),
10+
]
11+
12+
operations = [
13+
migrations.AlterField(
14+
model_name="user",
15+
name="email",
16+
field=models.EmailField(
17+
max_length=254, unique=True, verbose_name="email address"
18+
),
19+
),
20+
]

authentication/models.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33

44

55
class User(AbstractUser):
6+
email = models.EmailField("email address", unique=True)
67
company_name = models.CharField(max_length=200)
78
address = models.CharField(max_length=200)
89
post_code = models.CharField(max_length=200)

0 commit comments

Comments
 (0)