Search code examples
djangodjango-allauthdj-rest-auth

django allauth - ACCOUNT_UNIQUE_EMAIL doesn't prevent duplicate emails on signup


I'm making a basic website where users sign up with an email, a username, and a psssword.

I'm using dj_rest_auth, which wraps allauth to provide a REST API interface. Here's an example registration call:

POST: http://127.0.0.1:8000/api/auth/registration/

Body:
{
  "username": "new_user3",
  "password1": "testtest@123",
  "password2": "testtest@123",
  "email": "[email protected]"
}

These are the flags I've set in settings.py:

ACCOUNT_EMAIL_VERIFICATION = 'none'
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_USERNAME_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True

Yet, when I send the registration request twice, and only change the username (not email), it goes through just fine: Screenshot showing two different users with the same email.

What's the easiest way to have this fail if the email is a duplicate? Ideally I'm looking for something the library itself provides, and not a custom class.


Solution

  • The documentation of the allauth settings [allauth-docs] says:

    ACCOUNT_UNIQUE_EMAIL (default: True)

    Enforce uniqueness of email addresses. On the database level, this implies that only one user account can have an email address marked as verified. Forms prevent a user from registering with or adding an additional email address if that email address is in use by another account.

    The source code [GitHub] also seems to indicate this:

    class Migration(migrations.Migration):
        # …
        
        operations = (
            [
                migrations.AlterUniqueTogether(
                    name="emailaddress",
                    unique_together={("user", "email")},
                ),
                migrations.AddConstraint(
                    model_name="emailaddress",
                    constraint=models.UniqueConstraint(
                        condition=models.Q(("verified", True)),
                        fields=["email"],
                        name="unique_verified_email",
                    ),
                ),
            ]
            if getattr(settings, "ACCOUNT_UNIQUE_EMAIL", True)
            else []
        )
    

    So the conditon=Q(verified=True) part says that the email is required to be unique, only for rows with verified=True.

    Note furthermore that it does not even check if the email is unique in a caseinsensitive way, so [email protected] and [email protected] are seen as different email addresses.