Python and Django

In this example we will discuss the code required to configure a Django-based web-application to make use of Raven OAuth2 for sign in. We assume you've covered the first steps with Raven and have created some OAuth2 client credentials.

The python-social-auth package has direct support for OAuth2 and integration with a number of Python frameworks including Django. Most of the configuration we do in this guide could be ported to other Python web frameworks such as Flask or CherryPy.

Following the "golden rules"

When using Raven OAuth2, it is a "golden rule" that we verify at least the hd claim on sign in. Python-social-auth supports this by means of custom "pipelines" where small portions of code are run to process a sign in.

In the Django project module, we can add a small function to check the hd claim against a list of whitelisted domains:

# In project/pipelines.py

from social_core.exceptions import AuthForbidden


def enforce_hosted_domain(*, backend, response, **kwargs):
    """
    A pipeline for use with django-social-auth's Google OAuth2 support which
    enforces that the signed in user is a member of one of the
    WHITELISTED_HOSTED_DOMAINS.

    """
    hosted_domain = response.get('hd')
    whitelist = backend.setting('WHITELISTED_HOSTED_DOMAINS', None)
    if whitelist is not None and hosted_domain not in whitelist:
        raise AuthForbidden(backend)

Configuring Django

Once we have our OAuth2 credentials, we can configure Django to make use of python-social-auth for sign in. See the corresponding section of the python-social-auth documentation for more information.

# In project/settings.py

INSTALLED_APPS = [
    # ... other apps ...
    'social_django'
    # ... other apps ...
]

AUTHENTICATION_BACKENDS = [
    'social_core.backends.google.GoogleOAuth2',
    'django.contrib.auth.backends.ModelBackend',
]

SOCIAL_AUTH_URL_NAMESPACE = 'social'
SOCIAL_AUTH_JSONFIELD_ENABLED = True

SOCIAL_AUTH_GOOGLE_OAUTH2_KEY = 'your-client-id'
SOCIAL_AUTH_GOOGLE_OAUTH2_SECRET = 'your-client-secret'
SOCIAL_AUTH_GOOGLE_OAUTH2_AUTH_EXTRA_ARGUMENTS = {'hd': 'cam.ac.uk'}
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_DOMAINS = ['cam.ac.uk']
SOCIAL_AUTH_GOOGLE_OAUTH2_WHITELISTED_HOSTED_DOMAINS = ['cam.ac.uk']
SOCIAL_AUTH_GOOGLE_OAUTH2_SCOPE = ['email', 'profile']

SOCIAL_AUTH_PIPELINE = (
    'social_core.pipeline.social_auth.social_details',
    'social_core.pipeline.social_auth.social_uid',
    'social_core.pipeline.social_auth.auth_allowed',
    # use our custom pipeline to check the "hd" claim
    'project.pipelines.enforce_hosted_domain',
    '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',
)

LOGIN_URL = '/accounts/login/google-oauth2/'
LOGIN_REDIRECT_URL = '/'
LOGOUT_REDIRECT_URL = '/'
# In project/urls.py

urlpatterns = [
    # ...
    path('accounts/', include('django.contrib.auth.urls')),
    path('accounts/', include('social_django.urls', namespace='social')),
    # ...
]

Requiring sign in

Now we have configured Django, we can use the login_required decorator on views to enforce that a user has signed in to the site. A Django user object will be created for each new user on the site and we can make use of the Django groups and permissions settings to control what that user can do.


Last update: August 23, 2022