Categories
django IAM Integration Keycloak OAuth

Best Django Openidc Package

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

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.

signed-userinfo-endpoint-keycloak

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

  1. Look for templates in your project folder templates directory by adding to settings.py in the TEMPLATES part:

    [os.path.join(BASE_DIR, ' ,'templates'), ]

    Remember to import os

  2. 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)

  3. 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.