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
- From-scratch reimplementation of https://developers.cloudflare.com/cloudflare-one/identity/authorization-cookie/cors/#send-authentication-token-with-cloudflare-worker
- Setting username so it can be logged by gunicorn
- Setting the XmlHttpRequest(?) header to avoid redirects to the sign-in page
- 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
readonlyDB user - Use the
readonlyDB user for django-sql-explorer and jupyterhub - Update the django-sql-explorer allowlist to accept
SETsince 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
- Post-migration hook to create a
Open Questions
- Do existing projects like django-allauth or https://django-rest-framework-simplejwt.readthedocs.io/en/latest/index.html already provide this functionality?
- What about RemoteUserMiddleware?
- Should this package help with Wide Key
=Equals Value Logging now that it is already starting to customize Gunicorn? - Are there Free/Libre/Open Source alternatives to Cloudflare Access and Okta that I can run end-to-end tests against?
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)
| 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)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| 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.