djangosaml2
djangosaml2 is a Python library that integrates `pysaml2` into Django applications, enabling SAML 2.0 based Single Sign-On (SSO). The current stable version is 1.12.0. It receives regular updates to support new Django and Python versions, often aligning with Django's release cycle.
Warnings
- breaking Version 1.10.0 removed support for Python 3.8 and Django 3.2. Ensure your environment meets the new minimum requirements (Python 3.9+, Django 4.2+).
- gotcha The `xmlsec1` binary is a critical OS-level dependency for `pysaml2` (and thus `djangosaml2`) to sign SAML assertions. Without it, SAML authentication will fail.
- gotcha When using `HTTP-POST` binding for unsolicited responses, older Django versions (pre-3.1) might have `SameSite` cookie issues. This can lead to cookies not being sent.
- gotcha Dynamically modifying `settings.SAML_CONFIG` within views in a multi-tenant environment can lead to race conditions and security vulnerabilities where one user's configuration overwrites another's.
- gotcha When setting `SAML_DJANGO_USER_MAIN_ATTRIBUTE` to map a SAML attribute to a Django user field, ensure the chosen attribute is unique across your user base to prevent authentication failures or unintended user mapping.
Install
-
pip install djangosaml2 -
sudo apt-get install xmlsec1 # Debian/Ubuntu brew install xmlsec1 # macOS
Imports
- Saml2Backend
from djangosaml2.backends import Saml2Backend
- djangosaml2.urls
from django.urls import include, path urlpatterns = [ path('saml2/', include('djangosaml2.urls')) ]
Quickstart
# settings.py
import os
INSTALLED_APPS = [
# ... other Django apps
'djangosaml2',
]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.ModelBackend', # Keep default for admin
'djangosaml2.backends.Saml2Backend',
]
LOGIN_URL = '/saml2/login/'
SESSION_EXPIRE_AT_BROWSER_CLOSE = True # Recommended for SAML
BASEDIR = os.path.dirname(os.path.abspath(__file__))
SAML_CONFIG = {
'xmlsec_binary': '/usr/bin/xmlsec1', # Adjust path if necessary
'entityid': 'http://localhost:8000/saml2/metadata/', # Your SP Entity ID
'service': {
'sp': {
'endpoints': {
'assertion_consumer_service': [
('http://localhost:8000/saml2/acs/', saml2.BINDING_HTTP_POST),
],
'single_logout_service': [
('http://localhost:8000/saml2/ls/', saml2.BINDING_HTTP_REDIRECT),
('http://localhost:8000/saml2/ls/post', saml2.BINDING_HTTP_POST)
],
},
'allow_unsolicited': True, # Set to True for IdP-initiated SSO without prior SP request
'name_id_format': saml2.NAMEID_FORMAT_UNSPECIFIED,
'attribute_mapping': {
'uid': ('username', ),
'mail': ('email', ),
'cn': ('first_name', ),
'sn': ('last_name', ),
},
'metadata': {
'remote': [{
'url': 'https://idp.example.com/saml/metadata/', # Your IdP's metadata URL
}],
},
'key_file': os.path.join(BASEDIR, 'certs/private.key'), # Path to SP private key
'cert_file': os.path.join(BASEDIR, 'certs/public.cert'), # Path to SP public certificate
'encryption_keypairs': [{
'key_file': os.path.join(BASEDIR, 'certs/private.key'),
'cert_file': os.path.join(BASEDIR, 'certs/public.cert'),
}],
},
},
}
# urls.py
from django.contrib import admin
from django.urls import include, path
urlpatterns = [
path('admin/', admin.site.urls),
path('saml2/', include('djangosaml2.urls')),
# Your other app URLs
]