feat: Dynamic Client Registration Protocol (RFC 7591 / RFC 7592)#1667
Open
zacharypodbela wants to merge 1 commit intodjango-oauth:masterfrom
Open
feat: Dynamic Client Registration Protocol (RFC 7591 / RFC 7592)#1667zacharypodbela wants to merge 1 commit intodjango-oauth:masterfrom
zacharypodbela wants to merge 1 commit intodjango-oauth:masterfrom
Conversation
…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>
Author
|
@dopry could I get a review on this as well? |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
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:
Using
AccessTokenmodel withDCR_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.DCR_REGISTRATION_TOKEN_EXPIRE_SECONDS=None→ year 9999 — defaulting to "never expire" and using year 9999 to satisfy the model's requiredexpiresfield are both judgment calls.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.URL paths
register/andregister/{client_id}/— RFC specifies no URL structure.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 oneauthorization_grant_typecurrently. 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).Default permission class being
IsAuthenticatedDCRPermission— the RFC says thePOST register/endpoint "MAY" be protected; I chose a secure default. The library also providesAllowAllDCRPermissionout of the box, or developers can roll their own Permission class. Easy to customize.Follow same View-layer guard pattern as OIDC, with
DCR_ENABLED=False— New routes are always registered and returned, but DCREnabledMixin returns 404/403 whenDCR_ENABLEDsetting is False.DCR_ENABLEDdefaults 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
POST/o/register/GET/o/register/{client_id}/PUT/o/register/{client_id}/DELETE/o/register/{client_id}/Both endpoints are gated behind a new
DCR_ENABLEDsetting (defaultFalse), so existing installations are unaffected until they opt in.New settings (
OAUTH2_PROVIDER)DCR_ENABLEDFalseTrueto activate the endpointsDCR_REGISTRATION_PERMISSION_CLASSES("oauth2_provider.dcr.IsAuthenticatedDCRPermission",)DCR_REGISTRATION_SCOPE"oauth2_provider:registration"DCR_REGISTRATION_TOKEN_EXPIRE_SECONDSNone(year 9999)DCR_ROTATE_REGISTRATION_TOKEN_ON_UPDATETruePUTBuilt-in permission classes (
oauth2_provider.dcr)IsAuthenticatedDCRPermission— requires Django session authentication (default)AllowAllDCRPermission— open registration, no auth requiredCustom 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 aregistration_access_token(stored as anAccessTokenwithscope = DCR_REGISTRATION_SCOPE) that must be presented as aBearertoken to the management endpoints.Test plan
tests/test_dcr_views.pycovering registration, management (GET/PUT/DELETE), all new settings, and a full register→GET→PUT→DELETE roundtrip