Commit cf31325e authored by Madhura Bhave's avatar Madhura Bhave

Polish "OIDC issuer uri in OAuth resource server config"

Closes gh-14190
parent 0c299bbc
...@@ -15,29 +15,42 @@ ...@@ -15,29 +15,42 @@
*/ */
package org.springframework.boot.autoconfigure.security.oauth2.resource; package org.springframework.boot.autoconfigure.security.oauth2.resource;
import org.springframework.context.annotation.Condition; 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.context.annotation.ConditionContext;
import org.springframework.core.env.Environment; import org.springframework.core.env.Environment;
import org.springframework.core.type.AnnotatedTypeMetadata; import org.springframework.core.type.AnnotatedTypeMetadata;
import org.springframework.security.oauth2.jwt.JwtDecoder; import org.springframework.security.oauth2.jwt.JwtDecoder;
import org.springframework.util.StringUtils;
/** /**
* Condition for creating {@link JwtDecoder} by oidc issuer location. * Condition for creating {@link JwtDecoder} by oidc issuer location.
* *
* @author Artsiom Yudovin * @author Artsiom Yudovin
* @since 2.1.0
*/ */
public class OidcIssuerLocationCondition implements Condition { public class IssuerUriCondition extends SpringBootCondition {
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 @Override
public boolean matches(ConditionContext conditionContext, public ConditionOutcome getMatchOutcome(ConditionContext context,
AnnotatedTypeMetadata annotatedTypeMetadata) { AnnotatedTypeMetadata metadata) {
Environment environment = conditionContext.getEnvironment(); ConditionMessage.Builder message = ConditionMessage
return environment.containsProperty(OIDC_ISSUER_LOCATION) .forCondition("OpenID Connect Issuer URI Condition");
&& !environment.containsProperty(JWT_SET_URI); 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"));
} }
} }
...@@ -41,9 +41,9 @@ public class OAuth2ResourceServerProperties { ...@@ -41,9 +41,9 @@ public class OAuth2ResourceServerProperties {
private String jwkSetUri; 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() { public String getJwkSetUri() {
return this.jwkSetUri; return this.jwkSetUri;
...@@ -53,12 +53,12 @@ public class OAuth2ResourceServerProperties { ...@@ -53,12 +53,12 @@ public class OAuth2ResourceServerProperties {
this.jwkSetUri = jwkSetUri; this.jwkSetUri = jwkSetUri;
} }
public String getOidcIssuerLocation() { public String getIssuerUri() {
return this.oidcIssuerLocation; return this.issuerUri;
} }
public void setOidcIssuerLocation(String oidcIssuerLocation) { public void setIssuerUri(String issuerUri) {
this.oidcIssuerLocation = oidcIssuerLocation; this.issuerUri = issuerUri;
} }
} }
......
...@@ -17,8 +17,8 @@ package org.springframework.boot.autoconfigure.security.oauth2.resource.servlet; ...@@ -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.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; 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.OAuth2ResourceServerProperties;
import org.springframework.boot.autoconfigure.security.oauth2.resource.OidcIssuerLocationCondition;
import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional; import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.Configuration;
...@@ -27,8 +27,8 @@ import org.springframework.security.oauth2.jwt.JwtDecoders; ...@@ -27,8 +27,8 @@ import org.springframework.security.oauth2.jwt.JwtDecoders;
import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport; import org.springframework.security.oauth2.jwt.NimbusJwtDecoderJwkSupport;
/** /**
* Configures a {@link JwtDecoder} when a JWK Set URI is available or Oidc Issuer * Configures a {@link JwtDecoder} when a JWK Set URI or OpenID Connect Issuer URI is
* Location. * available.
* *
* @author Madhura Bhave * @author Madhura Bhave
* @author Artsiom Yudovin * @author Artsiom Yudovin
...@@ -45,16 +45,16 @@ class OAuth2ResourceServerJwkConfiguration { ...@@ -45,16 +45,16 @@ class OAuth2ResourceServerJwkConfiguration {
@Bean @Bean
@ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri") @ConditionalOnProperty(name = "spring.security.oauth2.resourceserver.jwt.jwk-set-uri")
@ConditionalOnMissingBean @ConditionalOnMissingBean
public JwtDecoder jwtDecoder() { public JwtDecoder jwtDecoderByJwkKeySetUri() {
return new NimbusJwtDecoderJwkSupport(this.properties.getJwt().getJwkSetUri()); return new NimbusJwtDecoderJwkSupport(this.properties.getJwt().getJwkSetUri());
} }
@Bean @Bean
@Conditional(OidcIssuerLocationCondition.class) @Conditional(IssuerUriCondition.class)
@ConditionalOnMissingBean @ConditionalOnMissingBean
public JwtDecoder jwtDecoderByOidcIssuerLocation() { public JwtDecoder jwtDecoderByIssuerUri() {
return JwtDecoders return JwtDecoders
.fromOidcIssuerLocation(this.properties.getJwt().getOidcIssuerLocation()); .fromOidcIssuerLocation(this.properties.getJwt().getIssuerUri());
} }
} }
...@@ -85,16 +85,17 @@ public class OAuth2ResourceServerAutoConfigurationTests { ...@@ -85,16 +85,17 @@ public class OAuth2ResourceServerAutoConfigurationTests {
} }
@Test @Test
public void autoConfigurationShouldConfigureResourceServerOidcIssuerLocation() public void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri()
throws Exception { throws Exception {
this.server = new MockWebServer(); this.server = new MockWebServer();
this.server.start(); this.server.start();
String issuer = this.server.url("").toString(); String issuer = this.server.url("").toString();
String cleanIssuerPath = cleanIssuerPath(issuer); String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponse(cleanIssuerPath); setupMockResponse(cleanIssuerPath);
this.contextRunner.withPropertyValues( this.contextRunner
"spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" .withPropertyValues(
+ this.server.getHostName() + ":" + this.server.getPort()) "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
+ this.server.getHostName() + ":" + this.server.getPort())
.run((context) -> { .run((context) -> {
assertThat(context.getBean(JwtDecoder.class)) assertThat(context.getBean(JwtDecoder.class))
.isInstanceOf(NimbusJwtDecoderJwkSupport.class); .isInstanceOf(NimbusJwtDecoderJwkSupport.class);
...@@ -103,23 +104,16 @@ public class OAuth2ResourceServerAutoConfigurationTests { ...@@ -103,23 +104,16 @@ public class OAuth2ResourceServerAutoConfigurationTests {
} }
@Test @Test
public void autoConfigurationShouldConfigureSetUriWithTwoProperties() public void autoConfigurationWhenBothSetUriAndIssuerUriPresentShouldUseSetUri() {
throws Exception {
this.server = new MockWebServer();
this.server.start();
String issuer = this.server.url("").toString();
String cleanIssuerPath = cleanIssuerPath(issuer);
setupMockResponse(cleanIssuerPath);
this.contextRunner.withPropertyValues( this.contextRunner.withPropertyValues(
"spring.security.oauth2.resourceserver.jwt.oidc-issuer-location=http://" "spring.security.oauth2.resourceserver.jwt.issuer-uri=http://issuer-uri.com",
+ this.server.getHostName() + ":" + this.server.getPort(),
"spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com") "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com")
.run((context) -> { .run((context) -> {
assertThat(context.getBean(JwtDecoder.class)) assertThat(context.getBean(JwtDecoder.class))
.isInstanceOf(NimbusJwtDecoderJwkSupport.class); .isInstanceOf(NimbusJwtDecoderJwkSupport.class);
assertThat(getBearerTokenFilter(context)).isNotNull(); assertThat(getBearerTokenFilter(context)).isNotNull();
assertThat(context.containsBean("jwtDecoder")).isTrue(); assertThat(context.containsBean("jwtDecoderByJwkKeySetUri")).isTrue();
assertThat(context.containsBean("jwtDecoderByOidcIssuerLocation")) assertThat(context.containsBean("jwtDecoderByOidcIssuerUri"))
.isFalse(); .isFalse();
}); });
} }
...@@ -131,7 +125,7 @@ public class OAuth2ResourceServerAutoConfigurationTests { ...@@ -131,7 +125,7 @@ public class OAuth2ResourceServerAutoConfigurationTests {
} }
@Test @Test
public void jwtDecoderBeanIsConditionalOnMissingBean() { public void jwtDecoderByJwkSetUriIsConditionalOnMissingBean() {
this.contextRunner.withPropertyValues( this.contextRunner.withPropertyValues(
"spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com") "spring.security.oauth2.resourceserver.jwt.jwk-set-uri=http://jwk-set-uri.com")
.withUserConfiguration(JwtDecoderConfig.class) .withUserConfiguration(JwtDecoderConfig.class)
...@@ -139,9 +133,9 @@ public class OAuth2ResourceServerAutoConfigurationTests { ...@@ -139,9 +133,9 @@ public class OAuth2ResourceServerAutoConfigurationTests {
} }
@Test @Test
public void jwtDecoderBeanIsConditionalOnMissingBeanOidcIssuerLocation() { public void jwtDecoderByOidcIssuerUriIsConditionalOnMissingBean() {
this.contextRunner.withPropertyValues( 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) .withUserConfiguration(JwtDecoderConfig.class)
.run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull()); .run((context) -> assertThat(getBearerTokenFilter(context)).isNotNull());
} }
...@@ -155,15 +149,6 @@ public class OAuth2ResourceServerAutoConfigurationTests { ...@@ -155,15 +149,6 @@ public class OAuth2ResourceServerAutoConfigurationTests {
.run((context) -> assertThat(getBearerTokenFilter(context)).isNull()); .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") @SuppressWarnings("unchecked")
private Filter getBearerTokenFilter(AssertableWebApplicationContext context) { private Filter getBearerTokenFilter(AssertableWebApplicationContext context) {
FilterChainProxy filterChain = (FilterChainProxy) context FilterChainProxy filterChain = (FilterChainProxy) context
......
...@@ -539,7 +539,7 @@ content into your application. Rather, pick only the properties that you need. ...@@ -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]) # 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.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 # DATA PROPERTIES
......
...@@ -3320,14 +3320,19 @@ Provider can be configured with the `issuer-uri`: ...@@ -3320,14 +3320,19 @@ Provider can be configured with the `issuer-uri`:
[[boot-features-security-oauth2-server]] [[boot-features-security-oauth2-server]]
==== Resource Server ==== Resource Server
If you have `spring-security-oauth2-resource-server` on your classpath, Spring Boot can 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 set up an OAuth2 Resource Server as long as a JWK Set URI or OIDC Issuer URI is specified,
following example: as shown in the following examples:
[source,properties,indent=0] [source,properties,indent=0]
---- ----
spring.security.oauth2.resourceserver.jwt.jwk-set-uri=https://example.com/oauth2/default/v1/keys 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. The same properties are applicable for both servlet and reactive applications.
Alternatively, you can define your own `JwtDecoder` bean for servlet applications Alternatively, you can define your own `JwtDecoder` bean for servlet applications
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment