Skip to main content

Datapunt Django Authorization

python 3.10 onward Mozilla Public License Version 2.0

Django middleware that adds functionality to check authorization, based on JSON Web Tokens.

Unlike many Django OAuth2/OIDC libraries, this middleware does NOT interact with Django User objects. It only validates the JSON Web Token, and exposes its scopes in the request object. This allows backends to operate based on the token scope.


Install

Install the Django middleware:

pip install datapunt-authorization-django

Add authorization_django.authorization_middleware to the list of middlewares in settings.py, and configure either a JWKS as json or an url to a JWKS.

MIDDLEWARE = (
   ...
   'authorization_django.middleware.AuthorizationMiddleware',
)

The old-style of using authorization_django.authorization_middleware is still supported, but no longer recommended.

Settings

The following settings are used by the middleware, and can be configured in your settings.py in the DATAPUNT_AUTHZ dictionary.

Setting Description Default value
JWKS A valid JWKS as json, to validate tokens. See RFC 7517 and 7518 for details ""
JWKS_URL A url to a valid JWKS, to validate tokens ""
JWKS_URLS A list of URLs to a valid JWKS, to validate tokens ""
CHECK_CLAIMS Which claims to check, e.g. {"iss": "...", "aud": "..."} {}
MIN_INTERVAL_KEYSET_UPDATE Minimal interval in secs between two checks for keyset update 30
MIN_SCOPE Minimum needed scope(s) to view non-whitelisted urls empty tuple
FORCED_ANONYMOUS_ROUTES Routes for which not to check for authorization (whitelist) empty tuple
PROTECTED Routes which require scopes for access. Optionally with distinction of methods empty list
ALWAYS_OK Disable any authorization checks, use only for local development False
ALLOWED_SIGNING_ALGORITHMS List of allowed algorithms for signing web tokens ['ES256', 'ES384', 'ES512', 'RS256', 'RS384', 'RS512']
EXCEPTION_HANDLER Custom function to handle a raised exception function None

The possible values for CHECK_CLAIMS are the RFC 7519 defined claims. The relevant values are:

  • iss (str issuer): Identifies the principal that issued the token.
  • aud (str/list audience): Identifies the intended audience. This can be a list too.

Usage

Scope notation

Beware of the scope notation! All scopes that are read from the token are converted using scope.upper().replace("_", "/").

All scopes are transformed to uppercase, and underscores _ are replaced by slashes /. So a scope read_only in keycloak should be defined as READ/ONLY in the settings.

The middleware provides different ways to add authorization to the application:

Define a minimal scope that is required for access

With the MIN_SCOPE setting you can define a tuple of scopes that are required to access the application. An exception is made for the routes defined in FORCED_ANONYMOUS_ROUTES, which is basically a whitelist, and for the OPTIONS method, which is always allowed. It is also allowed to configure a single scope as a string.

# Require 'EMPLOYEE' scope for access, except for /status route
'MIN_SCOPE': 'EMPLOYEE'
'FORCED_ANONYMOUS_ROUTES': '/status'

or e.g.

# Require 'EMPLOYEE' and 'HR' scope for access
'MIN_SCOPE': ('EMPLOYEE', 'HR')

Define protected routes

With the PROTECTED setting you can define routes that require certain scopes for access. A distinction can be made between HTTP methods. An exception is made for the OPTIONS method, which is always allowed.

# Require 'EMPLOYEE' scope for access to /api/secure route
'PROTECTED': [
  ('/api/secure', ['*'], ['EMPLOYEE'])
]
# Require 'EMPLOYEE' scope for read access to /private route
# Require 'ADMIN' scope for write access to /private route
'PROTECTED': [
  ('/private', ['GET', 'HEAD'], ['EMPLOYEE'])
  ('/private', ['POST', 'PUT', 'PATCH', 'DELETE'], ['ADMIN'])
]

Note: the FORCED_ANONYMOUS_ROUTES setting takes precedence over the routes defined in PROTECTED, so if a route in PROTECTED starts with a route set in FORCED_ANONYMOUS_ROUTES, this will lead to a ProtectedRouteConflictError

A method to check for authorization is added to the request object

It will add a callable request.is_authorized_for(scope) that can tell you whether the current request is authorized for the given scope:

if request.is_authorized_for('ADMIN'):
  ...  # do admin things
elif request.is_authorized_for('EMPLOYEE'):
  ...  # do employee level things
else:
  ...  # only the public stuff

Django REST Framework Extensions

To enforce JWT authentication for REST views, add the following code. This will also update the generated OpenAPI specification when drf-spectacular is used in the project.

from rest_framework.views import APIView
from authorization_django.extensions.drf import JWTAuthentication


class CustomAPIView(APIView):
    authentication_classes = [JWTAuthentication]

Or configure this globally:

REST_FRAMEWORK = {
    "DEFAULT_AUTHENTICATION_CLASSES": [
        "authorization_django.extensions.drf.JWTAuthentication",
    ],
}

Additional token-scopes can also be verified for specific view:

from rest_framework.views import APIView
from authorization_django.extensions.drf import HasTokenScopes


class CustomAPIView(APIView):
    permission_classes = [HasTokenScopes("extra-scope1", "extra-scope2")]

Contribute

Create a uv-managed environment, install the project with the optional development dependencies, and start coding:

uv sync --extra extended

Testing:

make test

Doing a release

(This is for authorization_django developers.)

