Django OAuth Toolkit DCR
An extension to Django OAuth Toolkit that adds support for OAuth 2.0 Dynamic Client Registration as defined in RFC 7591.
Features
- ✅ RFC 7591 Compliant: Full implementation of OAuth 2.0 Dynamic Client Registration
- ✅ Open Registration Mode: No authentication required for client registration
- ✅ Comprehensive Validation: Validates client metadata, redirect URIs, and grant types
- ✅ Django OAuth Toolkit Integration: Seamlessly works with existing DOT applications
- ✅ Flexible Grant Type Support: Authorization Code, Implicit, Client Credentials, and more
- ✅ Error Handling: RFC-compliant error responses
- ✅ Security Focused: Built-in validations and configurable restrictions
Installation
Install the package using pip:
pip install django-oauth-toolkit-dcr
Requirements
- Python 3.10+
- Django 4.0+
- django-oauth-toolkit 3.0.1+
Quick Start
1. Add to Django Settings
Add the package to your INSTALLED_APPS:
INSTALLED_APPS = [
# ... your other apps
'oauth2_provider',
'oauth_dcr',
]
2. Configure URLs
Add the Dynamic Client Registration endpoint to your URL configuration:
# urls.py
from django.urls import path, include
from oauth_dcr.views import DynamicClientRegistrationView
urlpatterns = [
# Your existing OAuth2 URLs
path('o/', include('oauth2_provider.urls', namespace='oauth2_provider')),
# Dynamic Client Registration endpoint
path('o/register/', DynamicClientRegistrationView.as_view(), name='oauth2_dcr'),
]
3. Run Migrations
Make sure your Django OAuth Toolkit migrations are up to date:
python manage.py migrate
Usage
Client Registration Request
Clients can register themselves by sending a POST request to the registration endpoint:
curl -X POST https://your-server.com/o/register/ \
-H "Content-Type: application/json" \
-d '{
"client_name": "My Amazing App",
"redirect_uris": [
"https://myapp.com/oauth/callback",
"https://myapp.com/oauth/callback2"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"scope": "read write",
"client_uri": "https://myapp.com",
"contacts": ["admin@myapp.com"]
}'
Successful Registration Response
{
"client_id": "AbCdEf123456",
"client_secret": "secret_AbCdEf123456789",
"client_id_issued_at": 1625097600,
"client_name": "My Amazing App",
"redirect_uris": [
"https://myapp.com/oauth/callback",
"https://myapp.com/oauth/callback2"
],
"grant_types": ["authorization_code"],
"response_types": ["code"],
"token_endpoint_auth_method": "client_secret_basic"
}
Error Response Examples
Invalid Grant Type:
{
"error": "invalid_client_metadata",
"error_description": "Grant type 'password' is not allowed for dynamic registration"
}
Missing Redirect URIs:
{
"error": "invalid_client_metadata",
"error_description": "redirect_uris required for authorization code grants"
}
HTTPS Required:
{
"error": "invalid_client_metadata",
"error_description": "HTTPS required for redirect URIs in production: http://example.com/callback"
}
Supported Client Metadata
The following client metadata parameters are supported:
| Parameter | Required | Description |
|---|---|---|
client_name |
No | Human-readable name for the client |
redirect_uris |
Conditional* | Array of redirect URIs |
grant_types |
No | Array of grant types (default: ["authorization_code"]) |
response_types |
No | Array of response types |
scope |
No | Space-separated list of scopes |
client_uri |
No | URL of the client's homepage |
contacts |
No | Array of contact email addresses |
token_endpoint_auth_method |
No | Token endpoint authentication method |
*Required for authorization_code, implicit, and hybrid flows.
Supported Grant Types
All RFC 7591 compliant grant types are supported:
authorization_code- Authorization Code Grant ✅ (enabled by default)implicit- Implicit Grant ✅ (enabled by default)refresh_token- Refresh Token Grant ✅ (enabled by default)password- Resource Owner Password Credentials Grant (⚠️ disabled by default - security risk)client_credentials- Client Credentials Grant (⚠️ disabled by default - security risk)urn:ietf:params:oauth:grant-type:jwt-bearer- JWT Bearer Grant (⚠️ disabled by default - security risk)urn:ietf:params:oauth:grant-type:saml2-bearer- SAML 2.0 Bearer Grant (⚠️ disabled by default - security risk)
Configuration
Django OAuth Toolkit Settings
The extension respects your existing Django OAuth Toolkit configuration:
# settings.py
OAUTH2_PROVIDER = {
'ALLOWED_REDIRECT_URI_SCHEMES': ['https', 'http'], # Used for validation
'OIDC_RSA_PRIVATE_KEY': 'your-rsa-key', # For OIDC support
# ... other settings
}
Dynamic Client Registration Settings
Configure DCR-specific settings for enhanced security:
# settings.py
OAUTH_DCR_SETTINGS = {
# Grant types allowed for dynamic registration (RFC 7591 safe defaults)
'ALLOWED_GRANT_TYPES': [
'authorization_code', # Safe for open registration
'implicit', # Part of RFC 7591 (deprecated in OAuth 2.1)
'refresh_token', # Safe - used for token renewal
# 'password', # SECURITY RISK: Allows credential collection
# 'client_credentials', # SECURITY RISK: Machine-to-machine access
# 'urn:ietf:params:oauth:grant-type:jwt-bearer', # SECURITY RISK
# 'urn:ietf:params:oauth:grant-type:saml2-bearer', # SECURITY RISK
],
# Require HTTPS for redirect URIs in production (default: True in production)
'REQUIRE_HTTPS_REDIRECT_URIS': True,
}
Security Rationale for Grant Type Restrictions
| Grant Type | Security Risk | Why Restricted by Default |
|---|---|---|
password |
HIGH | Allows any client to collect user credentials |
client_credentials |
HIGH | Enables machine-to-machine access without user consent |
jwt-bearer |
MEDIUM | Can potentially bypass normal authentication flows |
saml2-bearer |
MEDIUM | Can potentially bypass normal authentication flows |
authorization_code |
LOW | Secure with proper PKCE implementation ✅ |
implicit |
MEDIUM | Deprecated due to token exposure, but part of RFC 7591 ✅ |
refresh_token |
LOW | Safe token renewal mechanism ✅ |
Security Considerations
Since this implements "open" registration mode, the following security measures are strongly recommended:
- Grant Type Restrictions: Only allow safe grant types (default configuration)
- HTTPS Enforcement: Require HTTPS for redirect URIs in production (default)
- Rate Limiting: Use Django rate limiting middleware
- Monitoring: Log all registration attempts
- Cleanup: Implement periodic cleanup of unused clients
- Network Security: Consider IP allowlisting or VPN requirements
Advanced Usage
Custom Validation
You can extend the view to add custom validation:
from oauth_dcr.views import DynamicClientRegistrationView
from django.core.exceptions import ValidationError
class CustomDCRView(DynamicClientRegistrationView):
def _validate_client_metadata(self, metadata):
# Call parent validation first
processed = super()._validate_client_metadata(metadata)
# Add custom validation
if 'client_name' in metadata:
if 'forbidden' in metadata['client_name'].lower():
raise ValidationError("Client name contains forbidden words")
return processed
Rate Limiting Example
Using django-ratelimit:
from django_ratelimit.decorators import ratelimit
from django.utils.decorators import method_decorator
@method_decorator(ratelimit(key='ip', rate='10/h', method='POST'), name='post')
class RateLimitedDCRView(DynamicClientRegistrationView):
pass
API Reference
DynamicClientRegistrationView
Main view class that handles client registration requests.
Methods
post(request)- Handle registration requests_validate_client_metadata(metadata)- Override for custom validation_create_application(metadata)- Override for custom application creation
Testing
Run the tests:
pytest
Contributing
Contributions are welcome! Please feel free to submit a Pull Request.
Contributions are particularly welcome to implement:
- RFC 7592 Dynamic Client Registration Management Protocol (client configuration endpoint, update/delete operations)
- Additional registration modes (protected, authenticated, administrative registration beyond the current open mode)
- Enhanced security features (rate limiting, audit logging, client attestation)
- OpenID Connect Dynamic Client Registration support
- Security Measures rate limiting
- Fork the repository
- Create your feature branch (
git checkout -b feature/amazing-feature) - Commit your changes (
git commit -m 'Add some amazing feature') - Push to the branch (
git push origin feature/amazing-feature) - Open a Pull Request
License
This project is licensed under the MIT License - see the LICENSE file for details.
Related Projects
- Django OAuth Toolkit - The base OAuth 2.0 implementation
- oauthlib - The underlying OAuth library
Support
If you encounter any issues or have questions:
- Check the Issues page
- Create a new issue with detailed information
- For general OAuth questions, refer to the Django OAuth Toolkit documentation
Changelog
v0.1.0 (Initial Release)
- Initial implementation of RFC 7591 Dynamic Client Registration
- Support for open registration mode
- Comprehensive client metadata validation
- Integration with Django OAuth Toolkit Application model
Metadata
Release files for django-oauth-toolkit-dcr 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| django_oauth_toolkit_dcr-0.1.0.tar.gz | 8.3 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| django_oauth_toolkit_dcr-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 17.5 kB
Release files / django_oauth_toolkit_dcr-0.1.0.tar.gz
| Download URL | django_oauth_toolkit_dcr-0.1.0.tar.gz |
|---|---|
| Size | 8.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
f5560222fe0e7df95418d57687089ee107434d5d0f4afa3beb81a42934885af5
|
|
BLAKE2b-256 checksum How to use checksums |
d33a15ec2e3bc8666457d332ce3cede59fcc3e15a4a13f4b0367c99b1e9fd2da
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.12.8 Windows/11
|
Release files / django_oauth_toolkit_dcr-0.1.0-py3-none-any.whl
| Download URL | django_oauth_toolkit_dcr-0.1.0-py3-none-any.whl |
|---|---|
| Size | 9.2 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
cc7e45a47b01ef45a2a476279e5ebbbe716f29be7fc96bf8ba16e18c297b81d1
|
|
BLAKE2b-256 checksum How to use checksums |
35206c0fc0663e17e944286d0203b09e3c1084a4eaeaa2ab89b09ef113ac76fb
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
poetry/2.1.3 CPython/3.12.8 Windows/11
|