diff --git a/arkindex/users/api.py b/arkindex/users/api.py
index f64a62f200982490e17ffb4d7cc5168f98d68b36..56173b07968522350c13e6ba17d1712d83c8a634 100644
--- a/arkindex/users/api.py
+++ b/arkindex/users/api.py
@@ -163,6 +163,9 @@ class UserCreate(CreateAPIView):
         serializer = self.get_serializer(data=request.data)
         serializer.is_valid(raise_exception=True)
 
+        if User.objects.filter(email=serializer.validated_data['email']).exists():
+            raise ValidationError({'email': ['An user with this email address already exists.']})
+
         user = serializer.save()
         login(self.request, user)
 
diff --git a/arkindex/users/migrations/0016_alter_user_email_user_email_unique.py b/arkindex/users/migrations/0016_alter_user_email_user_email_unique.py
new file mode 100644
index 0000000000000000000000000000000000000000..a92635ff8773b791136df9b55e88d9e4bbe22ff0
--- /dev/null
+++ b/arkindex/users/migrations/0016_alter_user_email_user_email_unique.py
@@ -0,0 +1,34 @@
+# Generated by Django 4.0.2 on 2022-02-28 11:36
+
+from django.contrib.postgres.operations import CreateCollation
+from django.db import migrations, models
+
+
+class Migration(migrations.Migration):
+
+    dependencies = [
+        ('users', '0015_oauthcredentials_provider_choices'),
+    ]
+
+    operations = [
+        migrations.AlterField(
+            model_name='user',
+            name='email',
+            field=models.EmailField(max_length=255, verbose_name='email address'),
+        ),
+        migrations.AddConstraint(
+            model_name='user',
+            constraint=models.UniqueConstraint(fields=('email',), name='email_unique'),
+        ),
+        CreateCollation(
+            'case_insensitive',
+            provider='icu',
+            locale='und-u-ks-level2',
+            deterministic=False,
+        ),
+        migrations.AlterField(
+            model_name='user',
+            name='email',
+            field=models.EmailField(db_collation='case_insensitive', max_length=255, verbose_name='email address'),
+        ),
+    ]
diff --git a/arkindex/users/models.py b/arkindex/users/models.py
index cd4707ebd1805dcf66573088cc277e30c92f3a1c..704a972de6ff3b68147aeef01127e3a35bb1859a 100644
--- a/arkindex/users/models.py
+++ b/arkindex/users/models.py
@@ -60,7 +60,7 @@ class User(AbstractBaseUser):
     email = models.EmailField(
         verbose_name='email address',
         max_length=255,
-        unique=True,
+        db_collation='case_insensitive',
     )
     display_name = models.CharField(max_length=120)
     transkribus_email = models.EmailField(
@@ -87,6 +87,11 @@ class User(AbstractBaseUser):
     USERNAME_FIELD = 'email'
     REQUIRED_FIELDS = ['display_name', ]
 
+    class Meta:
+        constraints = [
+            models.UniqueConstraint(fields=['email'], name='email_unique'),
+        ]
+
     def __str__(self):
         return self.email
 
diff --git a/arkindex/users/tests/test_generic_memberships.py b/arkindex/users/tests/test_generic_memberships.py
index 59eb38910bc06f0a3c03651c952c7b71e9146e05..36413d08978e6ab9974d416994aae93abf6f121a 100644
--- a/arkindex/users/tests/test_generic_memberships.py
+++ b/arkindex/users/tests/test_generic_memberships.py
@@ -578,7 +578,7 @@ class TestMembership(FixtureAPITestCase):
         self.assertEqual(response.status_code, status.HTTP_403_FORBIDDEN)
         self.assertDictEqual(response.json(), {'detail': 'Authentication credentials were not provided.'})
 
-    def test_add_member_requires_verfified(self):
+    def test_add_member_requires_verified(self):
         self.client.force_login(self.unverified)
         with self.assertNumQueries(2):
             response = self.client.post(reverse('api:membership-create'), {
@@ -736,6 +736,32 @@ class TestMembership(FixtureAPITestCase):
             'group_id': None,
         })
 
+    def test_add_member_by_email_uppercase_letters(self):
+        """
+        Adds a new member referenced by its email
+        Asserts the endpoint is case insensitive for the email
+        """
+        user = User.objects.create_user('test@test.de', 'Pa$$w0rd')
+        self.client.force_login(self.user)
+        with self.assertNumQueries(8):
+            response = self.client.post(reverse('api:membership-create'), {
+                'level': 10,
+                # Introducing uppercase letters in the user's email
+                'user_email': 'tEsT@TeSt.DE',
+                'content_type': 'group',
+                'content_id': str(self.group.id)
+            })
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        membership = user.rights.get(group_target=self.group)
+        self.assertDictEqual(response.json(), {
+            'id': str(membership.id),
+            'content_id': str(self.group.id),
+            'content_type': 'group',
+            'level': 10,
+            'user_email': user.email,
+            'group_id': None,
+        })
+
     def test_add_member_superuser(self):
         """
         A superuser is able to add a member to a groups he has no right on
diff --git a/arkindex/users/tests/test_registration.py b/arkindex/users/tests/test_registration.py
index 66fe654480771aaa3adf27e8a20c392ddca920c3..a910d494389ab9ae1220172c9683b0c0fef2af14 100644
--- a/arkindex/users/tests/test_registration.py
+++ b/arkindex/users/tests/test_registration.py
@@ -94,6 +94,19 @@ class TestRegistration(FixtureAPITestCase):
         self.assertDictEqual(response.json(), {'detail': 'Incorrect authentication credentials.'})
         self.assertFalse(auth.get_user(self.client).is_authenticated)
 
+    def test_login_email_uppercase_letters(self):
+        """
+        Asserts the endpoint is case insensitive for the email
+        """
+        response = self.client.post(
+            reverse('api:user-login'),
+            data={'email': 'eMaIL@adDreSs.cOm', 'password': 'P4$5w0Rd'},
+            format='json',
+        )
+        self.assertEqual(response.status_code, status.HTTP_201_CREATED)
+        self.assertTrue(auth.get_user(self.client).is_authenticated)
+        self.assertEqual(auth.get_user(self.client).email, 'email@address.com')
+
     def test_login(self):
         response = self.client.post(
             reverse('api:user-login'),
@@ -263,3 +276,25 @@ class TestRegistration(FixtureAPITestCase):
         )
         self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
         self.assertListEqual(response.json(), ['Registration has been disabled on this instance.'])
+
+    def test_register_existing_user(self):
+        email = 'email@address.com'
+        self.assertTrue(User.objects.filter(email=email).exists())
+        response = self.client.post(
+            reverse('api:user-new'),
+            data={'display_name': 'New user', 'email': email, 'password': 'myVerySecretPassword'},
+            format='json',
+        )
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'email': ['An user with this email address already exists.']})
+
+        # Asserts that the check is insensitive on the email field
+        email = 'eMaIL@adDreSs.cOm'
+        self.assertTrue(User.objects.filter(email=email).exists())
+        response = self.client.post(
+            reverse('api:user-new'),
+            data={'display_name': 'New user', 'email': email, 'password': 'myVerySecretPassword'},
+            format='json',
+        )
+        self.assertEqual(response.status_code, status.HTTP_400_BAD_REQUEST)
+        self.assertDictEqual(response.json(), {'email': ['An user with this email address already exists.']})