diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java index 558a4ae1..32e2442b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java @@ -100,6 +100,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor + " WHERE " + PK_FILTER; // @formatter:on + private static final String COUNT_REGISTERED_CLIENT_SQL = "SELECT COUNT(*) FROM " + TABLE_NAME + " WHERE "; + private final JdbcOperations jdbcOperations; private RowMapper registeredClientRowMapper; private Function> registeredClientParametersMapper; @@ -141,11 +143,31 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor } private void insertRegisteredClient(RegisteredClient registeredClient) { + assertUniqueIdentifiers(registeredClient); List parameters = this.registeredClientParametersMapper.apply(registeredClient); PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters.toArray()); this.jdbcOperations.update(INSERT_REGISTERED_CLIENT_SQL, pss); } + private void assertUniqueIdentifiers(RegisteredClient registeredClient) { + Integer count = this.jdbcOperations.queryForObject( + COUNT_REGISTERED_CLIENT_SQL + "client_id = ?", + Integer.class, + registeredClient.getClientId()); + if (count != null && count > 0) { + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client identifier: " + registeredClient.getClientId()); + } + count = this.jdbcOperations.queryForObject( + COUNT_REGISTERED_CLIENT_SQL + "client_secret = ?", + Integer.class, + registeredClient.getClientSecret()); + if (count != null && count > 0) { + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client secret for identifier: " + registeredClient.getId()); + } + } + @Override public RegisteredClient findById(String id) { Assert.hasText(id, "id cannot be empty"); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java index 24299967..e11e87e3 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java @@ -162,6 +162,40 @@ public class JdbcRegisteredClientRepositoryTests { assertThat(registeredClient).isEqualTo(expectedRegisteredClient); } + @Test + public void saveWhenExistingClientIdThenThrowIllegalArgumentException() { + RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient() + .id("registration-1") + .clientId("client-1") + .build(); + this.registeredClientRepository.save(registeredClient1); + RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() + .id("registration-2") + .clientId("client-1") + .build(); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) + .withMessage("Registered client must be unique. Found duplicate client identifier: " + registeredClient2.getClientId()); + } + + @Test + public void saveWhenExistingClientSecretThenThrowIllegalArgumentException() { + RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient() + .id("registration-1") + .clientId("client-1") + .clientSecret("secret") + .build(); + this.registeredClientRepository.save(registeredClient1); + RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() + .id("registration-2") + .clientId("client-2") + .clientSecret("secret") + .build(); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) + .withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + registeredClient2.getId()); + } + @Test public void saveLoadRegisteredClientWhenCustomStrategiesSetThenCalled() throws Exception { RowMapper registeredClientRowMapper = spy(new RegisteredClientRowMapper()); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java index aafd01d3..bf2c2640 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java @@ -31,7 +31,7 @@ public class TestRegisteredClients { return RegisteredClient.withId("registration-1") .clientId("client-1") .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) - .clientSecret("secret") + .clientSecret("secret-1") .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) @@ -43,7 +43,7 @@ public class TestRegisteredClients { return RegisteredClient.withId("registration-2") .clientId("client-2") .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) - .clientSecret("secret") + .clientSecret("secret-2") .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS)