omab / python-social-auth

Social auth made simple
http://psa.matiasaguirre.net
BSD 3-Clause "New" or "Revised" License
2.83k stars 1.09k forks source link

IntegrityError at /social/complete/facebook/ duplicate key value violates unique constraint "userprofile_user_email_key" #208

Closed cansadadeserfeliz closed 9 years ago

cansadadeserfeliz commented 10 years ago

Hello,

I'm using both python-social-auth and email registration in my project. For the user model I use a subclass of AbstractBaseUser:

class User(AbstractBaseUser):
    USERNAME_FIELD = 'email'
AUTH_USER_MODEL = 'userprofile.User'

But when a user that is registered with his email (demo@demo.com) and password tries to login with his Facebook account that is associated with the same email address I get the following error:

IntegrityError at /social/complete/facebook/
duplicate key value violates unique constraint "userprofile_user_email_key"
DETAIL:  Key (email)=(demo@demo.com) already exists.

/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/core/handlers/base.py in get_response
                    response = wrapped_callback(request, *callback_args, **callback_kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/views/decorators/csrf.py in wrapped_view
        return view_func(*args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/apps/django_app/utils.py in wrapper
            return func(request, backend, *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/apps/django_app/views.py in complete
                       redirect_name=REDIRECT_FIELD_NAME, *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/actions.py in do_complete
                                 *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/strategies/base.py in complete
        return self.backend.auth_complete(*args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/facebook.py in auth_complete
        return self.do_auth(access_token, response, *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/facebook.py in do_auth
        return self.strategy.authenticate(*args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/strategies/django_strategy.py in authenticate
        return authenticate(*args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/contrib/auth/__init__.py in authenticate
            user = backend.authenticate(**credentials) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in authenticate
        return self.pipeline(pipeline, *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in pipeline
        out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in run_pipeline
            result = func(*args, **out) or {} ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/pipeline/user.py in user_details
            strategy.storage.user.changed(user) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/storage/django_orm.py in changed
        user.save() ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in save
                       force_update=force_update, update_fields=update_fields) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in save_base
            updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in _save_table
                                      forced_update) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in _do_update
        return filtered._update(values) > 0 ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/query.py in _update
        return query.get_compiler(self.db).execute_sql(None) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
        cursor = super(SQLUpdateCompiler, self).execute_sql(result_type) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
        cursor.execute(sql, params) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/debug_toolbar/panels/sql/tracking.py in execute
        return self._record(self.cursor.execute, sql, params) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/debug_toolbar/panels/sql/tracking.py in _record
            return method(sql, params) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
            return super(CursorDebugWrapper, self).execute(sql, params) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
                return self.cursor.execute(sql, params) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/utils.py in __exit__
                six.reraise(dj_exc_type, dj_exc_value, traceback) ...
▶ Local vars
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
                return self.cursor.execute(sql, params) ...
▶ Local vars
omab commented 10 years ago

The user is logged in? Which is your pipeline setting value? Do you have 'social.pipeline.social_auth.associate_by_email' in it?

cansadadeserfeliz commented 10 years ago

Thank you for your answer! The user is registered but not logged in and tries to log in with his Facebook account. When somebody registers with his Facebook account and then logs in everything works fine. The problem appears when he registers with his email (I'm using django-registration), so we have an instance of User (but not UserSocialAuth), and then we tries to login with his Facebook account associated the same email address. Adding 'social.pipeline.social_auth.associate_by_email' gives me the same error :(

omab commented 10 years ago

Then 'social.pipeline.social_auth.associate_by_email' should do the work, that pipeline will check if there's a user with the same email address and associate the social account to that user instead of creating one, check here for the correct position for this function https://github.com/omab/python-social-auth/blob/master/social/pipeline/__init__.py#L8

cansadadeserfeliz commented 10 years ago

Thank you so much. It worked. You were right, the issue was about the order in the pipeline.

Me alegró el día :) Muchísimas gracias por responder y perdón por las molestias.

AdaLovelance commented 10 years ago

worked for me too, thanks a lot!

cansadadeserfeliz commented 10 years ago

Hello Matías, I got the same error and have no idea how to sole it. It appears when I try to do the following thing:

There are two registered users on our website: a user A with an email a@example.com and a user B with an email b@example.com. Both are instances of User model and have no social network associated with their accounts.

A user A logs in and tries to associate his Google account with an account on our website. His Google account has an email b@example.com.

So at this moment it fails with an error:

duplicate key value violates unique constraint "userprofile_user_email_key"
DETAIL:  Key (email)=(b@example.com) already exists.

Log:

/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/core/handlers/base.py in get_response
                    response = wrapped_callback(request, *callback_args, **callback_kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/views/decorators/csrf.py in wrapped_view
        return view_func(*args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/apps/django_app/utils.py in wrapper
            return func(request, backend, *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/apps/django_app/views.py in complete
                       redirect_name=REDIRECT_FIELD_NAME, *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/actions.py in do_complete
                                 *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/strategies/base.py in complete
        return self.backend.auth_complete(*args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/oauth.py in auth_complete
                            *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/oauth.py in do_auth
        return self.strategy.authenticate(*args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/strategies/django_strategy.py in authenticate
        return authenticate(*args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/contrib/auth/__init__.py in authenticate
            user = backend.authenticate(**credentials) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in authenticate
        return self.pipeline(pipeline, *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in pipeline
        out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/base.py in run_pipeline
            result = func(*args, **out) or {} ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/pipeline/user.py in user_details
            strategy.storage.user.changed(user) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/storage/django_orm.py in changed
        user.save() ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in save
                       force_update=force_update, update_fields=update_fields) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in save_base
            updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in _save_table
                                      forced_update) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/base.py in _do_update
        return filtered._update(values) > 0 ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/query.py in _update
        return query.get_compiler(self.db).execute_sql(None) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
        cursor = super(SQLUpdateCompiler, self).execute_sql(result_type) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/models/sql/compiler.py in execute_sql
        cursor.execute(sql, params) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/debug_toolbar/panels/sql/tracking.py in execute
        return self._record(self.cursor.execute, sql, params) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/debug_toolbar/panels/sql/tracking.py in _record
            return method(sql, params) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
            return super(CursorDebugWrapper, self).execute(sql, params) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
                return self.cursor.execute(sql, params) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/utils.py in __exit__
                six.reraise(dj_exc_type, dj_exc_value, traceback) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/django/db/backends/util.py in execute
                return self.cursor.execute(sql, params) ...

Thank you in advance!

camilonova commented 10 years ago

any updates on this?

omab commented 9 years ago

@ctrl-alt-delete, the problem is that PSA doesn't know how to merge the accounts and that's something for your project to control, not PSA. In this case you should add a pipeline function that determines that this is going to cause a collision and proceed accordingly.

cansadadeserfeliz commented 9 years ago

@omab Thank you for your answer! I figured out that in order to fix it I need to add email to PROTECTED_USER_FIELDS of a strategy. In this case it won't replace a@example.comof A user with b@example.com and violate a unique constraint for email field.

What is the proper way to set this attribute?

https://github.com/omab/python-social-auth/blob/master/social/pipeline/user.py#L76

omab commented 9 years ago

That was the proper way.

camilonova commented 9 years ago

@omab can we have it documented somewhere?

mykhul commented 9 years ago

I am new with using these packages, and I am experiencing this issue. I have followed all of the advice here, and am still having the same problem.