2
votes

I created a django project which has separate User registration form and uses python-social-auth for direct login. Whenever I try to login with social_auth which uses the email id which I already used for creating a account through registration form, a separate row has been inserted instead of using existing one.

If I try to login through social_auth after changing the email Id as unique in model, I am getting an error "email id already exists". If I remove unique from email, then separate entry coming in User table and It is considered as a different account.

Now the problem is, I don't need separate accounts for same email Id(created using registration form, Facebook, Google). And I need to synchronize the accounts created using registration form and social auth.

Settings.py

import os
import datetime
BASE_DIR = os.path.dirname(os.path.dirname(__file__))
PROJECT_ROOT = os.path.abspath(os.path.dirname(BASE_DIR))

SECRET_KEY = '**********'

DEBUG = True

TEMPLATE_DEBUG = True

ALLOWED_HOSTS = ['*']

INSTALLED_APPS = (
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'rest_framework',
    'app.accounts',
    'social.apps.django_app.default',
)

MIDDLEWARE_CLASSES = (
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.auth.middleware.SessionAuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    'social.apps.django_app.middleware.SocialAuthExceptionMiddleware',
)

AUTHENTICATION_BACKENDS = (
    'social.backends.facebook.FacebookOAuth2',
    'social.backends.google.GoogleOAuth2',
    'app.accounts.backends.AuthBackend',
    'django.contrib.auth.backends.ModelBackend',
)
REST_FRAMEWORK = {
    'TEST_REQUEST_DEFAULT_FORMAT': 'json',
    'DEFAULT_RENDERER_CLASSES': (
        'rest_framework.renderers.JSONRenderer',
    ),
    'DEFAULT_PERMISSION_CLASSES': (
        'rest_framework.permissions.IsAuthenticated',
        'rest_framework.permissions.AllowAny',
    ),
    'DEFAULT_AUTHENTICATION_CLASSES': (
        'rest_framework_jwt.authentication.JSONWebTokenAuthentication',
        'rest_framework.authentication.SessionAuthentication',
        'rest_framework.authentication.BasicAuthentication',
    )
}

JWT_AUTH = {
    'JWT_EXPIRATION_DELTA': datetime.timedelta(days=14)
}
JWT_ALLOW_REFRESH = True

TEMPLATE_CONTEXT_PROCESSORS = (
    'django.contrib.auth.context_processors.auth',
    'django.core.context_processors.debug',
    'django.core.context_processors.i18n',
    'django.core.context_processors.media',
    'django.core.context_processors.static',
    'django.core.context_processors.tz',
    'django.core.context_processors.request',
    'django.core.context_processors.csrf',
    'django.contrib.messages.context_processors.messages',
    'social.apps.django_app.context_processors.backends',
    'social.apps.django_app.context_processors.login_redirect',
)


AUTH_USER_MODEL = 'accounts.Account'

ROOT_URLCONF = 'main.urls'

WSGI_APPLICATION = 'main.wsgi.application'


DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.mysql',
        'NAME': 'db',
        'USER': 'root',
        'PASSWORD': 'root',
        'HOST': 'localhost', 
        'PORT': '3306',
    }
}

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'UTC'

USE_I18N = True

USE_L10N = True

USE_TZ = True

STATIC_URL = '/static/'

STATIC_ROOT = os.path.join(PROJECT_ROOT, 'static')

TEMPLATE_DIRS = (
     os.path.join(PROJECT_ROOT, 'templates'),
)

STATICFILES_DIRS = (
    os.path.join(PROJECT_ROOT, 'static'),
)

SOCIAL_AUTH_LOGIN_URL = 'accounts/home/'
SOCIAL_AUTH_LOGIN_REDIRECT_URL = 'accounts/home/'


SOCIAL_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.user.create_user',
    'social.pipeline.social_auth.load_extra_data',
    )

SOCIAL_AUTH_USER_MODEL='accounts.Account'

Traceback

