From a75c0c6500e26782c6b3fc8ec40f60572a5996ad Mon Sep 17 00:00:00 2001 From: Risto Virtanen <818702+mapsu@users.noreply.github.com> Date: Wed, 26 Mar 2025 11:04:34 +0200 Subject: [PATCH 1/3] Add missing ClientAuthenticationMethods to jackson2 converter Closes gh-16825 Signed-off-by: Risto Virtanen <818702+mapsu@users.noreply.github.com> --- .../oauth2/client/jackson2/StdConverters.java | 14 ++- .../ClientRegistrationMixinTests.java | 112 ++++++++++++++++++ 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixinTests.java diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java index 4ce6223cf43..e6ee89668be 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2025 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -56,9 +56,21 @@ public ClientAuthenticationMethod convert(JsonNode jsonNode) { if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equalsIgnoreCase(value)) { return ClientAuthenticationMethod.CLIENT_SECRET_POST; } + if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equalsIgnoreCase(value)) { + return ClientAuthenticationMethod.CLIENT_SECRET_JWT; + } + if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equalsIgnoreCase(value)) { + return ClientAuthenticationMethod.PRIVATE_KEY_JWT; + } if (ClientAuthenticationMethod.NONE.getValue().equalsIgnoreCase(value)) { return ClientAuthenticationMethod.NONE; } + if (ClientAuthenticationMethod.TLS_CLIENT_AUTH.getValue().equalsIgnoreCase(value)) { + return ClientAuthenticationMethod.TLS_CLIENT_AUTH; + } + if (ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH.getValue().equalsIgnoreCase(value)) { + return ClientAuthenticationMethod.SELF_SIGNED_TLS_CLIENT_AUTH; + } return null; } diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixinTests.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixinTests.java new file mode 100644 index 00000000000..693abe7f46e --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixinTests.java @@ -0,0 +1,112 @@ +/* + * Copyright 2002-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.client.jackson2; + +import com.fasterxml.jackson.databind.ObjectMapper; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.security.jackson2.SecurityJackson2Modules; +import org.springframework.security.oauth2.client.registration.ClientRegistration; +import org.springframework.security.oauth2.client.registration.TestClientRegistrations; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; + +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; + +public class ClientRegistrationMixinTests { + + private ObjectMapper mapper; + + @BeforeEach + void setUp() { + ClassLoader loader = getClass().getClassLoader(); + this.mapper = new ObjectMapper(); + this.mapper.registerModules(SecurityJackson2Modules.getModules(loader)); + } + + @ParameterizedTest + @MethodSource("deserializeWhenMixinRegisteredThenDeserializes") + void deserializeWhenMixinRegisteredThenDeserializes( + ClientRegistration expectedClientRegistration + ) throws Exception { + String json = asJson(expectedClientRegistration); + ClientRegistration clientRegistration = this.mapper.readValue(json, ClientRegistration.class); + assertThat(clientRegistration.getClientAuthenticationMethod()).isEqualTo(expectedClientRegistration.getClientAuthenticationMethod()); + } + + private String asJson(ClientRegistration expectedClientRegistration) { + // @formatter:off + return "{" + + " \"@class\":\"org.springframework.security.oauth2.client.registration.ClientRegistration\"," + + " \"registrationId\":\"registration-id\"," + + " \"clientId\":\"client-id\"," + + " \"clientSecret\":\"client-secret\"," + + " \"clientAuthenticationMethod\":{" + + " \"value\":\"" + expectedClientRegistration.getClientAuthenticationMethod().getValue() + "\"" + + " }," + + " \"authorizationGrantType\":{" + + " \"value\":\"" + expectedClientRegistration.getAuthorizationGrantType().getValue() + "\"" + + " }," + + " \"redirectUri\":\"{baseUrl}/{action}/oauth2/code/{registrationId}\"," + + " \"scopes\":[" + + " \"java.util.Collections$UnmodifiableSet\",[\"read:user\"]" + + " ]," + + " \"providerDetails\":{" + + " \"@class\":\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails\"," + + " \"authorizationUri\":\"https://example.com/login/oauth/authorize\"," + + " \"tokenUri\": \"https://example.com/login/oauth/access_token\"," + + " \"userInfoEndpoint\":{" + + " \"@class\":\"org.springframework.security.oauth2.client.registration.ClientRegistration$ProviderDetails$UserInfoEndpoint\"," + + " \"uri\":\"https://api.example.com/user\"," + + " \"authenticationMethod\":{" + + " \"value\":\"header\"" + + " }," + + " \"userNameAttributeName\":\"id\"" + + " }," + + " \"jwkSetUri\":\"https://example.com/oauth2/jwk\"," + + " \"issuerUri\":\"https://example.com\"," + + " \"configurationMetadata\":{" + + " \"@class\":\"java.util.Collections$UnmodifiableMap\"" + + " }" + + " }," + + " \"clientName\":\"Client Name\"}"; + // @formatter:on + } + + static Stream deserializeWhenMixinRegisteredThenDeserializes() { + return Stream.of( + Arguments.of( + TestClientRegistrations.clientRegistration() + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .build() + ), + Arguments.of( + TestClientRegistrations.clientRegistration() + .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) + .build() + ), + Arguments.of( + TestClientRegistrations.clientRegistration() + .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) + .build() + ) + ); + } +} From 52d931c72e56e396c070e264e7922b30615c7d32 Mon Sep 17 00:00:00 2001 From: Risto Virtanen <818702+mapsu@users.noreply.github.com> Date: Fri, 4 Apr 2025 21:24:50 +0300 Subject: [PATCH 2/3] Replace ClientRegistrationMixinTests with StdConvertersTest Signed-off-by: Risto Virtanen <818702+mapsu@users.noreply.github.com> --- .../oauth2/client/jackson2/StdConverters.java | 23 +--- .../ClientRegistrationMixinTests.java | 112 ------------------ .../client/jackson2/StdConvertersTest.java | 56 +++++++++ 3 files changed, 57 insertions(+), 134 deletions(-) delete mode 100644 oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/ClientRegistrationMixinTests.java create mode 100644 oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java diff --git a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java index e6ee89668be..b544f267933 100644 --- a/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java +++ b/oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/jackson2/StdConverters.java @@ -50,28 +50,7 @@ static final class ClientAuthenticationMethodConverter extends StdConverter deserializeWhenMixinRegisteredThenDeserializes() { - return Stream.of( - Arguments.of( - TestClientRegistrations.clientRegistration() - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .build() - ), - Arguments.of( - TestClientRegistrations.clientRegistration() - .clientAuthenticationMethod(ClientAuthenticationMethod.PRIVATE_KEY_JWT) - .build() - ), - Arguments.of( - TestClientRegistrations.clientRegistration() - .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) - .build() - ) - ); - } -} diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java new file mode 100644 index 00000000000..428413e1541 --- /dev/null +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java @@ -0,0 +1,56 @@ +/* + * Copyright 2002-2025 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.security.oauth2.client.jackson2; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.JsonNodeFactory; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.fasterxml.jackson.databind.util.StdConverter; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.Arguments; +import org.junit.jupiter.params.provider.MethodSource; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; + +import java.util.stream.Stream; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +public class StdConvertersTest { + + private final StdConverter clientAuthenticationMethodConverter = + new StdConverters.ClientAuthenticationMethodConverter(); + + @ParameterizedTest + @MethodSource("testClientAuthenticationMethodConverting") + void testClientAuthenticationMethodConverting(String clientAuthenticationMethod) { + ObjectNode jsonNode = JsonNodeFactory.instance.objectNode(); + jsonNode.put("value", clientAuthenticationMethod); + ClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode); + assertEquals(clientAuthenticationMethod, actual.getValue()); + + } + + static Stream testClientAuthenticationMethodConverting() { + return Stream.of( + Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()), + Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()), + Arguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()), + Arguments.of(ClientAuthenticationMethod.NONE.getValue()), + Arguments.of("custom_method") + ); + } +} From 28c8bd204a9084547ff0c653b727d63701911d6a Mon Sep 17 00:00:00 2001 From: Risto Virtanen <818702+mapsu@users.noreply.github.com> Date: Tue, 8 Apr 2025 19:52:57 +0300 Subject: [PATCH 3/3] Formatted Signed-off-by: Risto Virtanen <818702+mapsu@users.noreply.github.com> --- ...rtersTest.java => StdConvertersTests.java} | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) rename oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/{StdConvertersTest.java => StdConvertersTests.java} (69%) diff --git a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTests.java similarity index 69% rename from oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java rename to oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTests.java index 428413e1541..2cee9a1b540 100644 --- a/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTest.java +++ b/oauth2/oauth2-client/src/test/java/org/springframework/security/oauth2/client/jackson2/StdConvertersTests.java @@ -16,6 +16,8 @@ package org.springframework.security.oauth2.client.jackson2; +import java.util.stream.Stream; + import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import com.fasterxml.jackson.databind.node.ObjectNode; @@ -23,34 +25,29 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.security.oauth2.core.ClientAuthenticationMethod; -import java.util.stream.Stream; +import org.springframework.security.oauth2.core.ClientAuthenticationMethod; -import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.assertj.core.api.Assertions.assertThat; -public class StdConvertersTest { +public class StdConvertersTests { - private final StdConverter clientAuthenticationMethodConverter = - new StdConverters.ClientAuthenticationMethodConverter(); + private final StdConverter clientAuthenticationMethodConverter = new StdConverters.ClientAuthenticationMethodConverter(); @ParameterizedTest - @MethodSource("testClientAuthenticationMethodConverting") - void testClientAuthenticationMethodConverting(String clientAuthenticationMethod) { + @MethodSource("convertWhenClientAuthenticationMethodConvertedThenDeserializes") + void convertWhenClientAuthenticationMethodConvertedThenDeserializes(String clientAuthenticationMethod) { ObjectNode jsonNode = JsonNodeFactory.instance.objectNode(); jsonNode.put("value", clientAuthenticationMethod); ClientAuthenticationMethod actual = this.clientAuthenticationMethodConverter.convert(jsonNode); - assertEquals(clientAuthenticationMethod, actual.getValue()); - + assertThat(actual.getValue()).isEqualTo(clientAuthenticationMethod); } - static Stream testClientAuthenticationMethodConverting() { - return Stream.of( - Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()), + static Stream convertWhenClientAuthenticationMethodConvertedThenDeserializes() { + return Stream.of(Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()), Arguments.of(ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue()), Arguments.of(ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue()), - Arguments.of(ClientAuthenticationMethod.NONE.getValue()), - Arguments.of("custom_method") - ); + Arguments.of(ClientAuthenticationMethod.NONE.getValue()), Arguments.of("custom_method")); } + }