We use GitHub pull requests. If your PR should produce a new release of authorization_django, make sure one of the commits 1) increments the version number in pyproject.toml appropriately and 2) adds a description of the release to the changelog below in this README. Then,

  1. Merge the commit in GitHub, after review;
  2. Pull the code from GitHub and merge it into the master branch: git checkout main && git fetch origin && git merge --ff-only origin/main
  3. Tag the release vX.Y.Z with git tag -a vX.Y.Z -m "Bump to vX.Y.Z"
  4. Push the tag to GitHub with git push origin --tags
  5. The publish-to-pypi-workflow will automatically publish the release

Changelog

  • v2.2.0
    • Update toolchain to use uv.
    • Added failunder for test coverage.
  • v2.1.1
    • Bump packages.
  • v2.1.0
    • Add account_id from the user to the request.
  • v2.0.4
    • Bump packages.
  • v2.0.3
    • Bump packages.
  • v2.0.2
    • Ensure thread safety by encapsulating jwks logic in wrapper class.
  • v2.0.1
    • Fix allow custom responses and preserve original responses
  • v2.0.0
    • Allow for custom (JSON) response after raised exception instead of direct 4** response.
  • v1.8.0
    • Add support for apps that use both Entra ID and Keycloak
  • v1.7.0
    • Support Python 3.13 and 3.14
  • v1.6.2
    • Add appid to Entra token claims
  • v1.6.1
    • Fix reading subject claim for Entra ID SPN tokens.
  • v1.6.0
    • Added claim checking using CHECK_CLAIMS, and enforce it for Microsoft Entra ID.
  • v1.5.0
    • Add authentication class for django rest framework and drf-spectacular
  • v1.4.0
    • Support Microsoft Entra ID token structure
    • Added JWKS_URLS setting to authenticate against multiple backends
  • v1.3.3
    • Bump jwcrypto requirement to 1.4.2
  • v1.3.2
    • Stopped logging entire Authorization headers in case of a parse error
  • v1.3.1
    • Extended support for Microsoft Azure AD JWT Token structure
    • Improved tests for Expired token logic
  • v1.3.0
    • Support Microsoft Azure AD JWT Token structure
  • v1.2.0:
    • expose claims via get_token_claims
    • Expose scopes via get_token_scopes
    • Fix SyntaxWarning in middleware
  • v1.1.0
    • Add option to require authorization for specific routes
    • Fix MIN_SCOPE as tuple bug
  • v1.0.0
    • By default do not allow symmetric signing algoritms
  • v0.3.1
    • Bugfix for token with empty scopes claim
    • Lowered version requirement for requests module
  • v0.3
    • Use jwcrypto module to verify tokens
    • Add support to load JWKS from public url
    • Remove support for custom logger settings
  • v0.2.3
    • Settings are now grouped in settings.py (see Settings section above)
    • Middleware now creates audit logs

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

datapunt_authorization_django-2.2.0.tar.gz (23.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

datapunt_authorization_django-2.2.0-py3-none-any.whl (20.4 kB view details)

Uploaded Python 3

File details

Details for the file datapunt_authorization_django-2.2.0.tar.gz.

File metadata

  • Download URL: datapunt_authorization_django-2.2.0.tar.gz
  • Upload date:
  • Size: 23.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for datapunt_authorization_django-2.2.0.tar.gz
Algorithm Hash digest
SHA256 28eedf126a92d6dd57f8633baaa3a08c5f8de8a10dc1fc24878a2ec4a44ee75b
MD5 82337088f1b80fe2ff6af4814940f2ab
BLAKE2b-256 1a7c08f67853b08ec78f616327822a13e32cd316e617c8cd9efede33e538cc10

See more details on using hashes here.

File details

Details for the file datapunt_authorization_django-2.2.0-py3-none-any.whl.

File metadata

  • Download URL: datapunt_authorization_django-2.2.0-py3-none-any.whl
  • Upload date:
  • Size: 20.4 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? Yes
  • Uploaded via: uv/0.12.5 {"installer":{"name":"uv","version":"0.12.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

File hashes

Hashes for datapunt_authorization_django-2.2.0-py3-none-any.whl
Algorithm Hash digest
SHA256 df18726514f6fc17005c303c76152e017e1812595ec0a937c9145ee99080fe9f
MD5 2920e25e7632ed9c8755b86a31f4791b
BLAKE2b-256 107efe6d98d2ac7c35a2ead78a276c4d4f19aa888b1e5840c10963f028820cba

See more details on using hashes here.

Release history Release notifications | RSS feed

2.3.0

2 files

This release

2.2.0 This release

2 files

2.1.1

2 files

2.1.0

2 files

2.0.4

2 files

2.0.3

2 files

2.0.2

2 files

2.0.1

2 files

2.0.0

2 files

1.8.0

2 files

1.7.0

2 files

1.6.2

2 files

1.6.1

2 files

1.6.0

2 files

1.5.0.post1

2 files

1.5.0

2 files

1.4.0

2 files

1.3.3

2 files

1.3.2

2 files

1.3.1

1 file

1.3.0

1 file

1.2.0

1 file

1.1.0

1 file

1.0.0

1 file

0.3.1

1 file

0.3

1 file

0.2.24

1 file

0.2.23

1 file

0.2.22

1 file

0.2.20

1 file

0.2.19

1 file

0.2.18

1 file

0.2.17

1 file

0.2.16

1 file

0.2.15

1 file

0.2.14

1 file

0.2.13

1 file

0.2.12

1 file

0.2.11

1 file

0.2.10

1 file

0.2.9

1 file

0.2.8

1 file

0.2.7

1 file

0.2.6

1 file

0.2.5

1 file

0.2.4

1 file

0.2.3

1 file

0.2.2

1 file

0.2.1

1 file

0.2.0

1 file

0.1.0

1 file

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page