Single Sign-On (SAML)
Important
The SAML registration method is disabled by default.
In order to enable this feature you have to follow the SAML setup instructions below and then activate it via global setting or from the admin interface.
SAML is supported by generating an additional temporary token right after users authenticates via SSO, the user is then redirected to the captive page with 3 querystring parameters:
username
token
(REST auth token)login_method=saml
The captive page must recognize these two parameters, validate the token
and automatically perform the submit action of the captive portal login
form: username
should obviously used for the username field, while
token
should be used for the password field.
The third parameter, login_method=saml
, is needed because it allows
the captive page to remember that the user logged in via SAML. This
information will be used later on when performing the SAML logout.
The internal REST API of openwisp-radius will recognize the token and authorize the user.
This kind of implementation allows to support SAML with any captive portal which already supports the RADIUS protocol because it's totally transparent for it, that is, the captive portal doesn't even know the user is signing-in with a SSO.
Note
If you're building a public wifi service, we suggest to take a look at OpenWISP WiFi Login Pages, which is built to work with openwisp-radius.
Setup
Install required system dependencies:
sudo apt install xmlsec1
Install the SAML dependencies in the python environment used by OpenWISP:
# by default, in instances deployed
# via ansible-openwisp2, the python env
# is in /opt/openwisp2/env/
source /opt/openwisp2/env/bin/activate
pip install openwisp-radius[saml]
Note
If you're unsure about what "Django settings" are, you can refer to How to Edit Django Settings in OpenWISP for guidance.
Ensure your settings.py
looks like the following:
INSTALLED_APPS = [
# ... other apps ..
# apps needed for SAML login
"rest_framework.authtoken",
"django.contrib.sites",
"allauth",
"allauth.account",
"djangosaml2",
]
SITE_ID = 1
# Update AUTHENTICATION_BACKENDS
AUTHENTICATION_BACKENDS = (
"openwisp_users.backends.UsersAuthenticationBackend",
"openwisp_radius.saml.backends.OpenwispRadiusSaml2Backend", # <- add for SAML login
)
# Update MIDDLEWARE
MIDDLEWARE = [
# ... other middlewares ...
"djangosaml2.middleware.SamlSessionMiddleware",
]
Ensure your main urls.py
contains the
openwisp_users.accounts.urls
:
urlpatterns = [
# .. other urls ...
path("accounts/", include("openwisp_users.accounts.urls")),
]
Configure the djangosaml2 settings
Refer to the djangosaml2 documentation to find out how to configure required settings for SAML.
Logout
When logging out a user which logged in via SAML, the captive page should
also call the SAML logout URL: /radius/saml2/logout/
.
The OpenWISP WiFi Login Pages app supports this with minimal configuration, refer to the OpenWISP WiFi Login Pages section.
Settings
FAQs
Preventing change in username of a registered user
The djangosaml2
library requires configuring
SAML_DJANGO_USER_MAIN_ATTRIBUTE
setting which serves as the primary
lookup value for User objects. Whenever a user logs in or registers
through the SAML method, a database query is made to check whether such a
user already exists. This lookup is done using the value of
SAML_DJANGO_USER_MAIN_ATTRIBUTE
setting. If a match is found, the
details of the user are updated with the information received from SAML
Identity Provider.
If a user (who has registered on OpenWISP with a different method from
SAML) logs into OpenWISP with SAML, then the default behavior of OpenWISP
RADIUS prevents updating username of this user. Because, this operation
could render the user's old credentials useless. If you want to update the
username in such scenarios with details received from Identity Provider,
set OPENWISP_RADIUS_SAML_UPDATES_PRE_EXISTING_USERNAME to True
.