I think we know the security benefits and the development benefits of using a delegated authentication protocol like OpenIDC or SAML.
However, actually doing the integration in the application can be difficult at times.
There is a lack of documentation and guidance on the best modules or packages to use for the various frameworks.
In this post I will be looking at the options available for Django when authenticating with an OpenIDC provider – Keycloak.
Using Django as an OpenIDC provider is not what I want – although it is possible with the django openidc provider package
First port of call is an anonymous web search and checking django packages authentication. Django packages seems to put everything in the auth basket where we specifically want to look at OpenIDC clients.
What we are Looking For
- Supports or is compatible with Django 3
- Explicit OpenIDC support (Oauth 2 is not good enough we also need the identity)
- Good Documentation
- A client that is not closely bound to a specific provider – but that is closely bound to the protocol specification.
- Integrated with django admin – ie. logging in from django admin redirects
- A client that can use django’s existing permissions and a nice way to integrate with the provider maybe by way of groups…
OpenIDC Django Packages we are reviewing
- Keycloak OIDC
- Mozilla Django OpenIDC – a package I have used before and I am very happy with. Not the most well known package as it is not even on django package.
- Django AllAuth – The batteries included.
- Django Keycloak Auth – Build to work with Drf (Django Rest Framework)
- Django Boss SSO 2 – Django package specifically for RHSSO and Keycloak.
- Django Social Auth DEPRECATED IN FAVOUR of Python Social Auth
- Python Social Auth – Not django specific but we’ll test it.
- Django Social Registration VERY OLD and NOT COMPATIBLE with DJANGO 3
- Django Admin SSO
If there is no decent documentation the package is discarded from review
There are ways to integrate apache and nginx with OpenIDC – the problem is making use of django’s permission. Will that still be possible.
Method of Testing
We going to create a blank django project, add a model (Beer) and 2 django groups. The first group "customers" can view the beers – as staff on the admin site. The second group "creators" can add, view and change beers on the admin site.
Django Admin SSO
Decent – although documentation is lacking and it defaults to google SSO. You have to check the example settings and change it to keycloak.
Initially it did not get the id token as scope
was set to email
.
I had to edit the code and change the scope to opendic
.
Then it would only progress is email_verified
was True
…so I had to edit that code as well.
Weirdly in the changelog it mentions:
Using OpenID is now deprecated and OpenID support will be removed in a future release
OpenIDC is the future.
This package contains migrations that create the assignments table.
Problems
The problem with this library is that it forces assignments to be created before users are allowed to login. An assignment maps an OpenIDC identity to a local user – that must already exist.
This increases the administrative burden and does not do what openIDC intends – delegating auth (and groups) to the identity provider.
Conclusion
A decent lightweight choice – purely for admin login. Just a bit of admin on setting up a user and an assignment.
Django Social Registration
Requires 'django.contrib.sites'
.
Conclusion
Excluded. Too old and not compatible with Django 3.
Python Social Auth
The repo looks very old in terms of recent updates on github.
However the social-core is where most of the updates happen.
Social auth core is a dependecy of the project:
social-auth-app-django==4.0.0
social-auth-core==4.1.0
The docs are not updated for keycloak settings but they are documented in the code:
AUTHENTICATION_BACKENDS = (
'social_core.backends.keycloak.KeycloakOAuth2',
'django.contrib.auth.backends.ModelBackend',
)
SOCIAL_AUTH_ADMIN_USER_SEARCH_FIELDS = ['username', 'first_name', 'email']
# OpenIDC URL
SOCIAL_AUTH_KEYCLOAK_KEY = 'test-django-oidc'
SOCIAL_AUTH_KEYCLOAK_SECRET = 'a7a41-245e-...'
SOCIAL_AUTH_KEYCLOAK_PUBLIC_KEY = \
'MIIBIjANBxxxdSD'
SOCIAL_AUTH_KEYCLOAK_AUTHORIZATION_URL = \
'https://iam.example.com/auth/realms/cloud-staff/protocol/openid-connect/auth'
SOCIAL_AUTH_KEYCLOAK_ACCESS_TOKEN_URL = \
'https://iam.example.com/auth/realms/cloud-staff/protocol/openid-connect/token'
SOCIAL_AUTH_KEYCLOAK_ID_KEY = 'email'
Problems
I was getting an issues where the client_id
(audience) set up on django was not present in the aud
key of the id_token
JWT. Creating the error: Invalid audience
.
The workaround to solve the client_id not in the audiences is on stackoverflow.
The gist of it is in you client’s mappers
tab – create an audience mapper for your client_id
.
You also have to override the django tempalte for admin login to add the link for keycloak login.
Conclusion
Overall a good quality package. Works well. Now it is just about ensuring the user gets staff status pulled through with the correct permissions from keycloak mapped to django groups.
To give a user the is_staff
and relevant group assignments – the flow with python social auth is to create a pipeline as mentioned in this github issue. Here are the docs for extending the pipeline.
So we can modify the pipelines to match our requirements. If we want users to be automatically created or not etc. The default pipeline can be overriden by this setting:
SOCIAL_AUTH_PIPELINE = (
'social_core.pipeline.social_auth.social_details',
'social_core.pipeline.social_auth.social_uid',
'social_core.pipeline.social_auth.auth_allowed',
'social_core.pipeline.social_auth.social_user',
'social_core.pipeline.social_auth.associate_user',
'social_core.pipeline.social_auth.load_extra_data',
'social_core.pipeline.user.user_details',
'my_module.pipeline.my_custom_pipeline'
)
Pipelines can also be defined per backend…for example: SOCIAL_AUTH_TWITTER_PIPELINE
We just need to be clear on the use of groups and roles in keycloak. Groups define the types of users in an organisation. Composite roles are for managing the application side.
In other words, groups are only an entity on keycloak side – they are not ever sent to client applications. Only the roles they assign are.
Overall I think this is a great choice
Django Boss SSO 2
Leverages other libraries namely drf-oidc-auth
and mozilla-django-oidc
to provide openidc auth with keycloak for django and django rest framework.
The readme has inconsistencies and does not lsit all required packages. There is too many libraries that are too similar used in my opinion.
django-oidc is used but not listed and is not django > 2 compliant.
Yes, it is not at the level required.
I do not recommend this package
Django Keycloak Auth
Specifically for drf – django rest framework.
Each viewset has to be explicity given roles:
keycloak_roles = {
'GET': ['judge'],
}
I don’t like this – I would prefer leveraging of djangos groups and permissions.
I am going to skip reviewing this package.
Django AllAuth
I have an issue where the response was the encoded JWT but the keycloak backend was expecting a json response.
So raised an issue and will look at this again when there is a change…
Turns out this was a problem on the settings on the provider side (keycloak) – the Userinfo signed response algorithm
must be unisgned
. I had it as RS256
.
Mozzila Django OpenIDC
Very straight forward in setup.
We need to do a bit of nigly work to get the login link on the admin page shown below.
Remember the link the initialise the redirect code flow is:
{% translate 'Login with SSO Provider' %}
Then similar to the pipelines in python-social-auth
you need to define a function for extra stuff like linking groups up and making people superusers – first names and last names etc. Although not as eligant as social auth does it…
You end up having to inherit from OIDCAuthenticationBackend
and heavily customise the backend to your liking.
More so that necessary with python-social-auth
.
A decent library choice buy architecturally python-social-auth
is better.
Tutorial on how to add a Login with OpenIDC link to the Admin Page
-
Look for templates in your project folder
templates
directory by adding tosettings.py
in theTEMPLATES
part:[os.path.join(BASE_DIR, '
,'templates'), ] Remember to
import os
-
Now add the template for the login page for that folder – copy the content from
env/lib/../django/contrib/admin/templates/admin/login.html
into<your_project>/templates/admin/custom_login.html
Make the changes to the template (a link to OpenIDC login)
-
Now tell the admin site to use that template – in
urls.py
admin.site.login_template = 'admin/custom_login.html'
Keycloak OIDC
An enhancement on top of mozilla-django-openidc
– adding the ability to link keycloak roles to groups on django.
Still not 100% now yet – as teh is_staff
and is_superuser
will have to be handled in a custom manner.