Skip to content

feat: Dynamic Client Registration Protocol (RFC 7591 / RFC 7592)#1667

Open
zacharypodbela wants to merge 1 commit intodjango-oauth:masterfrom
ForaTravel:feature/dynamic-client-registration
Open

feat: Dynamic Client Registration Protocol (RFC 7591 / RFC 7592)#1667
zacharypodbela wants to merge 1 commit intodjango-oauth:masterfrom
ForaTravel:feature/dynamic-client-registration

Conversation

@zacharypodbela
Copy link

@zacharypodbela zacharypodbela commented Mar 5, 2026

Summary

Implements the OAuth 2.0 Dynamic Client Registration Protocol (RFC 7591) and the Dynamic Client Registration Management Protocol (RFC 7592), allowing clients to programmatically register without manual admin action.

Closes #670

Implementation

Most of the implementation details stem directly from RFC requirements.

Here are the design decisions I made that were not directly specified by the RFCs. Whenever possible I created settings that allow developers to adjust in case they want different behavior:

  1. Using AccessToken model with DCR_REGISTRATION_SCOPE="oauth2_provider:registration" for the registration_access_token — RFC 7592 just says "issue a token"; using the existing model is my implementation choice. The default scope having oauth2_provider namespace should be unique enough to avoid collisions, but developers are free to override if needed/desired.

  2. DCR_REGISTRATION_TOKEN_EXPIRE_SECONDS=None → year 9999 — defaulting to "never expire" and using year 9999 to satisfy the model's required expires field are both judgment calls.

  3. DCR_ROTATE_REGISTRATION_TOKEN_ON_UPDATE=True — RFC 7592 says the server "MAY" issue a new registration access token after a PUT. I choose a default of always rotating as theres no downside, but developers are free to configure otherwise.

  4. URL paths register/ and register/{client_id}/ — RFC specifies no URL structure.

  5. Throw 400 if the client submits multiple grant types — RFC 7591 allows clients to register with an array of grant types, e.g.: {"grant_types": ["authorization_code", "refresh_token", "client_credentials"]}. DOT's Application model only supports one authorization_grant_type currently. Rather than altering the model, we'll just have to not comply with this requirement for now and we'll throw 400 if client tries to submit multiple (better to provide clients with clarity than silently fail and choose one of the submitted).

  6. Default permission class being IsAuthenticatedDCRPermission — the RFC says the POST register/ endpoint "MAY" be protected; I chose a secure default. The library also provides AllowAllDCRPermission out of the box, or developers can roll their own Permission class. Easy to customize.

  7. Follow same View-layer guard pattern as OIDC, with DCR_ENABLED=False — New routes are always registered and returned, but DCREnabledMixin returns 404/403 when DCR_ENABLED setting is False. DCR_ENABLED defaults to False so that DCR is not automatically enabled for current package users. This is the safest non-breaking approach that follows the existing paradigms.

New endpoints

Method URL RFC
POST /o/register/ RFC 7591 — create a new application
GET /o/register/{client_id}/ RFC 7592 — read current configuration
PUT /o/register/{client_id}/ RFC 7592 — update configuration
DELETE /o/register/{client_id}/ RFC 7592 — delete application

Both endpoints are gated behind a new DCR_ENABLED setting (default False), so existing installations are unaffected until they opt in.

New settings (OAUTH2_PROVIDER)

Setting Default Description
DCR_ENABLED False Must be True to activate the endpoints
DCR_REGISTRATION_PERMISSION_CLASSES ("oauth2_provider.dcr.IsAuthenticatedDCRPermission",) Controls who may register clients
DCR_REGISTRATION_SCOPE "oauth2_provider:registration" Scope on the RFC 7592 management token
DCR_REGISTRATION_TOKEN_EXPIRE_SECONDS None (year 9999) Expiry of the registration access token
DCR_ROTATE_REGISTRATION_TOKEN_ON_UPDATE True Issue a new management token on PUT

Built-in permission classes (oauth2_provider.dcr)

  • IsAuthenticatedDCRPermission — requires Django session authentication (default)
  • AllowAllDCRPermission — open registration, no auth required

Custom classes only need to implement has_permission(request) -> bool, making it straightforward to support initial-access tokens or any other scheme.

RFC 7592 management token

After a successful POST /register/, the response includes a registration_access_token (stored as an AccessToken with scope = DCR_REGISTRATION_SCOPE) that must be presented as a Bearer token to the management endpoints.

Test plan

  • 27 new tests in tests/test_dcr_views.py covering registration, management (GET/PUT/DELETE), all new settings, and a full register→GET→PUT→DELETE roundtrip
  • Full existing test suite passes

…o-oauth#670)

Implements OAuth 2.0 Dynamic Client Registration (RFC 7591) and the
Dynamic Client Registration Management Protocol (RFC 7592) via two
new views, configurable permission classes, and four new settings.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@zacharypodbela
Copy link
Author

@dopry could I get a review on this as well?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Implement RFC 7591 and 7592 (dynamic client registration)

1 participant