Skip to content

fix(authserver): replace custom JdbcRegisteredClientRepository with Spring stock#130

Merged
dfcoffin merged 1 commit into
mainfrom
feature/issue-127-stock-registered-client-repository
May 26, 2026
Merged

fix(authserver): replace custom JdbcRegisteredClientRepository with Spring stock#130
dfcoffin merged 1 commit into
mainfrom
feature/issue-127-stock-registered-client-repository

Conversation

@dfcoffin
Copy link
Copy Markdown
Contributor

Summary

Closes #127. Replaces the hand-written 335-line JdbcRegisteredClientRepository with Spring Authorization Server's stock implementation. Net +188 / −1045 lines.

The custom repo was the source of three defects surfaced during Phase 2.0 bring-up:

  • The TokenSettings/ClientSettings Jackson serialization bug worked around in #128 (that workaround is removed here along with the custom class — stock uses the right Jackson modules upstream)
  • Auto-encoding of client secrets on save (non-standard; caused the {bcrypt}secret confusion)
  • findAll() returning empty under the autocommit defect fixed in #125

Changes

File Change
AuthorizationServerConfig Construct stock JdbcRegisteredClientRepository(JdbcOperations); initializeDefaultClients() takes the RegisteredClientRepository interface
OAuth2ClientManagementConfig PasswordEncoderPasswordEncoderFactories.createDelegatingPasswordEncoder() (was bare BCryptPasswordEncoder(12))
AuthorizationServerConfig (seeds) Distinct secrets per client: {noop}dc-secret, {noop}tp-secret, {noop}tpadmin-secret
RegisteredClientAdminDao (NEW) Small JdbcTemplate DAO for the two non-interface ops the admin UI needs: findAllClientIds(), deleteById()
OAuthAdminController Uses the admin DAO; dropped instanceof JdbcRegisteredClientRepository branches
custom JdbcRegisteredClientRepository + test Deleted (335 + 559 lines)
4 test files Retargeted to stock class / admin DAO

Two corrections the swap forced (both improvements)

  1. Delegating password encoder. The stock repo stores secrets verbatim (with their {id} prefix) instead of re-encoding on save, so authentication needs a prefix-aware encoder. The previous bare BCryptPasswordEncoder(12) ignored the prefix — which is why {noop}secret failed with invalid_client until this change. The delegating encoder honors {bcrypt} (prod) and {noop} (dev seeds) and bcrypts unprefixed values. This is the Spring Security-recommended default.

  2. Distinct client secrets. Spring's stock repo enforces client-secret uniqueness (a security check the custom repo lacked) — three identical {noop}secret values were rejected with "duplicate client secret." Each seed client now has a distinct secret. The stock repo flagging this is a feature, not a regression.

Verification (local, fresh MySQL container, dev-mysql)

$ curl -u 'data_custodian_admin:dc-secret' \
    -d 'grant_type=client_credentials&scope=DataCustodian_Admin_Access' \
    http://localhost:9999/oauth2/token
{"access_token":"Zcox_I3SWdswVTL_Vp27jt4kWQ2hdextrypIZWCfozynySRN3I0fdIEJuwX8AeKRHz0it1QviyZ40yTOGHjNIHkz_BtKj9yPWcDL2w9o8WoFS6sl_vdgV0F0gXpKI83E",
 "scope":"DataCustodian_Admin_Access","token_type":"Bearer","expires_in":3599}     # 128 chars, 0 dots = opaque

$ curl -u 'data_custodian_admin:dc-secret' -d "token=$T" http://localhost:9999/oauth2/introspect
{"active":true,"sub":"data_custodian_admin","aud":["data_custodian_admin"],"scope":"DataCustodian_Admin_Access",
 "iss":"http://localhost:9999","exp":...,"jti":"...","client_id":"data_custodian_admin","token_type":"Bearer"}

$ curl -u 'data_custodian_admin:WRONG' ... /oauth2/token   ->  HTTP 401   # auth genuinely enforced

Also: Default ESPI Clients seeded: 3 of 3 on boot — all three persist correctly through the stock repo.

Test plan

Pre-existing test debt (NOT introduced by this PR)

