You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Run your Django application directly on Cloudflare Workers for edge performance and scalability.
22
23
@@ -174,7 +175,7 @@ root
174
175
Access `https://your-worker-url.com/__run_migrations__/` to trigger migrations.
175
176
Access `https://your-worker-url.com/__create_admin__/` to trigger admin creation.
176
177
177
-
### 2. Cloudflare D1 Integration
178
+
## Cloudflare D1 Integration
178
179
179
180
Use Cloudflare D1, a serverless SQL database, as your Django application's database.
180
181
@@ -184,7 +185,7 @@ Use Cloudflare D1, a serverless SQL database, as your Django application's datab
184
185
185
186
There are two ways to connect to D1:
186
187
187
-
#### a) D1 Binding (Recommended for Workers)
188
+
### a) D1 Binding (Recommended for Workers)
188
189
189
190
When your Django application is running on Cloudflare Workers, use D1 Bindings for the fastest access. The D1 database is made available to your worker via an environment binding.
190
191
@@ -200,7 +201,7 @@ DATABASES = {
200
201
```
201
202
Ensure your `wrangler.toml` includes the D1 database binding (as shown in the Cloudflare Workers section).
202
203
203
-
####b) D1 API (For Local Development or External Access)
204
+
### b) D1 API (For Local Development or External Access)
204
205
205
206
Connect to D1 via its HTTP API. This method is suitable for local development (e.g., running migrations) or when accessing D1 from outside Cloudflare Workers.
206
207
@@ -220,7 +221,7 @@ DATABASES = {
220
221
221
222
A simple tutorial for getting started with Django and D1 is [available here](https://massadas.com/posts/django-meets-cloudflare-d1/).
222
223
223
-
### 3. Cloudflare Durable Objects Integration
224
+
## Cloudflare Durable Objects Integration
224
225
225
226
Utilize Durable Objects for stateful data persistence directly within your Cloudflare Workers. This is useful for applications requiring low-latency access to state associated with specific objects or instances.
226
227
@@ -276,6 +277,280 @@ Utilize Durable Objects for stateful data persistence directly within your Cloud
276
277
```
277
278
The `django_cf.do_binding` engine will automatically use the Durable Object binding specified in your `wrangler.toml` that is associated with the `DjangoCFDurableObject` class. Ensure your `wrangler.toml` correctly binds a name to your `DjangoCFDurableObject` subclass.
278
279
280
+
## Cloudflare Access Authentication
281
+
282
+
Seamless authentication middleware for Cloudflare Access protected applications. The middleware is implemented using only Python standard library modules - no external dependencies required!
283
+
284
+
This middleware works on both self-hosted Django applications and Django hosted on Cloudflare Workers.
285
+
286
+
### Quick Start
287
+
288
+
#### 1. Add to INSTALLED_APPS
289
+
290
+
```python
291
+
# settings.py
292
+
INSTALLED_APPS = [
293
+
# ... your other apps
294
+
'django_cf',
295
+
]
296
+
```
297
+
298
+
#### 2. Configure Cloudflare Access Middleware
299
+
300
+
Add the middleware to your Django settings:
301
+
302
+
```python
303
+
# settings.py
304
+
MIDDLEWARE= [
305
+
'django.middleware.security.SecurityMiddleware',
306
+
'django.contrib.sessions.middleware.SessionMiddleware', # Must be before CloudflareAccessMiddleware
# ... other middleware (should be placed appropriately in your middleware stack)
311
+
]
312
+
```
313
+
314
+
#### 3. Configure Cloudflare Access Settings
315
+
316
+
Add the required settings to your Django configuration:
317
+
318
+
```python
319
+
# settings.py
320
+
321
+
# Required settings (at least one must be provided)
322
+
CLOUDFLARE_ACCESS_AUD='your-application-audience-tag'# Optional if team name is provided
323
+
CLOUDFLARE_ACCESS_TEAM_NAME='yourteam'# Optional if AUD is provided
324
+
325
+
# Optional settings
326
+
CLOUDFLARE_ACCESS_EXEMPT_PATHS= [
327
+
'/health/',
328
+
'/api/public/',
329
+
'/admin/login/', # If you want to keep Django admin login
330
+
]
331
+
CLOUDFLARE_ACCESS_CACHE_TIMEOUT=3600# Cache timeout for public keys (seconds)
332
+
```
333
+
334
+
### Required Settings (At Least One)
335
+
336
+
You must provide **at least one** of the following settings:
337
+
338
+
#### `CLOUDFLARE_ACCESS_AUD`
339
+
Your Cloudflare Access Application Audience (AUD) tag. You can find this in your Cloudflare Zero Trust dashboard:
340
+
1. Go to Access → Applications
341
+
2. Select your application
342
+
3. Copy the "Application Audience (AUD) Tag"
343
+
344
+
**When to use**: Provide this if you want strict audience validation or if you have multiple applications with the same team.
345
+
346
+
#### `CLOUDFLARE_ACCESS_TEAM_NAME`
347
+
Your Cloudflare team name (the subdomain part of your team domain). For example, if your team domain is `mycompany.cloudflareaccess.com`, then your team name is `mycompany`.
348
+
349
+
**When to use**: Provide this if you want simpler configuration or if you only have one application per team.
350
+
351
+
### Configuration Options
352
+
353
+
You can configure the middleware in three ways:
354
+
355
+
1.**Both AUD and Team Name** (Recommended for production):
3.**Only Team Name** (AUD validated but not enforced):
367
+
```python
368
+
CLOUDFLARE_ACCESS_TEAM_NAME='yourteam'
369
+
```
370
+
371
+
### Optional Settings
372
+
373
+
#### `CLOUDFLARE_ACCESS_EXEMPT_PATHS`
374
+
Default: `[]`
375
+
376
+
List of URL paths that should be exempt from Cloudflare Access authentication. Useful for:
377
+
- Health check endpoints
378
+
- Public API endpoints
379
+
- Webhooks
380
+
- Static file serving (if not handled by your web server)
381
+
382
+
Example:
383
+
```python
384
+
CLOUDFLARE_ACCESS_EXEMPT_PATHS= [
385
+
'/health/',
386
+
'/api/public/',
387
+
'/webhooks/',
388
+
'/static/', # If serving static files through Django
389
+
]
390
+
```
391
+
392
+
#### `CLOUDFLARE_ACCESS_CACHE_TIMEOUT`
393
+
Default: `3600` (1 hour)
394
+
395
+
How long to cache Cloudflare's public keys (in seconds). Cloudflare rotates these keys periodically, so caching reduces API calls while ensuring fresh keys are fetched when needed.
396
+
397
+
### How It Works
398
+
399
+
#### Authentication Flow
400
+
401
+
1.**Request Processing**: For each incoming request, the middleware checks if the path is exempt from authentication
402
+
2.**JWT Extraction**: Extracts the JWT token from:
403
+
-`CF-Access-Jwt-Assertion` header (preferred)
404
+
-`CF_Authorization` cookie (fallback)
405
+
3.**Team Discovery**: If team name is not configured, extracts it from the JWT's issuer claim
406
+
4.**Public Key Retrieval**: Fetches Cloudflare's public keys from the team's certs endpoint
407
+
5.**Token Validation**: Validates the JWT against Cloudflare's public keys:
408
+
- Validates token signature and expiration
409
+
- Validates audience (AUD) if configured
410
+
6.**User Management**:
411
+
- Extracts email and name from JWT claims
412
+
- Creates new Django user if doesn't exist
413
+
- Updates existing user's name if changed
414
+
- Logs the user into Django session
415
+
7.**Response**: Proceeds with the request if authentication succeeds, returns 401 if it fails
416
+
417
+
#### User Creation
418
+
419
+
When a user authenticates for the first time:
420
+
- Username is set to the user's email address
421
+
- Email is extracted from JWT `email` claim
422
+
- Name is split into first_name and last_name from JWT `name` claim
423
+
- User is created with `is_active=True`
424
+
425
+
For existing users:
426
+
- Name is updated if it has changed in Cloudflare
427
+
- User is automatically logged in
428
+
429
+
### Security Considerations
430
+
431
+
#### Token Validation
432
+
- All JWT tokens are validated against Cloudflare's public keys
433
+
- Tokens are checked for expiration
434
+
- Audience (AUD) is validated to ensure tokens are for your application
435
+
436
+
#### Caching
437
+
- Public keys are cached to reduce API calls to Cloudflare
438
+
- Cache keys are scoped to your team name
439
+
- Failed key fetches are logged but don't crash the application
440
+
441
+
#### Error Handling
442
+
- Authentication failures return 401 responses
443
+
- Server errors return 500 responses
444
+
- All errors are logged for debugging
445
+
446
+
### Logging
447
+
448
+
The middleware uses Django's logging system with logger name `django_cf.middleware`. To see debug information:
449
+
450
+
```python
451
+
# settings.py
452
+
LOGGING= {
453
+
'version': 1,
454
+
'disable_existing_loggers': False,
455
+
'handlers': {
456
+
'console': {
457
+
'class': 'logging.StreamHandler',
458
+
},
459
+
},
460
+
'loggers': {
461
+
'django_cf.middleware': {
462
+
'handlers': ['console'],
463
+
'level': 'INFO',
464
+
'propagate': True,
465
+
},
466
+
},
467
+
}
468
+
```
469
+
470
+
### Common Issues and Troubleshooting
471
+
472
+
#### 401 Unauthorized Responses
473
+
474
+
**Cause**: JWT token is missing, invalid, or expired.
475
+
476
+
**Solutions**:
477
+
1. Ensure your application is behind Cloudflare Access
478
+
2. Check that users are properly authenticated with Cloudflare Access
479
+
3. If using `CLOUDFLARE_ACCESS_AUD`, verify it matches your application's AUD tag
480
+
4. If using `CLOUDFLARE_ACCESS_TEAM_NAME`, check that it's correct
481
+
5. Review middleware logs for specific validation errors
482
+
483
+
#### 500 Internal Server Error
484
+
485
+
**Cause**: Unable to fetch Cloudflare public keys or other configuration issues.
486
+
487
+
**Solutions**:
488
+
1. Verify your team name is correct (if using `CLOUDFLARE_ACCESS_TEAM_NAME`)
489
+
2. Check network connectivity to Cloudflare
490
+
3. Review logs for specific error messages
491
+
4. If using only AUD, ensure the JWT contains a valid issuer claim
492
+
493
+
#### Users Not Being Created
494
+
495
+
**Cause**: Missing email claim in JWT or database issues.
496
+
497
+
**Solutions**:
498
+
1. Check that your Cloudflare Access application is configured to include email in JWT
499
+
2. Verify Django database migrations are up to date
500
+
3. Check Django user model permissions
501
+
502
+
### Advanced Usage
503
+
504
+
#### Custom User Creation
505
+
506
+
If you need custom user creation logic, you can subclass the middleware:
507
+
508
+
```python
509
+
from django_cf.middleware import CloudflareAccessMiddleware
If you have multiple Cloudflare Access applications, you can configure different middleware instances or use environment-specific settings.
533
+
534
+
### Development
535
+
536
+
#### Testing
537
+
538
+
When developing applications with this middleware, you may want to disable it in certain environments:
539
+
540
+
```python
541
+
# settings.py
542
+
ifDEBUGandnot os.getenv('ENABLE_CF_ACCESS'):
543
+
# Remove CloudflareAccessMiddleware from MIDDLEWARE list
544
+
MIDDLEWARE= [m for m inMIDDLEWAREif'CloudflareAccessMiddleware'notin m]
545
+
```
546
+
547
+
#### Local Development
548
+
549
+
For local development without Cloudflare Access:
550
+
1. Set exempt paths to include your development URLs
551
+
2. Use Django's built-in authentication for local testing
552
+
3. Consider using environment variables to toggle the middleware
553
+
279
554
## Limitations
280
555
281
556
***D1 Database:**
@@ -285,13 +560,23 @@ Utilize Durable Objects for stateful data persistence directly within your Cloud
285
560
* Always refer to the official [Django limitations for SQLite databases](https://docs.djangoproject.com/en/stable/ref/databases/#sqlite-notes), as D1 is SQLite-compatible but has its own serverless characteristics.
286
561
***Durable Objects:** While powerful for stateful serverless applications, ensure you understand the consistency model and potential data storage costs associated with Durable Objects.
287
562
288
-
## Development
563
+
## Contributing
289
564
290
565
See [DEVELOPMENT.md](DEVELOPMENT.md) for details on setting up a development environment and contributing to `django-cf`.
291
566
567
+
We welcome contributions! Please see our contributing guidelines and feel free to submit issues and pull requests.
568
+
569
+
## Support
570
+
571
+
For issues and questions:
572
+
1. Check the troubleshooting section above
573
+
2. Review Cloudflare Access documentation
574
+
3. Submit an issue on GitHub with detailed error logs and configuration (remove sensitive information)
575
+
292
576
## License
293
577
294
578
This project is licensed under the MIT License. See the [LICENSE](LICENSE) file for details.
579
+
295
580
---
296
581
297
582
*For a more detailed example of a Django project structured for Cloudflare Workers with D1, check out relevant community projects or the `django-cf` examples if available.*
0 commit comments