Customizing behaviour
The default behaviour of mozilla-django-oidc-db (which is an extension on top of mozilla-django-oidc, the upstream library) provides OpenID Connect configuration to authenticate Django (admin) users. The default claim mapping and settings gravitate towards staff users.
However, the generic mechanism of using a database-backed configuration for one or more OpenID Connect identity providers can be used much more broadly, and it doesn’t even have to manage Django user instances at all.
We offer flexibility through a generic configuration mechanism.
Added in version 0.17.0: The generic configuration mechanism was added.
Changed in version 0.24.0: The models were refactored to no longer be solo-models.
Changed in version 0.26.0: The type definitions and annotations were overhauled for correctness and completeness.
Models
We provide a model OIDCClient.
This makes some of the upstream library settings dynamic rather than having to specify
them as Django settings. The OIDCClient has a
JSON field options that can be used to specify any configuration that is specific to
an OIDC Identity Provider. The structure of this field is specified with a JSON schema
and thanks to django-jsonform the admin displays a nice form for it instead of a
plain text field.
If you want to bring your own configuration, you should create a new
OIDCClient
and a corresponding plugin that should inherit from either either
AnonymousUserOIDCPlugin or
AbstractUserOIDCPlugin.
The plugin should be registered with the same identifier as the corresponding
OIDCClient.
Plugin
We use a plugin architecture to encapsulate any behaviour specific to a particular OIDC provider or Identity Provider.
A custom plugin can be registered as follows:
from mozilla_django_oidc_db.plugins import AbstractUserOIDCPlugin
from mozilla_django_oidc_db.registry import register
@register("oidc-custom-identifier")
class OIDCCustomPlugin(AbstractUserOIDCPlugin):
...
The abstract base class BaseOIDCPlugin specifies
the functionality that all plugins must implement, while the
AnonymousUserOIDCPlugin and
AbstractUserOIDCPlugin specify additional methods
that should be implemented depending on whether Django users should be created when a
user logs in with OIDC or not.
At start-up, a signal will run after the migrations to create an OIDCClient record
(if it doesn’t already exist) for every plugin registered, keeping code and database
configuration in sync.
The OIDCCallbackView and the
OIDCAuthenticationBackend both rely on the
plugins. This should make it possible to implement all custom behaviour in the plugins
without having to override the callback view and the backend.
OIDC flow initialization
Typically when a user needs to authenticate, they click a button or link to do so. This
navigation is tied to a particular URL path, for example /auth/oidc-custom/.
We provide OIDCAuthenticationRequestInitView to
start an OIDC authentication flow. This view class is parametrized with the identifier
of the config model, so that the specific configuration can be retrieved and settings
such as the identity provider endpoint to redirect the user to can be obtained.
This view is not necessarily meant to be exposed directly via a URL pattern, but rather specific views are to be created from it, e.g.:
from mozilla_django_oidc_db.views import OIDCAuthenticationRequestInitView
digid_init = OIDCAuthenticationRequestInitView.as_view(identifier="digid-oidc")
redirect_response = digid_init(request) # Redirect to some keycloak instance, for example.
An example of a pre-configured view to use as the “default” could be as follows:
from mozilla_django_oidc_db.constants import OIDC_ADMIN_CONFIG_IDENTIFIER
class OIDCDefaultAuthenticationRequestView(OIDCAuthenticationRequestInitView):
identifier = OIDC_ADMIN_CONFIG_IDENTIFIER
allow_next_from_query = True
And then by configuring OIDC_AUTHENTICATE_CLASS to point to this class would result in this view being
used as default.
Recommended override hooks
Todo
Should this maybe be moved to the plugin?
mozilla_django_oidc_db.views.OIDCAuthenticationRequestInitView.check_idp_availability()You can implement your own behaviour here to determine if the identity provider is available, before the user is redirected to the authentication endpoint.
Authentication backend(s)
The authentication backend OIDCAuthenticationBackend
retrieves the OIDCClient whose identifier has been stored on the request session by the initialization view.
If you want real Django users to be managed, you don’t need to do anything.
However, if you want to do more advanced stuff (like only storing certain claims in the
django session), you can subclass our backend to modify the behaviour. Don’t forget
to add this backend to the AUTHENTICATION_BACKENDS setting.
Callback flow
OIDCCallbackView takes care of preparing the
request for the authentication backend(s). It stores the OIDCClient in the request._oidcdb_config
Based on the identifier of the OIDCClient, OIDCCallbackView calls the
method handle_callback of the corresponding plugin. This method should then call the appropriate callback view to use.
For example, this could be:
def handle_callback(self, request: HttpRequest) -> HttpResponse:
return default_callback_view(request)
Where:
from mozilla_django_oidc_db.views import OIDCAuthenticationCallbackView
default_callback_view = OIDCAuthenticationCallbackView.as_view()
You can implement your own callback view. We recommend using OIDCAuthenticationCallbackView
as a base.
From the get method in the callback view OIDCAuthenticationCallbackView
the backend authenticate method will be called.