Skip to content

fix: enforce DCR registration policy for device clients [DHIS2-19948] - #25470

Merged
netroms merged 2 commits into
masterfrom
DHIS2-19948_master
Oct 7, 2026
Merged

netroms merged 2 commits into
masterfrom
DHIS2-19948_master

Conversation

@netroms

@netroms netroms commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Clients registered through OAuth2 Dynamic Client Registration (POST /connect/register) must
match the DHIS2 Android device profile.

Registration policy

DcrRegistrationPolicyValidator runs after Spring Authorization Server's default registration
validator and requires:

  • redirect_uris: exactly the redirect URI of the initial access token (redirect_url claim)
  • grant_types: authorization_code, optionally refresh_token
  • response_types: code
  • token_endpoint_auth_method: private_key_jwt
  • no post_logout_redirect_uris
  • the subject of the initial access token is an active user

Other registrations get an RFC 7591 error (invalid_client_metadata / invalid_redirect_uri,
invalid_token for an inactive subject) and nothing is persisted. The DHIS2 Android SDK
registration request matches this profile.

Also:

  • Dhis2OAuth2ClientServiceImpl.save(RegisteredClient) applies the grant-type and scope rules and
    the active-subject check to clients saved with an initial access token.
  • OAuth2Constants.IAT_REDIRECT_URL_CLAIM is shared by the code that issues and checks initial
    access tokens.

Tests

DcrControllerTest.java (H2, through the Spring Authorization Server filter chain; the class in
that file is DcrWithJwksTest on 2.42):

  • registrations outside the profile are rejected: grant types other than authorization_code
    (+ refresh_token), redirect URIs other than the IAT redirect, client authentication other than
    private_key_jwt (including none given), post-logout redirect URIs
  • an IAT whose subject is disabled, expired or renamed is rejected and nothing is persisted
  • a rejected registration leaves the IAT usable for a conforming retry
  • a user without OAuth2 client authorities enrolls through /api/auth/enrollDevice and registers
    the device client
  • client_credentials tokens carry no username claim and are not accepted for API requests
  • an IAT-authenticated save() refuses a client_credentials client
  • the device-profile client refreshes tokens with private_key_jwt and calls the API
  • existing fixtures register the device profile instead of client_credentials

Results on the PR heads, org.hisp.dhis.webapi.controller.security package: master 91/91,
2.43 88/88, 2.42 88/88. spotless:check and dependency:analyze-only pass on all three.

Related PRs

Same change on the other lines:

Clients registered through OAuth2 Dynamic Client Registration
(/connect/register) must match the DHIS2 Android device profile.

- Add DcrRegistrationPolicyValidator, chained after Spring Authorization
  Server's default registration validator. A registration must use the
  redirect URI of its initial access token, the authorization_code grant
  (optionally refresh_token), response type code and private_key_jwt
  client authentication, without post-logout redirect URIs. The subject of
  the initial access token must be an active user.
- Dhis2OAuth2ClientServiceImpl.save(RegisteredClient) applies the
  grant-type and scope rules and the active-subject check to clients saved
  with an initial access token.
- The token customizer no longer adds a username claim to
  client_credentials tokens.
- Add IAT_REDIRECT_URL_CLAIM to OAuth2Constants, used when issuing and
  checking initial access tokens.
@sonarqubecloud

sonarqubecloud Bot commented Oct 2, 2026

Copy link
Copy Markdown

@netroms
netroms enabled auto-merge (squash) October 7, 2026 12:41
@netroms
netroms merged commit 9e08b6a into master Oct 7, 2026
25 checks passed
@netroms
netroms deleted the DHIS2-19948_master branch October 7, 2026 14:48
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.

3 participants