8

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) ...
/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/facebook.py in auth_complete
        return self.do_auth(access_token, response, *args, **kwargs) ...
/Users/vera/.virtualenvs/app/lib/python2.7/site-packages/social/backends/facebook.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) ...

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.

cansadadeserfeliz
  • 3,033
  • 5
  • 34
  • 50

1 Answers1

13

Problem solved. The issue was about the order in pipeline that has to be the following:

DEFAULT_AUTH_PIPELINE = (
    'social.pipeline.social_auth.social_details',
    'social.pipeline.social_auth.social_uid',
    'social.pipeline.social_auth.auth_allowed',
    'social.pipeline.social_auth.social_user',
    'social.pipeline.user.get_username',
    'social.pipeline.mail.mail_validation',
    'social.pipeline.social_auth.associate_by_email',
    'social.pipeline.user.create_user',
    'social.pipeline.social_auth.associate_user',
    'social.pipeline.social_auth.load_extra_data',
    'social.pipeline.user.user_details'
)

Many thanks for the author of the library who helped to figure this out.

cansadadeserfeliz
  • 3,033
  • 5
  • 34
  • 50
  • don't you have to be logged in for the second account to work? – Val Neekman Apr 01 '14 at 03:58
  • @ValNeekman if I got your question right, with 'associate_by_email' pipeline it automatically looks for registered users and compares their email with the email that provides a social network you enter with – cansadadeserfeliz Apr 01 '14 at 13:09
  • 1
    Thanks - I don't understand why `social.pipeline.social_auth.associate_by_email` is not part of the normal, recommended pipeline. – Matt Jul 16 '14 at 15:51
  • 3
    Probably because it may be a security issue. Let's say you have an account in the site that's using Python Social Auth, you signed up with an email address, but you don't have a Facebook account with that email address. Then I go and create one. Once I have the Facebook account with your email address, if I try to login with that FB account, it will associate the accounts by email and I will have access to your account in the site. – ngonzalvez Jul 30 '14 at 16:29
  • @Kryz When you create a Facebook account you have to confirm your email address, therefore you already have an access to that email account. – cansadadeserfeliz Jul 30 '14 at 17:10
  • 1
    If so, then Facebook is not a good example in this case. But you get what I mean, if any of the social networks you are using does not validate the email, then you have a security issue. – ngonzalvez Jul 30 '14 at 19:09
  • @vero4ka : If user is registered with one email and when the user tries to login with facebook with the same email, it should prompt user already exisits instead of associating the user. How can I do this? – Shashank Hegde Sep 26 '14 at 04:32
  • @ShashankHegde In this case you just remove ``'social.pipeline.social_auth.associate_user'`` from your settings. – cansadadeserfeliz Sep 26 '14 at 15:25
  • The I will hit django error. How can I overcome it. How can I do a custom validation @vero4ka – Shashank Hegde Sep 27 '14 at 16:43
  • @ShashankHegde, may be late for that, but if anyone is reading this, you can simply implement your own method and put it inside the pipeline as in the docs http://python-social-auth.readthedocs.io/en/latest/pipeline.html#authentication-pipeline – Tales Pádua Aug 18 '16 at 15:36