pennersr / django-allauth

Integrated set of Django applications addressing authentication, registration, account management as well as 3rd party (social) account authentication.
https://allauth.org
MIT License
9.55k stars 3.03k forks source link

Integrity error during social login: duplicate key violates unique constraint on user table #3266

Closed snopoke closed 1 year ago

snopoke commented 1 year ago

With the project setup described below, if a user that already has an account attempts to login with a social account (that has the same email) they are redirected to the 'social signup' with their email address pre-populated.

Upon submitting the form they get a 500 error response.

ACCOUNT_AUTHENTICATION_METHOD = "email"
ACCOUNT_EMAIL_REQUIRED = True
ACCOUNT_UNIQUE_EMAIL = True
ACCOUNT_EMAIL_VERIFICATION = "mandatory"

Error traceback:

Traceback (most recent call last):
File "site-packages/django/core/handlers/exception.py", line 55, in inner
response = get_response(request)
File "site-packages/django/core/handlers/base.py", line 197, in _get_response
response = wrapped_callback(request, *callback_args, **callback_kwargs)
File "site-packages/django/views/generic/base.py", line 103, in view
return self.dispatch(request, *args, **kwargs)
File "site-packages/allauth/socialaccount/views.py", line 42, in dispatch
return super(SignupView, self).dispatch(request, *args, **kwargs)
File "site-packages/allauth/account/views.py", line 77, in dispatch
response = super(RedirectAuthenticatedUserMixin, self).dispatch(
File "site-packages/allauth/account/views.py", line 207, in dispatch
return super(CloseableSignupMixin, self).dispatch(request, *args, **kwargs)
File "site-packages/django/views/generic/base.py", line 142, in dispatch
return handler(request, *args, **kwargs)
File "site-packages/allauth/account/views.py", line 105, in post
response = self.form_valid(form)
File "site-packages/allauth/socialaccount/views.py", line 56, in form_valid
form.save(self.request)
File "site-packages/allauth/socialaccount/forms.py", line 28, in save
user = adapter.save_user(request, self.sociallogin, form=self)
File "site-packages/allauth/socialaccount/adapter.py", line 81, in save_user
get_account_adapter().save_user(request, u, form)
File "site-packages/allauth/account/adapter.py", line 250, in save_user
user.save()
File "site-packages/django/contrib/auth/base_user.py", line 68, in save
super().save(*args, **kwargs)
File "site-packages/django/db/models/base.py", line 812, in save
self.save_base(
File "site-packages/django/db/models/base.py", line 863, in save_base
updated = self._save_table(
File "site-packages/django/db/models/base.py", line 1006, in _save_table
results = self._do_insert(
File "site-packages/django/db/models/base.py", line 1047, in _do_insert
return manager._insert(
File "site-packages/django/db/models/manager.py", line 85, in manager_method
return getattr(self.get_queryset(), name)(*args, **kwargs)
File "site-packages/django/db/models/query.py", line 1791, in _insert
return query.get_compiler(using=using).execute_sql(returning_fields)
File "site-packages/django/db/models/sql/compiler.py", line 1660, in execute_sql
cursor.execute(sql, params)
File "site-packages/django/db/backends/utils.py", line 67, in execute
return self._execute_with_wrappers(
File "site-packages/django/db/backends/utils.py", line 80, in _execute_with_wrappers
return executor(sql, params, many, context)
File "site-packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
File "site-packages/django/db/utils.py", line 91, in __exit__
raise dj_exc_value.with_traceback(traceback) from exc_value
File "site-packages/django/db/backends/utils.py", line 89, in _execute
return self.cursor.execute(sql, params)
django.db.utils.IntegrityError: duplicate key value violates unique constraint "users_customuser_username_key"

Diagnosis

allauth.account.forms.BaseSignupForm.clean_email only raises a ValidationError if self.prevent_enumeration is False (which is not the case with the above settings):

https://github.com/pennersr/django-allauth/blob/master/allauth/account/forms.py#L362-L364

Instead it sets self.account_already_exists = True on the form. This is then handled by the save method on the subclassed forms e.g. https://github.com/pennersr/django-allauth/blob/master/allauth/account/forms.py#L437-L442

The problem is that the allauth.socialaccount.forms.SignupForm does not handle this condition and attempts to save the user which triggers the IntegrityError: https://github.com/pennersr/django-allauth/blob/master/allauth/socialaccount/forms.py#L26-L30

Proposed solutions

or

pennersr commented 1 year ago

django.db.utils.IntegrityError: duplicate key value violates unique constraint "users_customuser_username_key"

The above suggests that email is not the root cause of the issue, but username is. How is username populated in your setup?

snopoke commented 1 year ago

django.db.utils.IntegrityError: duplicate key value violates unique constraint "users_customuser_username_key"

The above suggests that email is not the root cause of the issue, but username is. How is username populated in your setup?

Ah, I see we also override populate_username and set the username to the email address. I'll have to disable that and test again to see if this is still an issue.

pennersr commented 1 year ago

Closing -- I am fairly sure that allauth works correctly on this point and that the populate_username is the issue.

Piecio commented 1 year ago

django.db.utils.IntegrityError: duplicate key value violates unique constraint "users_customuser_username_key"

The above suggests that email is not the root cause of the issue, but username is. How is username populated in your setup?

Ah, I see we also override populate_username and set the username to the email address. I'll have to disable that and test again to see if this is still an issue.

Have you solved the issue? I have the same problem but with duplicated user email: django.db.utils.IntegrityError: duplicate key value violates unique constraint "users_user_email_243f6e77_uniq"

My settings (django-allauth 0.54.0): ACCOUNT_AUTHENTICATION_METHOD = "email" ACCOUNT_EMAIL_REQUIRED = True ACCOUNT_EMAIL_VERIFICATION = "mandatory" ACCOUNT_UNIQUE_EMAIL = True ACCOUNT_PREVENT_ENUMERATION = True

When ACCOUNT_PREVENT_ENUMERATION = False everything works as it should.

This happens only when: 1) User sign up with email & password 2) Next, the same user try to sign up with social login

There is no error, when: 1) User sign up with social login 2) The same user try to sign up with email & password