mozilla-django-oidc

repository·main·Indexed 20 days ago

https://github.com/mozilla/mozilla-django-oidc

A lightweight Django authentication and access management library for integration with OpenID Connect (OIDC) enabled authentication services. It focuses on the authorization code flow and minimal state storage, providing an OIDC authentication backend, middleware for session refresh, and integration support for Django REST Framework.

Tokens
10.9K
Snippets
34
Records
49
Agent score
66%

What's inside mozilla-django-oidc

  1. Overview of mozilla-django-oidc

    main

    mozilla-django-oidc is a lightweight authentication and access management library designed for integration with OpenID Connect (OIDC) enabled authentication services. It is built with several core design principles:

    • Minimalism: Keeps the footprint as lightweight as possible.
    • Minimal State: Stores as few authentication/authorization artifacts as possible.
    • Extensibility: Allows custom functionality by overriding the authentication backend.
    • OIDC Focus: Primarily supports the OIDC authorization code flow.
    • Security: E2E tested and audited by Mozilla InfoSec.
  2. Validate ID tokens by renewing them with SessionRefresh middleware

    main

    To ensure users are still authorized with the OIDC provider (e.g., if their corporate account was disabled), use the mozilla_django_oidc.middleware.SessionRefresh middleware. This middleware checks if the user's ID token has expired and, if so, performs a silent re-authentication by redirecting to the provider's authentication endpoint.

    Add the middleware to your MIDDLEWARE setting. Note that middleware involving sessions and authentication must come before this one.

    The expiration check interval is controlled by settings.OIDC_RENEW_ID_TOKEN_EXPIRY_SECONDS, which defaults to 15 minutes.

    MIDDLEWARE = [
        # middleware involving session and authentication must come first
        # ...
        'mozilla_django_oidc.middleware.SessionRefresh',
        # ...
    ]
  3. Customize how OIDC identities map to Django users

    main

    By default, the library matches the email address returned in the OIDC user info claims to the email field of an existing Django user. If your application uses a different unique identifier (like a unique email field in a Profile model), you must subclass mozilla_django_oidc.auth.OIDCAuthenticationBackend and override the filter_users_by_claims method.

    After subclassing, use the Python dotted path to your new class in settings.AUTHENTICATION_BACKENDS instead of the default one.

    from mozilla_django_oidc.auth import OIDCAuthenticationBackend
    
    class MyOIDCAB(OIDCAuthenticationBackend):
        def filter_users_by_claims(self, claims):
            email = claims.get('email')
            if not email:
                return self.UserModel.objects.none()
    
            try:
                profile = Profile.objects.get(email=email)
                return [profile.user]
    
            except Profile.DoesNotExist:
                return self.UserModel.objects.none()
  4. Set up mozilla-django-oidc for local development

    main

    To contribute to the project, follow these steps to set up a local development environment with all necessary dependencies for testing and linting:

    1. Fork the repository on GitHub.
    2. Clone your fork locally.
    3. Create and activate a virtual environment.
    4. Install the package in editable mode with development dependencies using pip install -e .[dev].
    5. Create a new branch for your work.

    Before submitting changes, ensure you run make lint and make test (or tox) to verify your changes pass linting and tests across supported Python versions.

    $ git clone git@github.com:your_name_here/mozilla-django-oidc.git
    $ cd mozilla-django-oidc/
    $ python -m venv .venv
    $ source .venv/bin/activate  # On Windows use `.venv\Scripts\activate`
    $ pip install -e .[dev]
    $ git checkout -b name-of-your-bugfix-or-feature
  5. Perform advanced user verification based on claims

    main

    To decide whether an authentication attempt should be allowed based on specific claim values (e.g., checking if a user belongs to a specific group), subclass mozilla_django_oidc.auth.OIDCAuthenticationBackend and override the verify_claims method. This method must return True to allow authentication or False to reject it.

    class MyOIDCAB(OIDCAuthenticationBackend):
        def verify_claims(self, claims):
            verified = super(MyOIDCAB, self).verify_claims(claims)
            is_admin = 'admin' in claims.get('group', [])
            return verified and is_admin
  6. Configure mozilla-django-oidc in settings.py

    main

    To integrate the library into your Django project, update settings.py with the following configurations:

    1. Add to INSTALLED_APPS: Ensure mozilla_django_oidc is loaded after django.contrib.auth.
    2. Set Authentication Backend: Add mozilla_django_oidc.auth.OIDCAuthenticationBackend to AUTHENTICATION_BACKENDS.
    3. Provider Credentials: Set OIDC_RP_CLIENT_ID and OIDC_RP_CLIENT_SECRET. Warning: Do not check these into version control; pull them from environment variables.
    4. Provider Endpoints: Provide the OP's specific endpoints for authorization, tokens, and user info.
    5. Redirect URLs: Define where users should go after login and logout.
    # Add to INSTALLED_APPS
    INSTALLED_APPS = (
        # ...
        'django.contrib.auth',
        'mozilla_django_oidc',  # Load after auth
        # ...
    )
    
    # Add authentication backend
    AUTHENTICATION_BACKENDS = (
        'mozilla_django_oidc.auth.OIDCAuthenticationBackend',
        # ...
    )
    
    # Provider credentials (use environment variables)
    OIDC_RP_CLIENT_ID = os.environ['OIDC_RP_CLIENT_ID']
    OIDC_RP_CLIENT_SECRET = os.environ['OIDC_RP_CLIENT_SECRET']
    
    # Provider endpoints
    OIDC_OP_AUTHORIZATION_ENDPOINT = "<URL of the OIDC OP authorization endpoint>"
    OIDC_OP_TOKEN_ENDPOINT = "<URL of the OIDC OP token endpoint"
    OIDC_OP_USER_ENDPOINT = "<URL of the OIDC OP userinfo endpoint>"
    
    # Site redirect settings
    LOGIN_REDIRECT_URL = "<URL path to redirect to after login>"
    LOGOUT_REDIRECT_URL = "<URL path to redirect to after logout>"
  7. Customize user creation and updates via OIDCAuthenticationBackend

    main

    To perform additional bookkeeping (like populating profile data from claims) when a user is created or updated, subclass mozilla_django_oidc.auth.OIDCAuthenticationBackend and override create_user and/or update_user.

    from mozilla_django_oidc.auth import OIDCAuthenticationBackend
    from myapp.models import Profile
    
    class MyOIDCAB(OIDCAuthenticationBackend):
        def create_user(self, claims):
            user = super(MyOIDCAB, self).create_user(claims)
    
            user.first_name = claims.get('given_name', '')
            user.last_name = claims.get('family_name', '')
            user.save()
    
            return user
    
        def update_user(self, user, claims):
            user.first_name = claims.get('given_name', '')
            user.last_name = claims.get('family_name', '')
            user.save()
    
            return user
  8. Integrate with Django REST Framework (DRF)

    main

    To allow Django REST Framework to authenticate users using an OAuth access token provided in the Authorization header, use the mozilla_django_oidc.contrib.drf.OIDCAuthentication class.

    Add this class to your REST_FRAMEWORK settings under DEFAULT_AUTHENTICATION_CLASSES. It is common to include rest_framework.authentication.SessionAuthentication alongside it.

    Important Limitations:

    • This integration only handles authenticating against an existing access token.
    • It does not provide functionality to create or renew tokens.
    REST_FRAMEWORK = {
        'DEFAULT_AUTHENTICATION_CLASSES': [
            'mozilla_django_oidc.contrib.drf.OIDCAuthentication',
            'rest_framework.authentication.SessionAuthentication',
            # other authentication classes, if needed
        ],
    }
  9. Pull Request Guidelines

    main

    When submitting a pull request, ensure the following requirements are met:

    1. Include tests: All new functionality must be accompanied by tests.
    2. Update documentation: If adding functionality, update the relevant docs. New functionality should be placed in a function with a docstring, and the feature should be added to the list in README.rst.
    3. Python Compatibility: Ensure the PR works for Python 3.10+. Verify that tests pass for all supported Python versions via GitHub Actions.
    4. Update History: Update HISTORY.rst with your changes under the appropriate categories: Backwards-incompatible changes, Features, or Bugs.
  10. Implement provider-side logout

    main

    By default, the library only ends the Django session. To also end the session at the OIDC provider, implement a function and assign it to settings.OIDC_OP_LOGOUT_URL_METHOD. This function should return the URL where the user should be redirected to log out of the provider.

    def provider_logout(request):
        # See your provider's documentation for details
        redirect_url = 'https://myprovider.com/logout'
        return redirect_url
    
    # In settings.py:
    # OIDC_OP_LOGOUT_URL_METHOD = 'myapp.auth_utils.provider_logout'