Traceback (most recent call last):
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/core/handlers/base.py", line 149, in get_response
    response = self.process_exception_by_middleware(e, request)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/core/handlers/base.py", line 147, in get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/views/decorators/cache.py", line 57, in _wrapped_view_func
    response = view_func(request, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
    return view_func(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/apps/django_app/utils.py", line 51, in wrapper
    return func(request, backend, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/apps/django_app/views.py", line 28, in complete
    redirect_name=REDIRECT_FIELD_NAME, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/actions.py", line 43, in do_complete
    user = backend.complete(user=user, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/base.py", line 41, in complete
    return self.auth_complete(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/utils.py", line 229, in wrapper
    return func(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/oauth.py", line 387, in auth_complete
    *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/utils.py", line 229, in wrapper
    return func(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/oauth.py", line 396, in do_auth
    return self.strategy.authenticate(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/strategies/django_strategy.py", line 96, in authenticate
    return authenticate(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/contrib/auth/__init__.py", line 74, in authenticate
    user = backend.authenticate(**credentials)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/base.py", line 82, in authenticate
    return self.pipeline(pipeline, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/base.py", line 85, in pipeline
    out = self.run_pipeline(pipeline, pipeline_index, *args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/backends/base.py", line 112, in run_pipeline
    result = func(*args, **out) or {}
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/pipeline/user.py", line 70, in create_user
    'user': strategy.create_user(**fields)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/strategies/base.py", line 53, in create_user
    return self.storage.user.create_user(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/social/storage/django_orm.py", line 60, in create_user
    return cls.user_model().objects.create_user(*args, **kwargs)
  File "/media/mugesh/Extra/STUDIES/Internship/Projects/wedifly/app/accounts/models.py", line 43, in create_user
    return self._create_user(email, first_name, last_name, password, False, False, **extra_fields)
  File "/media/mugesh/Extra/STUDIES/Internship/Projects/wedifly/app/accounts/models.py", line 26, in _create_user
    user.save(using=self._db)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/contrib/auth/base_user.py", line 74, in save
    super(AbstractBaseUser, self).save(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/base.py", line 700, in save
    force_update=force_update, update_fields=update_fields)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/base.py", line 728, in save_base
    updated = self._save_table(raw, cls, force_insert, force_update, using, update_fields)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/base.py", line 812, in _save_table
    result = self._do_insert(cls._base_manager, using, fields, update_pk, raw)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/base.py", line 851, in _do_insert
    using=using, raw=raw)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/manager.py", line 122, in manager_method
    return getattr(self.get_queryset(), name)(*args, **kwargs)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/query.py", line 1039, in _insert
    return query.get_compiler(using=using).execute_sql(return_id)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/models/sql/compiler.py", line 1060, in execute_sql
    cursor.execute(sql, params)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/backends/utils.py", line 79, in execute
    return super(CursorDebugWrapper, self).execute(sql, params)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/utils.py", line 95, in __exit__
    six.reraise(dj_exc_type, dj_exc_value, traceback)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/backends/utils.py", line 64, in execute
    return self.cursor.execute(sql, params)
  File "/home/mugesh/.local/lib/python2.7/site-packages/django/db/backends/sqlite3/base.py", line 323, in execute
    return Database.Cursor.execute(self, query, params)
IntegrityError: UNIQUE constraint failed: accounts.email

Any Suggestions or solutions are welcome. Thank you,

3
What do you have as your AUTH_USER_MODEL setting?Moses Koledoye
I specified the custom user model. like AUTH_USER_MODEL = 'accounts.Account'Mugesh
Can you show the settings for your social-auth app?Moses Koledoye
I included the settings in questionMugesh
Please, next time do not include your secret key and other sensitive data.Moses Koledoye

3 Answers

0
votes

When you are using social auth it has separate table 'social_auth_usersocialauth' it gets filled when you call the login for the first time, social auth work in such a way that if it doesn't have an entry in 'social_auth_usersocialauth' it creates a new entry in it and it assumes that you are login it for the first time and creates a new user, that's why user is getting created again.

Use social auth first and try to identify whether you are registering for the first time if so take him to the registration page else it will work as anormal login.

0
votes

I think adding associate_user to SOCIAL_AUTH_PIPELINE was designed to fix this:

SOCIAL_AUTH_PIPELINE = (
    ...
    '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',
    ...
)

It should associate a social_user with an already existing user created via your form registration as opposed to creating an entirely new user.

0
votes

I faced the same issue. The key here is to not only add to the pipeline the below line. 'social.pipeline.social_auth.associate_user', # <------

You need to all others that are by default enabled. Which would be the ones below (see link - https://python-social-auth.readthedocs.io/en/latest/pipeline.html)

(
    # Get the information we can about the user and return it in a simple
    # format to create the user instance later. In some cases the details are
    # already part of the auth response from the provider, but sometimes this
    # could hit a provider API.
    'social_core.pipeline.social_auth.social_details',

    # Get the social uid from whichever service we're authing thru. The uid is
    # the unique identifier of the given user in the provider.
    'social_core.pipeline.social_auth.social_uid',

    # Verifies that the current auth process is valid within the current
    # project, this is where emails and domains whitelists are applied (if
    # defined).
    'social_core.pipeline.social_auth.auth_allowed',

    # Checks if the current social-account is already associated in the site.
    'social_core.pipeline.social_auth.social_user',

    # Make up a username for this person, appends a random string at the end if
    # there's any collision.
    'social_core.pipeline.user.get_username',

    # Send a validation email to the user to verify its email address.
    # Disabled by default.
    # 'social_core.pipeline.mail.mail_validation',

    # Associates the current social details with another user account with
    # a similar email address. Disabled by default.
     'social_core.pipeline.social_auth.associate_by_email',

    # Create a user account if we haven't found one yet.
    'social_core.pipeline.user.create_user',

    # Create the record that associates the social account with the user.
    'social_core.pipeline.social_auth.associate_user',

    # Populate the extra_data field in the social record with the values
    # specified by settings (and the default ones like access_token, etc).
    'social_core.pipeline.social_auth.load_extra_data',

    # Update the user record with any changed info from the auth service.
    'social_core.pipeline.user.user_details',
)```