Skip to content

Latest commit

 

History

History
185 lines (146 loc) · 8.59 KB

File metadata and controls

185 lines (146 loc) · 8.59 KB

ksi-oidc-django

About this package

This package adds OpenID Connect authentication functionality for Django projects.

Features:

  • Autoconfiguration via the /.well-known/openid-configuration endpoint
  • Support for refresh tokens
    • The session duration is limited by the expiration time of the refresh token
    • The authentication middleware that the user always has a valid access token and refreshes it when it has expired
  • SSO sessions:
    • Checking for an active SSO session using a redirection to the authentication endpoint with prompt=none
    • Endpoint for ending the SSO session and logging out from Django
    • (Planned) Back-channel logout
  • Support for disabling the OidcAuthBackend in the settings file without requiring other changes to the views
  • Syncing of user groups and staff/superuser status based on the realm_access.roles claim in the access token
  • Custom @ksi_oidc_login_required and @ksi_oidc_check_sso view decorators
  • Automatic client configuration using the OpenID Connect Dynamic Client Registration protocol

Notice

The source code of this library incorporates modified source code from the mozilla-django-oidc library. The fragments based on mozilla-django-oidc are appropriately marked in comments in the source code. mozilla-django-oidc is licenced under the Mozilla Public License 2.0, available here.

Configuration

In the appropriate Django setting files:

  1. Add django.contrib.auth (if not yet added) and ksi_oidc_django to INSTALLED_APPS.

    It is required for adding the models for this library when running manage.py migrate and provides extra configuration checks.

    INSTALLED_APPS = [
        # ...
        'django.contrib.auth',
        # ...
        'ksi_oidc_django',
        # ...
    ]
  2. Add the OidcAuthMiddleware to MIDDLEWARE:

    It must be placed directly after Django's AuthenticationMiddleware, because it is required for the session expiry and refresh logic to work. If any other middleware was added in between AuthenticationMiddleware and OidcAuthMiddleware, the request.user might be a user whose session has expired while processing that middleware.

    See the Middleware Ordering section in the Django docs for a standard order of other middleware.

    MIDDLEWARE = [
        # ...
        'django.contrib.sessions.middleware.SessionMiddleware',
        # ...
        'django.contrib.auth.middleware.AuthenticationMiddleware',
        'ksi_oidc_django.middleware.OidcAuthMiddleware',
        # ...
    ]
  3. Add ksi_oidc_django-specific settings:

    OIDC_APP_BASE_URL = 'https://yourapp.com/'
    
    # Set user's Django groups to the roles from the access token claims.
    # Roles from the `realm_access.roles` claim will be saved as groups with the names `oidc.realm.{group_name}`.
    # Roles from the `resource_access.{client_id}.roles` claim will be saved as `oidc.client.{group_name}`.
    # If the user is in any other group with a name starting with `oidc.`, it will be removed.
    # See https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_token_role_mappings for more details.
    OIDC_SYNC_ROLES_AS_GROUPS = True
    
    # Sets or unsets the User.is_staff and User.is_superuser fields
    # if the user's `realm_access.roles` or `resource_access.${client_id}.roles` claims contain the specified role.
    # The settings are tuples in the form `('realm', role_name)` or `('client', role_name)`.
    # Set to None to disable this feature.
    # See https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_token_role_mappings for more details.
    OIDC_STAFF_ROLE = ('client', 'my-app-staff')
    OIDC_SUPERUSER_ROLE = ('client', 'my-app-superuser')
    
    OIDC_SSO_CHECK_COOLDOWN_SECONDS = 300
  4. Add OidcAuthBackend to AUTHENTICATION_BACKENDS:

    AUTHENTICATION_BACKENDS = (
        # This is the standard Django backend, you can remove it if you only use
        # OpenID Connect for authentication.
        'django.contrib.auth.backends.ModelBackend',
        'ksi_oidc_django.backends.OidcAuthBackend',
    )

    You can disable the OidcAuthBackend without removing the app and middleware. The middleware will detect that the backend is not enabled and raise MiddlewareNotUsed.

  5. Use the manage.py oidc_set_issuer and manage.py oidc_init_dynamic/manage.py oidc_init_static commands to configure the OpenID Connect client.

Views configuration

Add these entries in your urls.py:

urlpatterns = [
    # ...
    
    path('login/', OidcLoginView.as_view(), name='login'),
    
    # Register the endpoints `/oidc/callback/` and `/oidc/logout/`:
    path('oidc/', include('ksi_oidc_django.urls')),
]

You may change the paths. Make sure to set the setting LOGIN_URL to the path of the login page. ksi-oidc-django also uses the standard LOGOUT_REDIRECT_URL setting, set it to the path you want the user to be redirected to after logging out.

In the settings of your OIDC provider you will need to add the /oidc/callback/ URL as a valid redirect URL and the LOGOUT_REDIRECT_URL URL as a valid post logout redirect URL.

OidcLoginView redirects the user to the OIDC provider's login page if the OidcAuthBackend is enabled. If it's not, it uses the view specified in OidcLoginView.fallback_view to render the login page. It uses DjangoLoginView by default. You can use a different view for this by specifying the fallback_view when calling .as_view():

urlpatterns = [
    # ...
    path('login/', OidcLoginView.as_view(fallback_view=MyFallbackLoginView.as_view()), name='login'),
    # ...
]

Custom decorators

ksi-oidc-django provides these new view decorators:

  • @ksi_oidc_login_required performs the same check as Django's @login_required, but if the user is not logged in, it redirects the user directly to the OIDC login page (if the OidcAuthBackend is enabled).

    If you were to use @login_required instead, accessing a protected view would redirect the user twice, first to the LOGIN_URL, which would then redirect the user to the OIDC login page.

  • @ksi_oidc_check_sso is used for views that do not require authentication. When an unauthenticated user tries to access a view decorated with @ksi_oidc_check_sso, they will be redirected to the OIDC authentication endpoint with prompt=none, to check if the user already has an active SSO session.

    The OIDC_SSO_CHECK_COOLDOWN_SECONDS setting controls the minimum time between such checks.

Settings reference

OIDC_APP_BASE_URL

The publicly accessible base URL of your application, including the protocol and port, for example https://yourapp.com/.

OIDC_SYNC_ROLES_AS_GROUPS

Set user's Django groups to the roles from the access token claims:

  • Roles from the realm_access.roles claim will be saved as groups with the names oidc.realm.{group_name}.
  • Roles from the resource_access.{client_id}.roles claim, where {client_id} is the Client ID configured for this plugin, will be saved as oidc.client.{group_name}.
  • If the user is in any other group with a name starting with oidc., it will be removed.

See https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_token_role_mappings for more details.

OIDC_STAFF_ROLE, OIDC_SUPERUSER_ROLE

Set or unset the User.is_staff and User.is_superuser fields if the user's realm_access.roles or resource_access.${client_id}.roles claims contain the specified role.

The settings are tuples in the form ('realm', role_name) or ('client', role_name). Set to None to disable this feature.

See https://www.keycloak.org/docs/latest/server_admin/index.html#_oidc_token_role_mappings for more details.

OIDC_SSO_CHECK_COOLDOWN_SECONDS

The time after a redirect to the OIDC authentication endpoint with prompt=none when using the @ksi_oidc_check_sso decorator during which the user will not be redirected again.

If unspecified, the default value is 300 - 5 minutes.