Search code examples
pythondjangofacebookauthenticationpython-social-auth

2021 python-social-auth "facebook Authentication process canceled" on production, but works on localhost


In my Django=2.2 app I want to implement facebook login via python-all-auth. But I am faceing a problem with authentication on production.

Here some details:

When I use facebook login on localhost, everything is working as expected. When clicking on fb login icon, I am being redirected to fb page, I give permittion to the application to my data and I am being redirected back to the main page with User being logged in. User is created in the both tables 'Social Account Users' and my custom 'User' table.

BUT then on production, adjusting settings in facebook app accordingly, facebook authentication is being canceled. Same, after clicking on fb login icon I am being redirected to facebook page, pop up window is showing up and askig to give permittion to application by clicking "continue as a user" I am being redirected to main page with a message 'Authentication process canceled'

with debug=True, the error is:

Environment:


Request Method: GET
Request URL: http://www.sellspot.pl/oauth/complete/facebook/?granted_scopes=email%2Cpublic_profile&denied_scopes&code=SOME_CODE

Django Version: 2.2.17
Python Version: 3.7.4
Installed Applications:
['django.contrib.admin',
 'django.contrib.auth',
 'django.contrib.contenttypes',
 'django.contrib.sessions',
 'django.contrib.messages',
 'django.contrib.staticfiles',
 'crispy_forms',
 'django.contrib.humanize',
 'bootstrap_pagination',
 'storages',
 'social_django',
 'auctions',
 'users',
 'data',
 'contact_us',
 'report']
Installed Middleware:
('whitenoise.middleware.WhiteNoiseMiddleware',
 'django.middleware.security.SecurityMiddleware',
 'django.contrib.sessions.middleware.SessionMiddleware',
 'django.middleware.locale.LocaleMiddleware',
 'django.middleware.common.CommonMiddleware',
 'django.middleware.csrf.CsrfViewMiddleware',
 'django.contrib.auth.middleware.AuthenticationMiddleware',
 'django.contrib.messages.middleware.MessageMiddleware',
 'django.middleware.clickjacking.XFrameOptionsMiddleware',
 'social_django.middleware.SocialAuthExceptionMiddleware')



Traceback:

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/utils.py" in wrapper
  248.             return func(*args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/backends/facebook.py" in auth_complete
  101.             'code': self.data['code']

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/backends/base.py" in request
  237.         response.raise_for_status()

File "/app/.heroku/python/lib/python3.7/site-packages/requests/models.py" in raise_for_status
  940.             raise HTTPError(http_error_msg, response=self)


During handling of the above exception (400 Client Error: Bad Request for url: 
https://graph.facebook.com/v2.9/oauth/access_token?client_id=APP_ID&redirect_uri=http%3A%2F%2Fwww.sellspot.pl%2Foauth%2Fcomplete%2Ffacebook%2F&client_secret=CLIENT_SECRET&code=SOME_CODE), 
another exception occurred:


File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/exception.py" in inner
  34.             response = get_response(request)

File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  115.                 response = self.process_exception_by_middleware(e, request)

File "/app/.heroku/python/lib/python3.7/site-packages/django/core/handlers/base.py" in _get_response
  113.                 response = wrapped_callback(request, *callback_args, **callback_kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/django/views/decorators/cache.py" in _wrapped_view_func
  44.         response = view_func(request, *args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/django/views/decorators/csrf.py" in wrapped_view
  54.         return view_func(*args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_django/utils.py" in wrapper
  49.             return func(request, backend, *args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_django/views.py" in complete
  33.                        *args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/actions.py" in do_complete
  45.         user = backend.complete(user=user, *args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/backends/base.py" in complete
  40.         return self.auth_complete(*args, **kwargs)

File "/app/.heroku/python/lib/python3.7/site-packages/social_core/utils.py" in wrapper
  251.                 raise AuthCanceled(args[0], response=err.response)

Exception Type: AuthCanceled at /oauth/complete/facebook/
Exception Value: Authentication process canceled

settings.py

import os
import django_heroku

SHORTCODE_MAX = 15
SHORTCODE_MIN = 6

ENV_DOMAIN_NAME = "https://sellspot.pl"

AUCTION_PER_PAGE = 20

BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))

SECRET_KEY = os.environ.get("SELLSPOT_PROD_SECRET_KEY")

DEBUG = os.environ.get("SELLSPOT_PROD_DEBUG") == 'True'

ALLOWED_HOSTS = ['sellspot.pl', 'www.sellspot.pl', 'sellspot.herokuapp.com']

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',

    # django app
    'crispy_forms',
    'django.contrib.humanize',
    'bootstrap_pagination',
    'storages',

    # social authentication
    'social_django',

    # custom apps
    'auctions',
    'users',
    'data',
    'contact_us',
    'report',
]

MIDDLEWARE = [
    'django.middleware.security.SecurityMiddleware',
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.locale.LocaleMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    # social django exception
    'social_django.middleware.SocialAuthExceptionMiddleware',

]

ROOT_URLCONF = 'sellspot.urls'

TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        'DIRS': [os.path.join(BASE_DIR, 'templates'), ],
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
                # social authentication
                'social_django.context_processors.backends',
                'social_django.context_processors.login_redirect',
            ],
        },
    },
]

