ssoauth:
- Manages users and permissions
- Authenticates users using SAML2 (Shibboleth)
- Authorizes users based on groups:
- Groups are normally received via SAML2
- Groups can also be fetched locally: this is enabled automatically if the
hsh
app is available, skipping multiple software layers for additional security
- Suppresses direct permission assignment (
django.contrib.auth
User
<->Permission
), use groups instead
Minimal SSO Intro:
- SSO: Single Sign On
- SLO: Single Log Out
- SP: Service Provider (your web app)
- IDP: Identity Provider (e.g. Shibboleth)
- Metadata: an XML that describes SP or IDP (or some other relying party)
- SAML/SAML2: yet another XML-based enterprise-grade standard that will make you cry blood
Necessary Stuff
- Binary dependencies:
sudo apt install libxml2-dev libxslt1-dev xmlsec1 libxmlsec1-dev pkg-config
- Python dependencies: see
requirements.txt
orsetup.py
- Add the app into
INSTALLED_APPS
- Include the
ssoauth
urls.py
into the projecturls.py
urlpatterns
:- Without a path/prefix: youre done.
- With a path/prefix:
- Reconsider it. It's highly recommended to include
ssoauth
without a prefix/path to avoid issues with apps likecontrib.admin
andwagtail
that provide their own log in pages. - If you really need to use a path/prefix, make sure to set a setting
LOGIN_URL = urls.reverse_lazy("sso-login")
- Reconsider it. It's highly recommended to include
Development Setup
- Should work with zero additional configuration.
- You can use
localhost:8000/dev/
(might change depending on your url configuration) to access the dev view with all its helper features.
Advanced development setup
If you are not sure whether you need it: you don't need it.
Use this only if you want an actual SSO with SAML2. For extra details see the default settings in ssoauth.app_settings.defaults
. You might also need SSL, try using nginx
for it.
import os, socket
from django import urls
IDP_META_URL = "https://idp-test.it.hs-hannover.de/idp/shibboleth"
IDP_LOGOUT_URL = "https://idp-test.it.hs-hannover.de/idp/profile/Logout"
SP_KEY = "{project_settings}/cert/sp.key"
SP_CERT = "{project_settings}/cert/sp.pem"
SP_HOST = "localhost"
SP_PORT = 8000
SP_SSL = False
SP_FORCE_ENTITY_ID = "dev-id-{0}-{1}".format(socket.gethostname(), os.path.dirname(os.path.dirname(__file__))) # too many localhosts around
Overriding Log In Pages of Other Apps
There are some apps like django.contrib.admin
or wagtail
that will simply ignore LOGIN_URL
and use their own log in page. If this behavior is undesirable and you would prefer using ssoauth
instead, add the following into your urls.py
(before including URLs of that other app):
re_path(r"^(?:\w+/)?login/?$", ssoauth_views.LogInView.as_view(already_authenticated_403=True)),
- Adjust the path if needed
- Optional argument
already_authenticated_403=True
is used to avoid redirect loops (e.g. those caused bydjango.contrib.admin
). You can also usealready_authenticated_redirect="url-name"
.
Regarding Logging Out
After logging out locally, user will be redirected to one of the following (in this order):
-
?next=
GET value (Note: instead ofnext
use usedjango.contrib.auth.REDIRECT_FIELD_NAME
) -
LOGOUT_REDIRECT_URL
(vanilla django setting), recommended values are:-
LOGOUT_REDIRECT_URL = None
for the default behavior -
LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logout-idp")
if you want to instantly initiate IDP log out, possibly triggering SLO LOGOUT_REDIRECT_URL = urls.reverse_lazy("sso-logged-out-locally")
-
- Default: redirect to the view that asks users what to do next. You might want to override template
ssoauth/logged_out_locally.html
Advanced Logging Out and SLO
If unsure, ignore this section.
SLO: SLO/SLS is disabled by default. If you want to try your luck with SLO you should add to your project settings SP_SLS_ENABLED = True
Currently only IDP-initiated SLO is supported by this app. The only supported binding type is HTTP-Redirect due to the limitations of the underlying library used.
For SLO with HTTP-Redirect to work, the SLS page must be included as <iframe>
. Your server and/or browser might restrict such behavior. Start with setting SP_SLS_X_FRAME_OPTIONS
(check ssoauth.app_settings.defaults
).
If you have nginx
serving pages to users, you might need to configure x-frame-options
for the SLS view (Only the SLS view, nowhere else!). Additionally you might need to configure CSP on the web server on the IDP side. Anyways it will most likely be a lot of fun for you.
Groups and Permissions
With ssoauth
the only way to assign permissions is with groups:
- when user logs in,
ssoauth
receives group names from the IDP, or fetches them from thehsh
app if it's present - if your project has local groups (
django.contrib.auth
Group
) with exactly the same names, these groups are assigned to the user - all other groups and permissions are removed from the user for security reasons (better don't workaround this behaviour in your project)
You can predefine some groups in project settings (see ssoauth
default config for details). These predefined groups will be created automatically (when migrating). For example, a superuser group:
PREDEFINED_GROUPS = {
"my_project_superusers": [ssoauth.SUPERUSER_PERM_CODENAME],
}
Production Settings
This example might be incomplete. See ssoauth.app_settings.defaults
for additional info
""" settings.py """
from django import urls
SP_HOST = "foobar.it.hs-hannover.de" # FQDN of your SP
IDP_META_URL = "https://idp.hs-hannover.de/idp/shibboleth" # production
IDP_LOGOUT_URL = "https://idp.it.hs-hannover.de/idp/profile/Logout" # web page for IDP logout (might initiate SLO)
LOGIN_URL = urls.reverse_lazy("sso-login") # django setting
Certs
- if you don't have a cert yet you can create one (and it doesn't need to be signed to use for SAML2 encryption):
openssl req -newkey rsa:16384 -x509 -days 3650 -nodes -out sp.pem -keyout sp.key
- create
cert
directory:- inside of project
settings
directory if it's a package - next to project
settings.py
file if it's a module
- inside of project
-
./cert/sp.key
put the private key here -
./cert/sp.pem
put the certificate here, signing is optional - if you prefer your keys somewhere else, set
SP_KEY
andSP_CERT
settings
Add your SP to an IDP
- Ask for assistance if not sure
- The project should be running with production settings
- Take the metadata of your project:
- Run your project
- The metadata of your SP is generated and shown on a web page, default path is
/saml2/meta
- Use Ctrl+U ("view source") to get the actual XML, otherwise your browser could mess it up
- Shibboleth IDP:
-
SSH
to the IDP and locate the Shibboleth directory, e.g./opt/shibboleth-idp/
- put your meta into a new file in
./metadata/
and give it a nice verbose name - edit
./conf/metadata-providers.xml
- create a new
<MetadataProvider .../>
element, your best guess is to copy an existing line that belongs to some existing Django project -
id
should be unique -
metadataFile
should point on your new metadata - edit
./conf/attribute-filter.xml
- create a new
systemctl restart tomcat8
- make sure the IDP still works, it can be super picky regarding its config files
-