From 8eb3b032a4010f7bc75351fb3cc0585c98b2b7b7 Mon Sep 17 00:00:00 2001 From: Vinod Kumar Date: Thu, 11 Jun 2026 19:41:38 +0530 Subject: [PATCH 1/2] Treat empty JWK Set URI as absent Signed-off-by: Vinod Kumar See gh-50755 --- .../autoconfigure/JwkSetUriCondition.java | 45 +++++++++++++++++++ ...2ResourceServerAutoConfigurationTests.java | 12 +++++ 2 files changed, 57 insertions(+) create mode 100644 module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java diff --git a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java new file mode 100644 index 000000000000..5562b2798d60 --- /dev/null +++ b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java @@ -0,0 +1,45 @@ +/* + * Copyright 2012-present 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.boot.security.oauth2.server.resource.autoconfigure; + +import org.springframework.boot.autoconfigure.condition.ConditionMessage; +import org.springframework.boot.autoconfigure.condition.ConditionOutcome; +import org.springframework.boot.autoconfigure.condition.SpringBootCondition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.util.StringUtils; + +/** + * Condition for creating a JWT decoder using a JWK Set URI. + * + * @author Vinod Kumar M + */ +class JwkSetUriCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata) { + ConditionMessage.Builder message = ConditionMessage.forCondition("JWK Set URI Condition"); + Environment environment = context.getEnvironment(); + String jwkSetUri = environment.getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"); + if (!StringUtils.hasText(jwkSetUri)) { + return ConditionOutcome.noMatch(message.didNotFind("jwk-set-uri property").atAll()); + } + return ConditionOutcome.match(message.foundExactly("jwk-set-uri property")); + } + +} \ No newline at end of file diff --git a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java index 556cbf77060f..20624cab6e15 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -362,6 +362,18 @@ void autoConfigurationWhenSetUriKeyLocationAndIssuerUriPresentShouldUseSetUri() assertThat(context.containsBean("jwtDecoderByIssuerUri")).isFalse(); }); } + + @Test + void autoConfigurationWhenIssuerUriPresentAndJwkSetUriEmptyShouldUseIssuerUri() { + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=https://issuer-uri.com", + "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=") + .run((context) -> { + assertThat(context).hasSingleBean(JwtDecoder.class); + assertThat(context.containsBean("jwtDecoderByJwkKeySetUri")).isFalse(); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + } @Test void autoConfigurationWhenKeyLocationAndIssuerUriPresentShouldUseIssuerUri() throws Exception { From a49549203b47bc8a9aa5aa5cb8d9e544539100ff Mon Sep 17 00:00:00 2001 From: Andy Wilkinson Date: Wed, 24 Jun 2026 17:45:32 +0100 Subject: [PATCH 2/2] Polish "Treat empty JWK Set URI as absent" See gh-50755 Signed-off-by: Andy Wilkinson --- .../ConditionalOnJwkSetUriJwtDecoder.java | 42 +++++++++++++++++++ .../autoconfigure/JwkSetUriCondition.java | 4 +- ...eOAuth2ResourceServerJwtConfiguration.java | 3 +- .../OAuth2ResourceServerJwtConfiguration.java | 3 +- ...2ResourceServerAutoConfigurationTests.java | 12 ++++++ ...2ResourceServerAutoConfigurationTests.java | 2 +- 6 files changed, 61 insertions(+), 5 deletions(-) create mode 100644 module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/ConditionalOnJwkSetUriJwtDecoder.java diff --git a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/ConditionalOnJwkSetUriJwtDecoder.java b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/ConditionalOnJwkSetUriJwtDecoder.java new file mode 100644 index 000000000000..c6793b353de5 --- /dev/null +++ b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/ConditionalOnJwkSetUriJwtDecoder.java @@ -0,0 +1,42 @@ +/* + * Copyright 2012-present 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.boot.security.oauth2.server.resource.autoconfigure; + +import java.lang.annotation.Documented; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + +import org.springframework.context.annotation.Conditional; +import org.springframework.security.oauth2.jwt.NimbusJwtDecoder; +import org.springframework.security.oauth2.jwt.NimbusReactiveJwtDecoder; + +/** + * Condition that matches when a JWK Set URI based {@link NimbusJwtDecoder} or + * {@link NimbusReactiveJwtDecoder} should be used. + * + * @author Andy Wilkinson + * @since 4.0.8 + */ +@Retention(RetentionPolicy.RUNTIME) +@Target({ ElementType.TYPE, ElementType.METHOD }) +@Documented +@Conditional(JwkSetUriCondition.class) +public @interface ConditionalOnJwkSetUriJwtDecoder { + +} diff --git a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java index 5562b2798d60..589d0b74b89f 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java +++ b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/JwkSetUriCondition.java @@ -5,7 +5,7 @@ * 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 + * 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, @@ -42,4 +42,4 @@ public ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeM return ConditionOutcome.match(message.foundExactly("jwk-set-uri property")); } -} \ No newline at end of file +} diff --git a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerJwtConfiguration.java b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerJwtConfiguration.java index 9b648e80e90e..9b4adc17474d 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerJwtConfiguration.java +++ b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerJwtConfiguration.java @@ -35,6 +35,7 @@ import org.springframework.boot.context.properties.PropertyMapper; import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnIssuerLocationJwtDecoder; +import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnJwkSetUriJwtDecoder; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnPublicKeyJwtDecoder; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.OAuth2ResourceServerProperties; import org.springframework.context.annotation.Bean; @@ -92,7 +93,7 @@ static class JwtConfiguration { } @Bean - @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") + @ConditionalOnJwkSetUriJwtDecoder ReactiveJwtDecoder jwtDecoder(ObjectProvider customizers) { String jwkSetUri = this.properties.getJwkSetUri(); Assert.state(jwkSetUri != null, "'jwkSetUri' must not be null"); diff --git a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerJwtConfiguration.java b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerJwtConfiguration.java index 3ea6dc80ee5d..2f9e477d8db6 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerJwtConfiguration.java +++ b/module/spring-boot-security-oauth2-resource-server/src/main/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerJwtConfiguration.java @@ -36,6 +36,7 @@ import org.springframework.boot.context.properties.source.InvalidConfigurationPropertyValueException; import org.springframework.boot.security.autoconfigure.web.servlet.ConditionalOnDefaultWebSecurity; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnIssuerLocationJwtDecoder; +import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnJwkSetUriJwtDecoder; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.ConditionalOnPublicKeyJwtDecoder; import org.springframework.boot.security.oauth2.server.resource.autoconfigure.OAuth2ResourceServerProperties; import org.springframework.context.annotation.Bean; @@ -92,7 +93,7 @@ static class JwtDecoderConfiguration { } @Bean - @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") + @ConditionalOnJwkSetUriJwtDecoder JwtDecoder jwtDecoderByJwkKeySetUri(ObjectProvider customizers) { JwkSetUriJwtDecoderBuilder builder = NimbusJwtDecoder.withJwkSetUri(this.properties.getJwkSetUri()) .jwsAlgorithms(this::jwsAlgorithms); diff --git a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java index db204052ccf3..ab737ca6bd7e 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java +++ b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java @@ -444,6 +444,18 @@ void autoConfigurationWhenJwkSetUriAndIntrospectionUriAvailable() { }); } + @Test + void autoConfigurationWhenIssuerUriPresentAndJwkSetUriEmptyShouldUseIssuerUri() { + this.contextRunner + .withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=https://issuer-uri.com", + "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=") + .run((context) -> { + assertThat(context).hasSingleBean(ReactiveJwtDecoder.class); + assertThat(context.containsBean("jwtDecoder")).isFalse(); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + } + @Test void opaqueTokenIntrospectorIsConditionalOnMissingBean() { this.contextRunner diff --git a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java index 20624cab6e15..b6e472d29611 100644 --- a/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/module/spring-boot-security-oauth2-resource-server/src/test/java/org/springframework/boot/security/oauth2/server/resource/autoconfigure/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -362,7 +362,7 @@ void autoConfigurationWhenSetUriKeyLocationAndIssuerUriPresentShouldUseSetUri() assertThat(context.containsBean("jwtDecoderByIssuerUri")).isFalse(); }); } - + @Test void autoConfigurationWhenIssuerUriPresentAndJwkSetUriEmptyShouldUseIssuerUri() { this.contextRunner