WSGI_APPLICATION = 'sellspot.wsgi.application'

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

AUTH_PASSWORD_VALIDATORS = [
    {
        'NAME': 'django.contrib.auth.password_validation.MinimumLengthValidator',
    },
    {
        'NAME': 'django.contrib.auth.password_validation.NumericPasswordValidator',
    },
]

AUTH_USER_MODEL = 'users.User'

DEFAULT_AUTHENTICATION_BACKEND = 'django.contrib.auth.backends.ModelBackend'
AUTHENTICATION_BACKENDS = (
    'users.authentication_backends.EmailBackend',
    'social_core.backends.facebook.FacebookOAuth2',
    DEFAULT_AUTHENTICATION_BACKEND,

)

LANGUAGE_CODE = 'en-us'

TIME_ZONE = 'Europe/Warsaw'

USE_I18N = True

USE_L10N = True

USE_TZ = True

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, "static_cdn")
STATICFILES_DIRS = [
    os.path.join(BASE_DIR, "static"),
]

MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, "media_cdn")

VALID_IMAGE_EXTENSIONS = [
    "jpg",
    "jpeg",
    "png",
    "gif",
    "webp",
]

AWS_ACCESS_KEY_ID = os.environ.get("SELLSPOT_PROD_AWS_ACCESS_KEY_ID")
AWS_SECRET_ACCESS_KEY = os.environ.get("SELLSPOT_PROD_AWS_SECRET_ACCESS_KEY")
AWS_STORAGE_BUCKET_NAME = os.environ.get("SELLSPOT_PROD_AWS_STORAGE_BUCKET_NAME")
AWS_S3_REGION_NAME = 'eu-central-1'
AWS_S3_SIGNATURE_VERSION = 's3v4'

AWS_S3_FILE_OVERWRITE = False
AWS_DEFAULT_ACL = None

STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
DEFAULT_FILE_STORAGE = 'sellspot.storage_backends.MediaStorage'
AWS_LOCATION = 'static'

AWS_S3_OBJECT_PARAMETERS = {
    'CacheControl': 'max-age=43200',
}

CRISPY_TEMPLATE_PACK = 'bootstrap4'

LOGIN_REDIRECT_URL = '/'

django_heroku.settings(locals())

EMAIL_BACKEND = 'django.core.mail.backends.smtp.EmailBackend'
EMAIL_HOST = 'smtp.gmail.com'
EMAIL_PORT = 587
EMAIL_USE_TLS = True
EMAIL_HOST_USER = os.environ.get("SELLSPOT_PROD_EMAIL_HOST_USER")
EMAIL_HOST_PASSWORD = os.environ.get("SELLSPOT_PROD_EMAIL_HOST_PASSWORD")

