In this article we’ll cover lessons learned implementing a Keycloak authentication solution into Django Rest Framework (DRF) using the mozilla-django-oidc library. Note that this article assumes some familiarity with Django.
Requirements
These can be summarized as follows:
Replace the current authentication solution with a Keycloak-based solution so that users can be authenticated and enable single sign-on between applications using different authentication providers.
The solution should cover both Django (session authentication) and DRF (token authentication).
It should be able to handle a dedicated Keycloak client and expandable to allow roles-based authorisation.
OpenID connect preferred
Any libraries used should be currently supported and widely used
Design Phase
There are many Keycloak-related Django libraries available. A small PoC was developed to look at different options and decide on the most appropriate solution. Some of the options looked at included:
Each of these caused issues with the particular implementation/configuration we were trying to introduce and would have required additional customisation but may be suitable in other cases. We ultimately settled on mozilla-django-oidc - a well supported, lightweight option which could be implemented in a simple Django application more or less according to the instructions in the documentation.
The PoC was a simple app developed according to the standard Django tutorial and then updated to add the oidc library following their instructions. After some trial and error, it was running successfully, but required a couple of additional tweaks:
In settings.py:
As indicated, for openID, RS256 had to be set explicitly as the signing algorithm (OIDC_RP_SIGN_ALGO).
The JWKS endpoint (OIDC_OP_JWKS_ENDPOINT) has to be set up ()
The sign key (OIDC_RP_IDP_SIGN_KEY) has to be present, but be left empty so it uses the JWKS endpoint.
With these modifications it was possible to set up dedicated OIDC client realm (see OIDC Clients) in keycloak and demonstrate that basic authentication was possible. Furthermore, using the groups mapper for the realm, I was able to add a groups claim, add a user group and then add a user to the group. When the user logons on via a session, or via a JWT (JSON Web Token) the claims are returned allowing authorisation groups to be set up in keycloak - see here - and interrogated by the Django app.
Development/Implementation
Implementation to production is being phased with part one - session authentication (based on the middleware) - described here. During development some additional customisations became necessary. Fortunately, the additional configuration instructions provided enough hints to cover these use cases:
UC1: Use Username rather than Email
mozilla-django-oidc defaults to setting up Django users using the email address as the user name whereas to enable communication with an associated system, the preferred_username from keycloak was required. Fortunately this is set up by default in Keycloak as a claim. The claim can interrogated by overriding the OIDCAuthenticationBackend class in mozilla_django_oidc.auth and referring to this in AUTHENTICATION_BACKENDS as below:
In a new file auth.py, add:
In settings.py, overide the new library you have just added in AUTHENTICATION_BACKENDS :
UC2: Allow the option to fully log out from Keycloak.
The logout view in mozilla-django-oidc defaults to ending the Django session. We also wanted to be able to optionally end the keycloak session by use of an environment parameter in our docker configuration. This can be achieved by setting, and referring to, the OIDC_OP_LOGOUT_URL_METHOD parameter and overriding the logout view as follows:
Create a file to contain the logout view:
Set the OIDC_OP_LOGOUT_ENDPOINT in settings.py so this can be used as an environment variable:
Also refer to your new LogoutView in your URLs file, with a line like: