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.
Added in version 0.24.0: The models were refactored to no longer be solo-models.
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 implement the interface specified by either the
AnonymousUserOIDCPluginProtocol or the AbstractUserOIDCPluginProtocol.
The plugin should be registered with the same identifier as the corresponding OIDCClient.
You can inherit from the BaseOIDCPlugin to inherit some
of the base plugin behaviour.
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.registry import register
@register("oidc-custom-identifier")
class OIDCCustomPlugin(AbstractUserOIDCPluginProtocol):
...
The protocol OIDCBasePluginProtocol specifies the functionality that all plugins
should implement, while the AnonymousUserOIDCPluginProtocol and
AbstractUserOIDCPluginProtocol 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 (if it doesn’t already exist) for every plugin
registered.
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.