Skip to content

uw-it-aca/uw-django-saml2

Repository files navigation

uw-django-saml2

Build Status Coverage Status PyPi Version Python versions

This app allows a Django project to be a SAML SP without running shibd and apache mod_shib. The key dependency is the python3-saml package. For easier development and testing, the app also supports configuring a mocked SAML-authenticated session.

Four steps to SAML

Configure the LOGIN_URL and required settings

Add required settings, and configure the saml_login URL in uw_saml.urls.py to be the LOGIN_URL in your project/settings.py:

INSTALLED_APPS = ['uw_saml',]

AUTHENTICATION_BACKENDS = ['django.contrib.auth.backends.RemoteUserBackend',]

MIDDLEWARE = ['django.contrib.auth.middleware.PersistentRemoteUserMiddleware',]

from django.urls import reverse_lazy
LOGIN_URL = reverse_lazy('saml_login')

Add your SP config

This app uses Django settings to configure the SP and IdP. Copy the UW_SAML setting dict in conf/settings.py to your project/settings.py.

You will need to update these settings to the correct values for your SP:

sp.entityId sp.x509cert sp.assertionConsumerService.url sp.singleLogoutService.url

Also, be sure to set strict: True for production usage!

Include the uw_saml URLs

Add the uw_saml URLs to your project/urls.py:

path('saml/', include('uw_saml.urls')),

Register your app as an SP

Register your app with the UW Service Provider Registry

Mocking a SAML login

To mock a SAML-authenticated session in your app, add the setting MOCK_SAML_ATTRIBUTES to project/settings.py. When this setting is present, mocking takes precedence over the live SAML workflow. The value of the setting is a SAML attribute set, representing the desired attributes for the mocked user:

MOCK_SAML_ATTRIBUTES = {
    'uwnetid': ['javerage'],
    'affiliations': ['student', 'member'],
    'eppn': ['[email protected]'],
    'scopedAffiliations': ['[email protected]', '[email protected]'],
    'isMemberOf': ['u_test_group', 'u_test_another_group'],
}

Mocking a SAML login with a Django Login

To mock a SAML-authenticated session in your app change the AUTHENTICATION_BACKENDS to include the mock backend.

AUTHENTICATION_BACKENDS = ('django.contrib.auth.backends.ModelBackend',)

Also add the following:

DJANGO_LOGIN_MOCK_SAML = {
    'NAME_ID': 'mock-nameid',
    'SESSION_INDEX': 'mock-session',
    'SAML_USERS': [
        {
            "username": <some username>,
            "password": <some password>,
            "email": <some email>,
            "MOCK_ATTRIBUTES" : {
                'uwnetid': [<some username>],
                'affiliations': ['student', 'member'],
                'eppn': ['[email protected]'],
                'scopedAffiliations': ['[email protected]', '[email protected]'],
                'isMemberOf': [
                    'u_test_group', 'u_test_another_group'
                ],
            }
        }
    ]
}

replace <some username>, <some password>, <some email> with the values you want. You will use these to login when in mock mode. You can add more mock users by appending to the SAML_USERS array in UW_SAML_MOCK.

Some Recommendations

Safetly setting login credentials

Replace:

<some username> with os.getenv('MOCK_USERNAME', None), <some password> with os.getenv('MOCK_PASSWORD', None), <some email> with os.getenv('MOCK_EMAIL', None)

Create a .env file and add this:

MOCK_USERNAME=<desired username>
MOCK_PASSWORD=<desired password>
MOCK_EMAIL=<desired emailid>

If you are using docker:

Add the following to your docker-compose inside the main app:

environment:
    MOCK_USERNAME: $MOCK_USERNAME
    MOCK_PASSWORD: $MOCK_PASSWORD
    MOCK_EMAIL: $MOCK_EMAIL

If you are using pipenv it should just work.

If you are using virtual env checkout https://pybit.es/persistent-environment-variables.html

Maintaining login session across server restarts

Make sure that:

SECRET_KEY = os.getenv('DJANGO_SECRET_KEY', get_random_secret_key())

Create a .env file and add this:

DJANGO_SECRET_KEY = <50 character random value>

If you are using docker:

Add the following to your docker-compose inside the main app:

environment:
    DJANGO_SECRET_KEY: $DJANGO_SECRET_KEY

If you are using pipenv it should just work.

If you are using virtual env checkout https://pybit.es/persistent-environment-variables.html