LOGGING = {
    'version': 1,
    'disable_existing_loggers': False,
    'formatters': {
        'verbose': {
            'format': "[%(asctime)s] %(levelname)s [%(name)s:%(lineno)s] %(message)s",
            'datefmt': "%d/%b/%Y %H:%M:%S"
        },
        'simple': {
            'format': '%(levelname)s %(message)s'
        },
    },
    'handlers': {
        'file': {
            'level': 'DEBUG',
            'class': 'logging.FileHandler',
            'filename': 'mysite.log',
            'formatter': 'verbose'
        },
    },
    'loggers': {
        'django': {
            'handlers': ['file'],
            'propagate': True,
            'level': 'DEBUG',
        },
        'MYAPP': {
            'handlers': ['file'],
            'level': 'DEBUG',
        },
    }
}


# Social login authentication
SOCIAL_AUTH_POSTGRES_JSONFIELD = True
SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ['last_name', 'first_name', 'email']
SOCIAL_AUTH_FACEBOOK_KEY = os.environ.get("SELLSPOT_PROD_SOCIAL_AUTH_FACEBOOK_KEY")
SOCIAL_AUTH_FACEBOOK_SECRET = os.environ.get("SELLSPOT_PROD_SOCIAL_AUTH_FACEBOOK_SECRET")
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
SOCIAL_AUTH_LOGIN_REDIRECT_URL = '/konto/'
SOCIAL_AUTH_LOGIN_URL = '/login'
LOGIN_ERROR_URL = "/login"

SOCIAL_AUTH_FACEBOOK_PROFILE_EXTRA_PARAMS = {
  'locale': 'pl_PL',
  'fields': 'id, name, email'
}
SOCIAL_AUTH_FACEBOOK_API_VERSION = '2.9'
LOGOUT_URL = 'logout'
LOGOUT_REDIRECT_URL = 'login'
SOCIAL_AUTH_RAISE_EXCEPTIONS = False

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    'social_core.pipeline.social_auth.social_user',
    'social_core.pipeline.user.get_username',
    'social_core.pipeline.user.create_user',
    'social_core.pipeline.social_auth.associate_user',
    'social_core.pipeline.social_auth.load_extra_data',
    'social_core.pipeline.user.user_details',
)

SOCIAL_AUTH_URL_NAMESPACE = 'social'

SOCIAL_AUTH_FACEBOOK_EXTRA_DATA = [
    ('name', 'name'),
    ('email', 'email'),
    ('picture', 'picture'),
]

url.py

urlpatterns = [
    path('admin/', admin.site.urls),
    path(r"", HomeView.as_view(), name="home"),
    url('oauth/', include('social_django.urls', namespace='social')),
    ****
    ****
    and more,
]
if settings.DEBUG:
    urlpatterns += static(settings.STATIC_URL, document_root=settings.STATIC_ROOT)
    urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

login.html

    <a href="{% url 'social:begin' 'facebook' %}?next={{ request.path }}">
        <i class="fab fa-facebook-square fa-3x"></i>
    </a>

model.py

from django.contrib.auth.models import AbstractUser
from django.db import models

class User(AbstractUser):
    email = models.EmailField(_('email address'), unique=True)
    first_name = models.CharField(_('first name'), max_length=30, blank=False)
    last_name = models.CharField(_('last name'), max_length=150, blank=False)

What am I missing ?!

Thank you

UPDATE

I've found out that if I am trying to login myself from heroku subdomain adress which is https://sellspot.herokuapp.com (this is equal to https://sellspot.pl) I am being loggin in properly with facebook login. As @CBroe mentioned and this is probably causing the issue, the redirect_uri parameter is pointing to http instead of https. When using heroku subdomain this redirect_uri is pointing to https and then loggin is working as expected


Solution

  • adding this in settings.py has solved the issue

    SOCIAL_AUTH_REDIRECT_IS_HTTPS = True