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.

Captive page button example

After successfully configuring SAML settings for your Identity Provider, you will need an HTML button similar to the one in the following example.

This example needs the slug of the organization to assign the new user to the right organization:

<a href="https://openwisp2.mywifiproject.com/radius/saml2/login/?RelayState=https://captivepage.mywifiproject.com%3Forg%3Ddefault"
   class="button">
   Log in with SSO
</a>

Substitute openwisp2.mywifiproject.com, https://captivepage.mywifiproject.com and default with the hostname of your openwisp-radius instance, your captive page and the organization slug respectively.

Alternatively, you can take a look at OpenWISP WiFi Login Pages, which provides buttons for Single Sign-On (SAML) by default.

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

See SAML related 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.