From 0c299bbc1f786601a1cea6f2a1b9e36e923e688e Mon Sep 17 00:00:00 2001 From: artsiom Date: Thu, 23 Aug 2018 22:16:27 +0300 Subject: [PATCH 1/2] Support OIDC issuer uri in OAuth resource server config --- .../OAuth2ResourceServerProperties.java | 14 +++ .../resource/OidcIssuerLocationCondition.java | 43 +++++++ .../OAuth2ResourceServerJwkConfiguration.java | 15 ++- ...2ResourceServerAutoConfigurationTests.java | 114 ++++++++++++++++++ .../appendix-application-properties.adoc | 1 + 5 files changed, 186 insertions(+), 1 deletion(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java index 4f6b10721f..21f97c566a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java @@ -21,6 +21,7 @@ import org.springframework.boot.context.properties.ConfigurationProperties; * OAuth 2.0 resource server properties. * * @author Madhura Bhave + * @author Artsiom Yudovin * @since 2.1.0 */ @ConfigurationProperties(prefix = "spring.security.oauth2.resourceserver") @@ -39,6 +40,11 @@ public class OAuth2ResourceServerProperties { */ private String jwkSetUri; + /** + * Oidc issuer location. + */ + private String oidcIssuerLocation; + public String getJwkSetUri() { return this.jwkSetUri; } @@ -47,6 +53,14 @@ public class OAuth2ResourceServerProperties { this.jwkSetUri = jwkSetUri; } + public String getOidcIssuerLocation() { + return this.oidcIssuerLocation; + } + + public void setOidcIssuerLocation(String oidcIssuerLocation) { + this.oidcIssuerLocation = oidcIssuerLocation; + } + } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java new file mode 100644 index 0000000000..ae2e91c0cb --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java @@ -0,0 +1,43 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.autoconfigure.security.oauth2.resource; + +import org.springframework.context.annotation.Condition; +import org.springframework.context.annotation.ConditionContext; +import org.springframework.core.env.Environment; +import org.springframework.core.type.AnnotatedTypeMetadata; +import org.springframework.security.oauth2.jwt.JwtDecoder; + +/** + * Condition for creating {@link JwtDecoder} by oidc issuer location. + * + * @author Artsiom Yudovin + */ +public class OidcIssuerLocationCondition implements Condition { + + private static final String OIDC_ISSUER_LOCATION = "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location"; + + private static final String JWT_SET_URI = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri"; + + @Override + public boolean matches(ConditionContext conditionContext, + AnnotatedTypeMetadata annotatedTypeMetadata) { + Environment environment = conditionContext.getEnvironment(); + return environment.containsProperty(OIDC_ISSUER_LOCATION) + && !environment.containsProperty(JWT_SET_URI); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java index 7c32288488..f2ddd42579 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java @@ -18,15 +18,20 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; +import org.springframework.boot.autoconfigure.security.oauth2.resource.OidcIssuerLocationCondition; import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; import org.springframework.security.oauth2.jwt.JwtDecoder; +import org.springframework.security.oauth2.jwt.JwtDecoders; import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; /** - * Configures a {@link JwtDecoder} when a JWK Set URI is available. + * Configures a {@link JwtDecoder} when a JWK Set URI is available or Oidc Issuer + * Location. * * @author Madhura Bhave + * @author Artsiom Yudovin */ @Configuration class OAuth2ResourceServerJwkConfiguration { @@ -44,4 +49,12 @@ class OAuth2ResourceServerJwkConfiguration { return new NimbusJwtDecoderJwkSupport(this.properties.getJwt().getJwkSetUri()); } + @Bean + @Conditional(OidcIssuerLocationCondition.class) + @ConditionalOnMissingBean + public JwtDecoder jwtDecoderByOidcIssuerLocation() { + return JwtDecoders + .fromOidcIssuerLocation(this.properties.getJwt().getOidcIssuerLocation()); + } + } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java index f4da36bf59..e248b08bbf 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -15,11 +15,19 @@ */ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; +import java.util.Collections; +import java.util.HashMap; import java.util.List; +import java.util.Map; import javax.servlet.Filter; +import okhttp3.mockwebserver.MockResponse; +import okhttp3.mockwebserver.MockWebServer; +import org.junit.After; import org.junit.Test; +import org.testcontainers.shaded.com.fasterxml.jackson.core.JsonProcessingException; +import org.testcontainers.shaded.com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.boot.autoconfigure.AutoConfigurations; import org.springframework.boot.test.context.FilteredClassLoader; @@ -27,6 +35,9 @@ import org.springframework.boot.test.context.assertj.AssertableWebApplicationCon import org.springframework.boot.test.context.runner.WebApplicationContextRunner; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpStatus; +import org.springframework.http.MediaType; import org.springframework.security.config.BeanIds; import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; import org.springframework.security.oauth2.jwt.JwtDecoder; @@ -44,6 +55,7 @@ import static org.mockito.Mockito.mock; * Tests for {@link OAuth2ResourceServerAutoConfiguration}. * * @author Madhura Bhave + * @author Artsiom Yudovin */ public class OAuth2ResourceServerAutoConfigurationTests { @@ -52,6 +64,15 @@ public class OAuth2ResourceServerAutoConfigurationTests { AutoConfigurations.of(OAuth2ResourceServerAutoConfiguration.class)) .withUserConfiguration(TestConfig.class); + private MockWebServer server; + + @After + public void cleanup() throws Exception { + if (this.server != null) { + this.server.shutdown(); + } + } + @Test public void autoConfigurationShouldConfigureResourceServer() { this.contextRunner.withPropertyValues( @@ -63,6 +84,46 @@ public class OAuth2ResourceServerAutoConfigurationTests { }); } + @Test + public void autoConfigurationShouldConfigureResourceServerOidcIssuerLocation() + throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String issuer = this.server.url("").toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponse(cleanIssuerPath); + this.contextRunner.withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" + + this.server.getHostName() + ":" + this.server.getPort()) + .run((context) -> { + assertThat(context.getBean(JwtDecoder.class)) + .isInstanceOf(NimbusJwtDecoderJwkSupport.class); + assertThat(getBearerTokenFilter(context)).isNotNull(); + }); + } + + @Test + public void autoConfigurationShouldConfigureSetUriWithTwoProperties() + throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String issuer = this.server.url("").toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponse(cleanIssuerPath); + this.contextRunner.withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" + + this.server.getHostName() + ":" + this.server.getPort(), + "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com") + .run((context) -> { + assertThat(context.getBean(JwtDecoder.class)) + .isInstanceOf(NimbusJwtDecoderJwkSupport.class); + assertThat(getBearerTokenFilter(context)).isNotNull(); + assertThat(context.containsBean("jwtDecoder")).isTrue(); + assertThat(context.containsBean("jwtDecoderByOidcIssuerLocation")) + .isFalse(); + }); + } + @Test public void autoConfigurationWhenJwkSetUriNullShouldNotFail() { this.contextRunner @@ -77,6 +138,14 @@ public class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull()); } + @Test + public void jwtDecoderBeanIsConditionalOnMissingBeanOidcIssuerLocation() { + this.contextRunner.withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://jwk-oidc-issuer-location.com") + .withUserConfiguration(JwtDecoderConfig.class) + .run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull()); + } + @Test public void autoConfigurationShouldBeConditionalOnJwtAuthenticationTokenClass() { this.contextRunner.withPropertyValues( @@ -86,6 +155,15 @@ public class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); } + @Test + public void autoConfigurationShouldBeConditionalOnJwtAuthenticationTokenClassOidcIssuerLocation() { + this.contextRunner.withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://jwk-oidc-issuer-location.com") + .withUserConfiguration(JwtDecoderConfig.class) + .withClassLoader(new FilteredClassLoader(JwtAuthenticationToken.class)) + .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); + } + @SuppressWarnings("unchecked") private Filter getBearerTokenFilter(AssertableWebApplicationContext context) { FilterChainProxy filterChain = (FilterChainProxy) context @@ -98,6 +176,42 @@ public class OAuth2ResourceServerAutoConfigurationTests { .orElse(null); } + private String cleanIssuerPath(String issuer) { + if (issuer.endsWith("/")) { + return issuer.substring(0, issuer.length() - 1); + } + return issuer; + } + + private void setupMockResponse(String issuer) throws JsonProcessingException { + MockResponse mockResponse = new MockResponse() + .setResponseCode(HttpStatus.OK.value()) + .setBody(new ObjectMapper().writeValueAsString(getResponse(issuer))) + .setHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE); + this.server.enqueue(mockResponse); + } + + private Map getResponse(String issuer) { + Map response = new HashMap<>(); + response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth"); + response.put("claims_supported", Collections.emptyList()); + response.put("code_challenge_methods_supported", Collections.emptyList()); + response.put("id_token_signing_alg_values_supported", Collections.emptyList()); + response.put("issuer", issuer); + response.put("jwks_uri", "https://example.com/oauth2/v3/certs"); + response.put("response_types_supported", Collections.emptyList()); + response.put("revocation_endpoint", "https://example.com/o/oauth2/revoke"); + response.put("scopes_supported", Collections.singletonList("openid")); + response.put("subject_types_supported", Collections.singletonList("public")); + response.put("grant_types_supported", + Collections.singletonList("authorization_code")); + response.put("token_endpoint", "https://example.com/oauth2/v4/token"); + response.put("token_endpoint_auth_methods_supported", + Collections.singletonList("client_secret_basic")); + response.put("userinfo_endpoint", "https://example.com/oauth2/v3/userinfo"); + return response; + } + @Configuration @EnableWebSecurity static class TestConfig { diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 10466330bc..0e6fa71102 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -539,6 +539,7 @@ content into your application. Rather, pick only the properties that you need. # SECURITY OAUTH2 RESOURCE SERVER ({sc-spring-boot-autoconfigure}/security/oauth2/resource/OAuth2ResourceServerProperties.{sc-ext}[OAuth2ResourceServerProperties]) spring.security.oauth2.resourceserver.jwt.jwk-set-uri= # JSON Web Key URI to use to verify the JWT token. + spring.security.oauth2.resource.jwt.oidc-issuer-location= # Location for issuer oidc # ---------------------------------------- # DATA PROPERTIES From cf31325e5d969ae5ac38e814a9137030b650468f Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Fri, 24 Aug 2018 14:57:45 -0700 Subject: [PATCH 2/2] Polish "OIDC issuer uri in OAuth resource server config" Closes gh-14190 --- .../oauth2/resource/IssuerUriCondition.java | 56 +++++++++++++++++++ .../OAuth2ResourceServerProperties.java | 12 ++-- .../resource/OidcIssuerLocationCondition.java | 43 -------------- .../OAuth2ResourceServerJwkConfiguration.java | 14 ++--- ...2ResourceServerAutoConfigurationTests.java | 39 ++++--------- .../appendix-application-properties.adoc | 2 +- .../main/asciidoc/spring-boot-features.adoc | 9 ++- 7 files changed, 89 insertions(+), 86 deletions(-) create mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java delete mode 100644 spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java new file mode 100644 index 0000000000..0f08507f8b --- /dev/null +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/IssuerUriCondition.java @@ -0,0 +1,56 @@ +/* + * Copyright 2012-2018 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 + * + * http://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.autoconfigure.security.oauth2.resource; + +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.security.oauth2.jwt.JwtDecoder; +import org.springframework.util.StringUtils; + +/** + * Condition for creating {@link JwtDecoder} by oidc issuer location. + * + * @author Artsiom Yudovin + * @since 2.1.0 + */ +public class IssuerUriCondition extends SpringBootCondition { + + @Override + public ConditionOutcome getMatchOutcome(ConditionContext context, + AnnotatedTypeMetadata metadata) { + ConditionMessage.Builder message = ConditionMessage + .forCondition("OpenID Connect Issuer URI Condition"); + Environment environment = context.getEnvironment(); + String issuerUri = environment + .getProperty("spring.security.oauth2.resourceserver.jwt.issuer-uri"); + String jwkSetUri = environment + .getProperty("spring.security.oauth2.resourceserver.jwt.jwk-set-uri"); + if (!StringUtils.hasText(issuerUri)) { + return ConditionOutcome + .noMatch(message.didNotFind("issuer-uri property").atAll()); + } + if (StringUtils.hasText(jwkSetUri)) { + return ConditionOutcome + .noMatch(message.found("jwk-set-uri property").items(jwkSetUri)); + } + return ConditionOutcome.match(message.foundExactly("issuer-uri property")); + } + +} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java index 21f97c566a..6df4452a4b 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java @@ -41,9 +41,9 @@ public class OAuth2ResourceServerProperties { private String jwkSetUri; /** - * Oidc issuer location. + * URI that an OpenID Connect Provider asserts as its Issuer Identifier. */ - private String oidcIssuerLocation; + private String issuerUri; public String getJwkSetUri() { return this.jwkSetUri; @@ -53,12 +53,12 @@ public class OAuth2ResourceServerProperties { this.jwkSetUri = jwkSetUri; } - public String getOidcIssuerLocation() { - return this.oidcIssuerLocation; + public String getIssuerUri() { + return this.issuerUri; } - public void setOidcIssuerLocation(String oidcIssuerLocation) { - this.oidcIssuerLocation = oidcIssuerLocation; + public void setIssuerUri(String issuerUri) { + this.issuerUri = issuerUri; } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java deleted file mode 100644 index ae2e91c0cb..0000000000 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OidcIssuerLocationCondition.java +++ /dev/null @@ -1,43 +0,0 @@ -/* - * Copyright 2012-2018 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 - * - * http://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.autoconfigure.security.oauth2.resource; - -import org.springframework.context.annotation.Condition; -import org.springframework.context.annotation.ConditionContext; -import org.springframework.core.env.Environment; -import org.springframework.core.type.AnnotatedTypeMetadata; -import org.springframework.security.oauth2.jwt.JwtDecoder; - -/** - * Condition for creating {@link JwtDecoder} by oidc issuer location. - * - * @author Artsiom Yudovin - */ -public class OidcIssuerLocationCondition implements Condition { - - private static final String OIDC_ISSUER_LOCATION = "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location"; - - private static final String JWT_SET_URI = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri"; - - @Override - public boolean matches(ConditionContext conditionContext, - AnnotatedTypeMetadata annotatedTypeMetadata) { - Environment environment = conditionContext.getEnvironment(); - return environment.containsProperty(OIDC_ISSUER_LOCATION) - && !environment.containsProperty(JWT_SET_URI); - } - -} diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java index f2ddd42579..d760162198 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwkConfiguration.java @@ -17,8 +17,8 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean; import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.boot.autoconfigure.security.oauth2.resource.IssuerUriCondition; import org.springframework.boot.autoconfigure.security.oauth2.resource.OAuth2ResourceServerProperties; -import org.springframework.boot.autoconfigure.security.oauth2.resource.OidcIssuerLocationCondition; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Configuration; @@ -27,8 +27,8 @@ import org.springframework.security.oauth2.jwt.JwtDecoders; import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; /** - * Configures a {@link JwtDecoder} when a JWK Set URI is available or Oidc Issuer - * Location. + * Configures a {@link JwtDecoder} when a JWK Set URI or OpenID Connect Issuer URI is + * available. * * @author Madhura Bhave * @author Artsiom Yudovin @@ -45,16 +45,16 @@ class OAuth2ResourceServerJwkConfiguration { @Bean @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") @ConditionalOnMissingBean - public JwtDecoder jwtDecoder() { + public JwtDecoder jwtDecoderByJwkKeySetUri() { return new NimbusJwtDecoderJwkSupport(this.properties.getJwt().getJwkSetUri()); } @Bean - @Conditional(OidcIssuerLocationCondition.class) + @Conditional(IssuerUriCondition.class) @ConditionalOnMissingBean - public JwtDecoder jwtDecoderByOidcIssuerLocation() { + public JwtDecoder jwtDecoderByIssuerUri() { return JwtDecoders - .fromOidcIssuerLocation(this.properties.getJwt().getOidcIssuerLocation()); + .fromOidcIssuerLocation(this.properties.getJwt().getIssuerUri()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java index e248b08bbf..56cda029bb 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java @@ -85,16 +85,17 @@ public class OAuth2ResourceServerAutoConfigurationTests { } @Test - public void autoConfigurationShouldConfigureResourceServerOidcIssuerLocation() + public void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws Exception { this.server = new MockWebServer(); this.server.start(); String issuer = this.server.url("").toString(); String cleanIssuerPath = cleanIssuerPath(issuer); setupMockResponse(cleanIssuerPath); - this.contextRunner.withPropertyValues( - "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" - + this.server.getHostName() + ":" + this.server.getPort()) + this.contextRunner + .withPropertyValues( + "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + + this.server.getHostName() + ":" + this.server.getPort()) .run((context) -> { assertThat(context.getBean(JwtDecoder.class)) .isInstanceOf(NimbusJwtDecoderJwkSupport.class); @@ -103,23 +104,16 @@ public class OAuth2ResourceServerAutoConfigurationTests { } @Test - public void autoConfigurationShouldConfigureSetUriWithTwoProperties() - throws Exception { - this.server = new MockWebServer(); - this.server.start(); - String issuer = this.server.url("").toString(); - String cleanIssuerPath = cleanIssuerPath(issuer); - setupMockResponse(cleanIssuerPath); + public void autoConfigurationWhenBothSetUriAndIssuerUriPresentShouldUseSetUri() { this.contextRunner.withPropertyValues( - "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" - + this.server.getHostName() + ":" + this.server.getPort(), + "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://issuer-uri.com", "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com") .run((context) -> { assertThat(context.getBean(JwtDecoder.class)) .isInstanceOf(NimbusJwtDecoderJwkSupport.class); assertThat(getBearerTokenFilter(context)).isNotNull(); - assertThat(context.containsBean("jwtDecoder")).isTrue(); - assertThat(context.containsBean("jwtDecoderByOidcIssuerLocation")) + assertThat(context.containsBean("jwtDecoderByJwkKeySetUri")).isTrue(); + assertThat(context.containsBean("jwtDecoderByOidcIssuerUri")) .isFalse(); }); } @@ -131,7 +125,7 @@ public class OAuth2ResourceServerAutoConfigurationTests { } @Test - public void jwtDecoderBeanIsConditionalOnMissingBean() { + public void jwtDecoderByJwkSetUriIsConditionalOnMissingBean() { this.contextRunner.withPropertyValues( "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com") .withUserConfiguration(JwtDecoderConfig.class) @@ -139,9 +133,9 @@ public class OAuth2ResourceServerAutoConfigurationTests { } @Test - public void jwtDecoderBeanIsConditionalOnMissingBeanOidcIssuerLocation() { + public void jwtDecoderByOidcIssuerUriIsConditionalOnMissingBean() { this.contextRunner.withPropertyValues( - "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://jwk-oidc-issuer-location.com") + "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://jwk-oidc-issuer-location.com") .withUserConfiguration(JwtDecoderConfig.class) .run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull()); } @@ -155,15 +149,6 @@ public class OAuth2ResourceServerAutoConfigurationTests { .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); } - @Test - public void autoConfigurationShouldBeConditionalOnJwtAuthenticationTokenClassOidcIssuerLocation() { - this.contextRunner.withPropertyValues( - "spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://jwk-oidc-issuer-location.com") - .withUserConfiguration(JwtDecoderConfig.class) - .withClassLoader(new FilteredClassLoader(JwtAuthenticationToken.class)) - .run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); - } - @SuppressWarnings("unchecked") private Filter getBearerTokenFilter(AssertableWebApplicationContext context) { FilterChainProxy filterChain = (FilterChainProxy) context diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc index 0e6fa71102..8db9dd7ede 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/appendix-application-properties.adoc @@ -539,7 +539,7 @@ content into your application. Rather, pick only the properties that you need. # SECURITY OAUTH2 RESOURCE SERVER ({sc-spring-boot-autoconfigure}/security/oauth2/resource/OAuth2ResourceServerProperties.{sc-ext}[OAuth2ResourceServerProperties]) spring.security.oauth2.resourceserver.jwt.jwk-set-uri= # JSON Web Key URI to use to verify the JWT token. - spring.security.oauth2.resource.jwt.oidc-issuer-location= # Location for issuer oidc + spring.security.oauth2.resource.jwt.issuer-uri= # URI that an OpenID Connect Provider asserts as its Issuer Identifier. # ---------------------------------------- # DATA PROPERTIES diff --git a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc index 22292f2ec8..1821bf6b9c 100644 --- a/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc +++ b/spring-boot-project/spring-boot-docs/src/main/asciidoc/spring-boot-features.adoc @@ -3320,14 +3320,19 @@ Provider can be configured with the `issuer-uri`: [[boot-features-security-oauth2-server]] ==== Resource Server If you have `spring-security-oauth2-resource-server` on your classpath, Spring Boot can -set up an OAuth2 Resource Server as long as a JWK Set URI is specified, as shown in the -following example: +set up an OAuth2 Resource Server as long as a JWK Set URI or OIDC Issuer URI is specified, +as shown in the following examples: [source,properties,indent=0] ---- spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys ---- +[source,properties,indent=0] +---- + spring.security.oauth2.resourceserver.jwt.issuer-uri=https://dev-123456.oktapreview.com/oauth2/default/ +---- + The same properties are applicable for both servlet and reactive applications. Alternatively, you can define your own `JwtDecoder` bean for servlet applications