Skip to main content

Authenticate to Django with JSON Web Tokens (JWTs) signed by Cloudflare Access. A Django reimplementation of https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/validating-json/#python-example

To run the demo, set the following environment variables:

export ALLOWEDFLARE_ACCESS_URL=https://your-organization.cloudflareaccess.com
export ALLOWEDFLARE_AUDIENCE=64-character hexadecimal string
export ALLOWEDFLARE_PRIVATE_DOMAIN=your-domain.tld

Then run

docker compose up

Configure Cloudflare Tunnel public hostname demodj.your-domain.tld to http://localhost:8001 or equivalent.

Customizing user setup

On every authenticated request, the backend calls configure_user(user, request, created), which by default makes the user staff and grants all view_* permissions to an allowedflare_everyone group so everyone can browse the admin site read-only. Set ALLOWEDFLARE_CONFIGURE_USER in settings to replace it.

The default re-checks group permissions on every request (a few queries). If you'd rather pay nothing on the request path, do per-user setup only on creation and sync group permissions at post_migrate — the only moment new permissions can come into existence:

# settings.py
def ALLOWEDFLARE_CONFIGURE_USER(user, request, created):
    from yourapp.allowedflare_user import configure_user
    return configure_user(user, request, created)

# yourapp/allowedflare_user.py
from django.contrib.auth.models import Group, Permission

def configure_user(user, request, created):
    if created:
        user.is_staff = True
        user.save()
        everyone, _ = Group.objects.get_or_create(name='allowedflare_everyone')
        user.groups.add(everyone)
    return user

def sync_everyone_permissions(sender, **kwargs):
    everyone, _ = Group.objects.get_or_create(name='allowedflare_everyone')
    missing = Permission.objects.filter(codename__startswith='view').exclude(group=everyone)
    if missing.exists():
        everyone.permissions.add(*missing)

# yourapp/apps.py
from django.apps import AppConfig
from django.db.models.signals import post_migrate

class YourAppConfig(AppConfig):
    name = 'yourapp'

    def ready(self):
        from yourapp.allowedflare_user import sync_everyone_permissions
        post_migrate.connect(sync_everyone_permissions, weak=False)

TODO

  • Iterate on the same-origin (re-)authenticating proxy
  • Iterate on Admin site ModelBackend
    • http://localhost:8001/admin/login/ text when authenticated is "You are authenticated as , but are not authorized to access this page. Would you like to login to a different account?"
  • Expand unit test coverage
  • Basic integration and end-to-end tests
  • mTLS support and testing
  • Configure PostgreSQL
    • Post-migration hook to create a readonly DB user
    • Use the readonly DB user for django-sql-explorer and jupyterhub
    • Update the django-sql-explorer allowlist to accept SET since it's only dangerous for MySQL
    • Exclude only specific fields, like password hash, from the django-sql-explorer view of the django.contrib.auth schema
    • See if admin site change history fields can be shown in the django-sql-explorer schema viewer

Open Questions

Metadata

Release files for allowedflare 2026.35.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for allowedflare 2026.35.1
File Size Uploaded Provenance Actions
allowedflare-2026.35.1.tar.gz 29.3 kB Without provenance Details for allowedflare-2026.35.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for allowedflare 2026.35.1
File Interpreter ABI Platform Actions
allowedflare-2026.35.1-py3-none-any.whl Python 3 none any Details for allowedflare-2026.35.1-py3-none-any.whl

Total release size: 45.6 kB

Release files / allowedflare-2026.35.1.tar.gz

Download URL allowedflare-2026.35.1.tar.gz
Size 29.3 kB
Tags Source
SHA-256 checksum
How to use checksums
eabae676ad16103f19df73a15ac4f768f991afecb5b419f29e11ff3e19f70120
BLAKE2b-256 checksum
How to use checksums
f3d6c071f43729d60176071defd7d86092101535e4c10af0202fc34d6b057d85
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.6

Release files / allowedflare-2026.35.1-py3-none-any.whl

Download URL allowedflare-2026.35.1-py3-none-any.whl
Size 16.3 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4912e5d22bd2336055752dd4a20c032ca53c14ec997c18cef0f3eae850fdc1c7
BLAKE2b-256 checksum
How to use checksums
35ba748a5ff1c7922d8d64eae214b57f049ffdf375465d547a99e20177715df9
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/6.1.0 CPython/3.12.6

Security for allowedflare 2026.35.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

No provenance found

None of the files in this release were uploaded with provenance metadata.

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

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