An extension to Django OAuth Toolkit that adds support for OAuth 2.0 Dynamic Client Registration as defined in RFC 7591.
- ✅ 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
 
Install the package using pip:
pip install django-oauth-toolkit-dcr- Python 3.10+
 - Django 4.0+
 - django-oauth-toolkit 3.0.1+
 
Add the package to your INSTALLED_APPS:
INSTALLED_APPS = [
    # ... your other apps
    'oauth2_provider',
    'oauth_dcr',
]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'),
]Make sure your Django OAuth Toolkit migrations are up to date:
python manage.py migrateClients 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"]
  }'{
  "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"
}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"
}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.
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)
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
}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,
}| 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 ✅ | 
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
 
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 processedUsing 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):
    passMain view class that handles client registration requests.
post(request)- Handle registration requests_validate_client_metadata(metadata)- Override for custom validation_create_application(metadata)- Override for custom application creation
Run the tests:
pytestContributions 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
 
This project is licensed under the MIT License - see the LICENSE file for details.
- Django OAuth Toolkit - The base OAuth 2.0 implementation
 - oauthlib - The underlying OAuth library
 
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
 
- Initial implementation of RFC 7591 Dynamic Client Registration
 - Support for open registration mode
 - Comprehensive client metadata validation
 - Integration with Django OAuth Toolkit Application model