You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
The auth-server uses a hand-written JdbcRegisteredClientRepository (openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/repository/JdbcRegisteredClientRepository.java, 335 lines) instead of Spring's stock org.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository. The custom implementation has at least two confirmed defects and is a recurring source of bugs.
Discovered during Phase 2.0 boot verification (#122 defect #8 and during PR #124 verification — see comments on those for the diagnostic trail).
Confirmed defects in the custom implementation
TokenSettings ClassCastException — new ObjectMapper() in the constructor without OAuth2AuthorizationServerJackson2Module registered. OAuth2TokenFormat.REFERENCE serializes to JSON correctly but reads back as LinkedHashMap (line 326: objectMapper.readValue(tokenSettings, new TypeReference<Map<String, Object>>() {})). TokenSettings.withSettings(settings).build() doesn't reconstruct the typed value, so later calls to TokenSettings.getAccessTokenFormat() throw ClassCastException: LinkedHashMap cannot be cast to OAuth2TokenFormat. The JwtGenerator then runs for clients configured as opaque, returning HTTP 500 on every POST /oauth2/token request.
Change seed .clientSecret("{bcrypt}secret") to .clientSecret("{noop}secret") (DelegatingPasswordEncoder treats {noop} prefix as cleartext). Cleartext test credential becomes just secret instead of literal {bcrypt}secret (which is what today's seed effectively created via double-encoding). Production seeds (env-driven) would use pre-bcrypted hashes directly.
Cost of swap
File
Change
AuthorizationServerConfig.java
Change import; change constructor call from new JdbcRegisteredClientRepository(jdbcTemplate, passwordEncoder) to new JdbcRegisteredClientRepository(jdbcTemplate); change initializeDefaultClients parameter type to RegisteredClientRepository; change 3 .clientSecret("{bcrypt}secret") to .clientSecret("{noop}secret")
OAuthAdminController.java
Remove 2 instanceof JdbcRegisteredClientRepository jdbcRepo checks; inject and use new RegisteredClientAdminDao for list/delete
RegisteredClientAdminDao.java (NEW)
Small JdbcTemplate-backed DAO for the 2 non-interface ops
AuthorizationServerConfigTest.java
Update 4 isInstanceOf(JdbcRegisteredClientRepository.class) assertions to use the stock class
OAuthAdminControllerTest.java
Update mocks to reflect new admin DAO injection
MySqlTestcontainersIntegrationTest.java
Update field type
JdbcRegisteredClientRepository.java
DELETE
JdbcRegisteredClientRepositoryTest.java
DELETE
Estimated 2-3 hours for the refactor + regression test.
Why this matters
The custom implementation is a maintenance liability:
Reimplements ~300 lines of Spring code that the Spring Security team already maintains
Uses plain new ObjectMapper() instead of the Jackson modules Spring provides for this exact purpose
Summary
The auth-server uses a hand-written
JdbcRegisteredClientRepository(openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/repository/JdbcRegisteredClientRepository.java, 335 lines) instead of Spring's stockorg.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository. The custom implementation has at least two confirmed defects and is a recurring source of bugs.Discovered during Phase 2.0 boot verification (#122 defect #8 and during PR #124 verification — see comments on those for the diagnostic trail).
Confirmed defects in the custom implementation
TokenSettingsClassCastException —new ObjectMapper()in the constructor withoutOAuth2AuthorizationServerJackson2Moduleregistered.OAuth2TokenFormat.REFERENCEserializes to JSON correctly but reads back asLinkedHashMap(line 326:objectMapper.readValue(tokenSettings, new TypeReference<Map<String, Object>>() {})).TokenSettings.withSettings(settings).build()doesn't reconstruct the typed value, so later calls toTokenSettings.getAccessTokenFormat()throwClassCastException: LinkedHashMap cannot be cast to OAuth2TokenFormat. TheJwtGeneratorthen runs for clients configured as opaque, returning HTTP 500 on everyPOST /oauth2/tokenrequest.SecurityJackson2Modules+OAuth2AuthorizationServerJackson2Moduleon the existing customObjectMapper. Verified to unblock token issuance.findAll()returned empty list while DB had rows. May have been the autocommit defect (fixed in fix(authserver): six pre-existing defects blocking dev-mysql boot #125) manifesting through this code path, or a separate row-mapping bug. Worth re-verifying once stock is in place.What the custom implementation provides beyond stock
findAll()OAuthAdminController.listClients()(GET/clients)JdbcTemplate.query("SELECT * FROM oauth2_registered_client", ...)deleteById(String id)OAuthAdminController.deleteClient()(DELETE/clients/{id})JdbcTemplate.update("DELETE FROM oauth2_registered_client WHERE id = ?", id)passwordEncoder.encode(secret)on saveAuthorizationServerConfig.initializeDefaultClients()seed loop.clientSecret("{bcrypt}secret")to.clientSecret("{noop}secret")(DelegatingPasswordEncoder treats{noop}prefix as cleartext). Cleartext test credential becomes justsecretinstead of literal{bcrypt}secret(which is what today's seed effectively created via double-encoding). Production seeds (env-driven) would use pre-bcrypted hashes directly.Cost of swap
AuthorizationServerConfig.javanew JdbcRegisteredClientRepository(jdbcTemplate, passwordEncoder)tonew JdbcRegisteredClientRepository(jdbcTemplate); changeinitializeDefaultClientsparameter type toRegisteredClientRepository; change 3.clientSecret("{bcrypt}secret")to.clientSecret("{noop}secret")OAuthAdminController.javainstanceof JdbcRegisteredClientRepository jdbcRepochecks; inject and use newRegisteredClientAdminDaofor list/deleteRegisteredClientAdminDao.java(NEW)AuthorizationServerConfigTest.javaisInstanceOf(JdbcRegisteredClientRepository.class)assertions to use the stock classOAuthAdminControllerTest.javaMySqlTestcontainersIntegrationTest.javaJdbcRegisteredClientRepository.javaJdbcRegisteredClientRepositoryTest.javaEstimated 2-3 hours for the refactor + regression test.
Why this matters
The custom implementation is a maintenance liability:
new ObjectMapper()instead of the Jackson modules Spring provides for this exact purpose{bcrypt}secretconfusion that required separate fixes during PR fix(authserver): six pre-existing defects blocking dev-mysql boot #125 verificationAcceptance criteria
openespi-authserver/src/main/java/org/greenbuttonalliance/espi/authserver/repository/JdbcRegisteredClientRepository.javadeletedAuthorizationServerConfig.registeredClientRepository()constructs Spring's stockorg.springframework.security.oauth2.server.authorization.client.JdbcRegisteredClientRepository(JdbcOperations){noop}secretin dev, allowing cleartext basic-auth with secretsecretRegisteredClientAdminDao(or equivalent) providesfindAll()anddeleteById()for theOAuthAdminControlleradmin endpointsPOST /oauth2/token→ 200 with opaque token;POST /oauth2/introspect→ RFC 7662 response. Same as Auth-server SecurityFilterChain: replace non-canonical pattern with Spring Authorization Server canonical config #124 acceptance, but verified against the stock repo.findAll()returning empty when rows exist) re-verified — may already be fixed by autocommit change in fix(authserver): six pre-existing defects blocking dev-mysql boot #125, or by this refactorRelated