AuthorizationServerConfigTest (mock-HttpSecurity unit tests) and OAuthAdminControllerTest's 4 standaloneSetup security tests fail because of how they're written, and the authserver module is excluded from CI entirely (.github/workflows/ci.yml comments out the authserver test step and omits it from the integration/Sonar -pl lists). Both problems — broken tests and their CI exclusion — are documented in #129. This PR leaves them no worse: OAuthAdminControllerTest went 5 → 4 failures (removed one pre-existing failure; dropped 2 obsolete instanceof-fallback tests).

Related

  • Closes #127
  • Advances #122 Phase 2.0
  • Removes the workaround from #128 (Jackson modules on the now-deleted custom repo)
  • Test-infra + CI-exclusion debt tracked in #129

🤖 Generated with Claude Code

…pring stock

Closes #127.

The hand-written JdbcRegisteredClientRepository (335 lines) reimplemented
Spring Authorization Server's stock repository and carried multiple defects:
the TokenSettings/ClientSettings serialization bug worked around in #128,
auto-encoding of client secrets on save, and a findAll() that returned empty
under the autocommit defect fixed in #125. This replaces it with Spring's
stock org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository,
which is maintained upstream and uses the correct Jackson modules out of the box.

Changes:

- AuthorizationServerConfig: construct the stock JdbcRegisteredClientRepository
  (JdbcOperations-only constructor); initializeDefaultClients() now takes the
  RegisteredClientRepository interface and no longer calls the custom findAll().

- OAuth2ClientManagementConfig: PasswordEncoder bean is now
  PasswordEncoderFactories.createDelegatingPasswordEncoder() instead of a bare
  BCryptPasswordEncoder(12). The stock repo stores client secrets verbatim
  (prefix included) rather than re-encoding on save, so authentication needs a
  prefix-aware encoder: {bcrypt}... for production, {noop}... for dev seeds.
  The old bare bcrypt encoder ignored the prefix and is why {noop}secret failed
  with invalid_client until this change.

- Default seed clients use distinct secrets ({noop}dc-secret, {noop}tp-secret,
  {noop}tpadmin-secret). The stock repo enforces client-secret uniqueness
  (a security check the custom repo lacked); three identical {noop}secret values
  were rejected with "duplicate client secret".

- New RegisteredClientAdminDao: small JdbcTemplate-backed component exposing the
  two operations not on the RegisteredClientRepository interface that the admin
  UI needs - findAllClientIds() and deleteById(). OAuthAdminController now
  resolves each id through RegisteredClientRepository.findByClientId(), so
  listing goes through Spring's tested deserialization path.

- OAuthAdminController: dropped the `instanceof JdbcRegisteredClientRepository`
  branches (and their dead fallback paths) in favor of the admin DAO.

- Deleted the custom JdbcRegisteredClientRepository and its 559-line test.
  Updated AuthorizationServerConfigTest (stock class + 1-arg bean signature),
  OAuthAdminControllerTest (admin DAO mocks; removed 2 tests for the now-gone
  instanceof fallback), and the MySQL/PostgreSQL TestContainers tests
  (findAll/deleteById -> RegisteredClientAdminDao).

Verified end-to-end against a fresh MySQL container (dev-mysql):
  POST /oauth2/token (data_custodian_admin:dc-secret, client_credentials)
    -> 200, 128-char opaque token (0 dots, REFERENCE format)
  POST /oauth2/introspect
    -> 200, RFC 7662 response (active, sub, aud, scope, iss, exp, iat, jti,
       client_id, token_type)
  POST /oauth2/token with wrong secret -> 401 (delegating encoder enforces auth)

Net: +112 / -1045 lines.

Pre-existing test debt (NOT introduced here): AuthorizationServerConfigTest's
mock-HttpSecurity unit tests and OAuthAdminControllerTest's standaloneSetup
security tests fail because of how they're written, and the authserver module
is excluded from CI entirely. Both are documented in #129; this change leaves
them no worse (OAuthAdminControllerTest 5 -> 4 failures).

Refs: #122 #128 #129

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@dfcoffin dfcoffin added bug Something isn't working ESPI 4.0 Touches the NAESB ESPI 4.0 implementation labels May 26, 2026
@dfcoffin dfcoffin merged commit 0d8e944 into main May 26, 2026
4 checks passed
@dfcoffin dfcoffin deleted the feature/issue-127-stock-registered-client-repository branch May 26, 2026 03:48
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working ESPI 4.0 Touches the NAESB ESPI 4.0 implementation

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Swap custom JdbcRegisteredClientRepository for Spring's stock implementation

1 participant