From 8baec96453b4d912acc4d35f93934446a86b6744 Mon Sep 17 00:00:00 2001 From: HaiTao Zhang Date: Thu, 1 Aug 2019 15:49:13 -0700 Subject: [PATCH 1/2] Support RFC 8414 in JwtDecoders and ClientRegistrations See gh-17761 --- ...h2ClientPropertiesRegistrationAdapter.java | 2 +- ...eOAuth2ResourceServerJwkConfiguration.java | 2 +- .../OAuth2ResourceServerJwtConfiguration.java | 2 +- ...entPropertiesRegistrationAdapterTests.java | 107 ++++++++++++++++++ ...2ResourceServerAutoConfigurationTests.java | 50 +++++++- ...2ResourceServerAutoConfigurationTests.java | 49 +++++++- 6 files changed, 204 insertions(+), 8 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java index 13feae1b360..481257a44a9 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java @@ -80,7 +80,7 @@ public final class OAuth2ClientPropertiesRegistrationAdapter { Provider provider = providers.get(providerId); String issuer = provider.getIssuerUri(); if (issuer != null) { - Builder builder = ClientRegistrations.fromOidcIssuerLocation(issuer).registrationId(registrationId); + Builder builder = ClientRegistrations.fromIssuerLocation(issuer).registrationId(registrationId); return getBuilder(builder, provider); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java index 356c0786217..a4f80a89254 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java @@ -79,7 +79,7 @@ class ReactiveOAuth2ResourceServerJwkConfiguration { @Bean @Conditional(IssuerUriCondition.class) ReactiveJwtDecoder jwtDecoderByIssuerUri() { - return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri()); + return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java index c7ad97955fa..1bcb68eee8f 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java @@ -81,7 +81,7 @@ class OAuth2ResourceServerJwtConfiguration { @Bean @Conditional(IssuerUriCondition.class) JwtDecoder jwtDecoderByIssuerUri() { - return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri()); + return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri()); } } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java index 0bf8877d6e7..cf873de2724 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java @@ -212,6 +212,22 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { testOidcConfiguration(login, "okta"); } + @Test + void oidcRfc8414ProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception { + OAuth2ClientProperties.Registration login = new Registration(); + login.setClientId("clientId"); + login.setClientSecret("clientSecret"); + testOidcRfc8414Configuration(login, "okta"); + } + + @Test + void oAuthProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception { + OAuth2ClientProperties.Registration login = new Registration(); + login.setClientId("clientId"); + login.setClientSecret("clientSecret"); + testOAuthConfiguration(login, "okta"); + } + @Test void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception { OAuth2ClientProperties.Registration login = new Registration(); @@ -221,6 +237,24 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { testOidcConfiguration(login, "okta-oidc"); } + @Test + void oidcRfc8414ProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception { + OAuth2ClientProperties.Registration login = new Registration(); + login.setProvider("okta-oidcRfc8414"); + login.setClientId("clientId"); + login.setClientSecret("clientSecret"); + testOidcRfc8414Configuration(login, "okta-oidcRfc8414"); + } + + @Test + void oAuthProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception { + OAuth2ClientProperties.Registration login = new Registration(); + login.setProvider("okta-oauth"); + login.setClientId("clientId"); + login.setClientSecret("clientSecret"); + testOAuthConfiguration(login, "okta-oauth"); + } + @Test void oidcProviderConfigurationWithCustomConfigurationOverridesProviderDefaults() throws Exception { this.server = new MockWebServer(); @@ -300,6 +334,70 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); assertThat(userInfoEndpoint.getAuthenticationMethod()) .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); + assertThat(this.server.getRequestCount()).isEqualTo(1); + } + + private void testOidcRfc8414Configuration(OAuth2ClientProperties.Registration registration, String providerId) + throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + setupMockResponseWithEmptyResponses(issuer, 1); + OAuth2ClientProperties properties = new OAuth2ClientProperties(); + Provider provider = new Provider(); + provider.setIssuerUri(issuer); + properties.getProvider().put(providerId, provider); + properties.getRegistration().put("okta", registration); + Map registrations = OAuth2ClientPropertiesRegistrationAdapter + .getClientRegistrations(properties); + ClientRegistration adapted = registrations.get("okta"); + ProviderDetails providerDetails = adapted.getProviderDetails(); + assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(adapted.getRegistrationId()).isEqualTo("okta"); + assertThat(adapted.getClientName()).isEqualTo(issuer); + assertThat(adapted.getScopes()).containsOnly("openid"); + assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); + assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); + assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs"); + UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint(); + assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); + assertThat(userInfoEndpoint.getAuthenticationMethod()) + .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); + assertThat(this.server.getRequestCount()).isEqualTo(2); + + } + + private void testOAuthConfiguration(OAuth2ClientProperties.Registration registration, String providerId) + throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + setupMockResponseWithEmptyResponses(issuer, 2); + OAuth2ClientProperties properties = new OAuth2ClientProperties(); + Provider provider = new Provider(); + provider.setIssuerUri(issuer); + properties.getProvider().put(providerId, provider); + properties.getRegistration().put("okta", registration); + Map registrations = OAuth2ClientPropertiesRegistrationAdapter + .getClientRegistrations(properties); + ClientRegistration adapted = registrations.get("okta"); + ProviderDetails providerDetails = adapted.getProviderDetails(); + assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); + assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(adapted.getRegistrationId()).isEqualTo("okta"); + assertThat(adapted.getClientName()).isEqualTo(issuer); + assertThat(adapted.getScopes()).containsOnly("openid"); + assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); + assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); + assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs"); + UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint(); + assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); + assertThat(userInfoEndpoint.getAuthenticationMethod()) + .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); + assertThat(this.server.getRequestCount()).isEqualTo(3); } private void setupMockResponse(String issuer) throws JsonProcessingException { @@ -309,6 +407,15 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { this.server.enqueue(mockResponse); } + private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) + throws JsonProcessingException { + for (int i = 0; i < amountOfEmptyResponse; i++) { + MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); + this.server.enqueue(emptyResponse); + } + setupMockResponse(issuer); + } + private Map getResponse(String issuer) { Map response = new HashMap<>(); response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth"); diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java index df8b13a82dc..5253e5f879a 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java @@ -94,14 +94,51 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws IOException { this.server = new MockWebServer(); this.server.start(); - String issuer = this.server.url("").toString(); + String path = "test"; + String issuer = this.server.url(path).toString(); String cleanIssuerPath = cleanIssuerPath(issuer); setupMockResponse(cleanIssuerPath); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" - + this.server.getHostName() + ":" + this.server.getPort()).run((context) -> { + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class); + assertFilterConfiguredWithJwtAuthenticationManager(context); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + assertThat(this.server.getRequestCount()).isEqualTo(1); + } + + @Test + void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponseWithEmptyResponses(cleanIssuerPath, 1); + this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class); assertFilterConfiguredWithJwtAuthenticationManager(context); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); }); + assertThat(this.server.getRequestCount()).isEqualTo(2); + } + + @Test + void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponseWithEmptyResponses(cleanIssuerPath, 2); + this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class); + assertFilterConfiguredWithJwtAuthenticationManager(context); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + assertThat(this.server.getRequestCount()).isEqualTo(3); } @Test @@ -322,6 +359,15 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { this.server.enqueue(mockResponse); } + private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) + throws JsonProcessingException { + for (int i = 0; i < amountOfEmptyResponse; i++) { + MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); + this.server.enqueue(emptyResponse); + } + setupMockResponse(issuer); + } + private Map getResponse(String issuer) { Map response = new HashMap<>(); response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth"); 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 aabbfb591d7..f62afd6f530 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 @@ -114,14 +114,48 @@ class OAuth2ResourceServerAutoConfigurationTests { void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws Exception { this.server = new MockWebServer(); this.server.start(); - String issuer = this.server.url("").toString(); + String path = "test"; + String issuer = this.server.url(path).toString(); String cleanIssuerPath = cleanIssuerPath(issuer); setupMockResponse(cleanIssuerPath); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" - + this.server.getHostName() + ":" + this.server.getPort()).run((context) -> { + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { assertThat(context).hasSingleBean(JwtDecoder.class); - assertThat(getBearerTokenFilter(context)).isNotNull(); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + assertThat(this.server.getRequestCount()).isEqualTo(1); + } + + @Test + void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponseWithEmptyResponses(cleanIssuerPath, 1); + this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + assertThat(context).hasSingleBean(JwtDecoder.class); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); }); + assertThat(this.server.getRequestCount()).isEqualTo(2); + } + + @Test + void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception { + this.server = new MockWebServer(); + this.server.start(); + String path = "test"; + String issuer = this.server.url(path).toString(); + String cleanIssuerPath = cleanIssuerPath(issuer); + setupMockResponseWithEmptyResponses(cleanIssuerPath, 2); + this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + assertThat(context).hasSingleBean(JwtDecoder.class); + assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); + }); + assertThat(this.server.getRequestCount()).isEqualTo(3); } @Test @@ -306,6 +340,15 @@ class OAuth2ResourceServerAutoConfigurationTests { this.server.enqueue(mockResponse); } + private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) + throws JsonProcessingException { + for (int i = 0; i < amountOfEmptyResponse; i++) { + MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); + this.server.enqueue(emptyResponse); + } + setupMockResponse(issuer); + } + private Map getResponse(String issuer) { Map response = new HashMap<>(); response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth"); From e06b06d8173d6f787117898d48832eb3b71ffb74 Mon Sep 17 00:00:00 2001 From: Madhura Bhave Date: Thu, 1 Aug 2019 18:09:41 -0700 Subject: [PATCH 2/2] Polish "Support RFC 8414 in JwtDecoders and ClientRegistrations" See gh-17761 --- .../oauth2/client/OAuth2ClientProperties.java | 3 +- .../OAuth2ResourceServerProperties.java | 3 +- ...entPropertiesRegistrationAdapterTests.java | 107 +++--------------- ...2ResourceServerAutoConfigurationTests.java | 20 ++-- ...2ResourceServerAutoConfigurationTests.java | 10 +- 5 files changed, 31 insertions(+), 112 deletions(-) diff --git a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java index 2bb49a2453b..8b16e6b2fd8 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java @@ -214,7 +214,8 @@ public class OAuth2ClientProperties { private String jwkSetUri; /** - * URI that an OpenID Connect Provider asserts as its Issuer Identifier. + * URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 + * Authorization Server Metadata endpoint defined by RFC 8414. */ private String issuerUri; 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 54d6c5665df..fe6eff975d7 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 @@ -82,7 +82,8 @@ public class OAuth2ResourceServerProperties { private String jwsAlgorithm = "RS256"; /** - * URI that an OpenID Connect Provider asserts as its Issuer Identifier. + * URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0 + * Authorization Server Metadata endpoint defined by RFC 8414. */ private String issuerUri; diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java index cf873de2724..cda74fae725 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java @@ -48,6 +48,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalStateException; * @author Phillip Webb * @author Madhura Bhave * @author Thiago Hirata + * @author HaiTao Zhang */ class OAuth2ClientPropertiesRegistrationAdapterTests { @@ -209,23 +210,7 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { Registration login = new OAuth2ClientProperties.Registration(); login.setClientId("clientId"); login.setClientSecret("clientSecret"); - testOidcConfiguration(login, "okta"); - } - - @Test - void oidcRfc8414ProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception { - OAuth2ClientProperties.Registration login = new Registration(); - login.setClientId("clientId"); - login.setClientSecret("clientSecret"); - testOidcRfc8414Configuration(login, "okta"); - } - - @Test - void oAuthProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Exception { - OAuth2ClientProperties.Registration login = new Registration(); - login.setClientId("clientId"); - login.setClientSecret("clientSecret"); - testOAuthConfiguration(login, "okta"); + testIssuerConfiguration(login, "okta", 0, 1); } @Test @@ -234,25 +219,23 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { login.setProvider("okta-oidc"); login.setClientId("clientId"); login.setClientSecret("clientSecret"); - testOidcConfiguration(login, "okta-oidc"); + testIssuerConfiguration(login, "okta-oidc", 0, 1); } @Test - void oidcRfc8414ProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception { + void issuerUriConfigurationTriesOidcRfc8414UriSecond() throws Exception { OAuth2ClientProperties.Registration login = new Registration(); - login.setProvider("okta-oidcRfc8414"); login.setClientId("clientId"); login.setClientSecret("clientSecret"); - testOidcRfc8414Configuration(login, "okta-oidcRfc8414"); + testIssuerConfiguration(login, "okta", 1, 2); } @Test - void oAuthProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Exception { + void issuerUriConfigurationTriesOAuthMetadataUriThird() throws Exception { OAuth2ClientProperties.Registration login = new Registration(); - login.setProvider("okta-oauth"); login.setClientId("clientId"); login.setClientSecret("clientSecret"); - testOAuthConfiguration(login, "okta-oauth"); + testIssuerConfiguration(login, "okta", 2, 3); } @Test @@ -307,75 +290,12 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { return registration; } - private void testOidcConfiguration(OAuth2ClientProperties.Registration registration, String providerId) - throws Exception { + private void testIssuerConfiguration(OAuth2ClientProperties.Registration registration, String providerId, + int errorResponseCount, int numberOfRequests) throws Exception { this.server = new MockWebServer(); this.server.start(); String issuer = this.server.url("").toString(); - setupMockResponse(issuer); - OAuth2ClientProperties properties = new OAuth2ClientProperties(); - Provider provider = new Provider(); - provider.setIssuerUri(issuer); - properties.getProvider().put(providerId, provider); - properties.getRegistration().put("okta", registration); - Map registrations = OAuth2ClientPropertiesRegistrationAdapter - .getClientRegistrations(properties); - ClientRegistration adapted = registrations.get("okta"); - ProviderDetails providerDetails = adapted.getProviderDetails(); - assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(adapted.getRegistrationId()).isEqualTo("okta"); - assertThat(adapted.getClientName()).isEqualTo(issuer); - assertThat(adapted.getScopes()).containsOnly("openid"); - assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); - assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); - assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs"); - UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint(); - assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); - assertThat(userInfoEndpoint.getAuthenticationMethod()) - .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); - assertThat(this.server.getRequestCount()).isEqualTo(1); - } - - private void testOidcRfc8414Configuration(OAuth2ClientProperties.Registration registration, String providerId) - throws Exception { - this.server = new MockWebServer(); - this.server.start(); - String path = "test"; - String issuer = this.server.url(path).toString(); - setupMockResponseWithEmptyResponses(issuer, 1); - OAuth2ClientProperties properties = new OAuth2ClientProperties(); - Provider provider = new Provider(); - provider.setIssuerUri(issuer); - properties.getProvider().put(providerId, provider); - properties.getRegistration().put("okta", registration); - Map registrations = OAuth2ClientPropertiesRegistrationAdapter - .getClientRegistrations(properties); - ClientRegistration adapted = registrations.get("okta"); - ProviderDetails providerDetails = adapted.getProviderDetails(); - assertThat(adapted.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.BASIC); - assertThat(adapted.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(adapted.getRegistrationId()).isEqualTo("okta"); - assertThat(adapted.getClientName()).isEqualTo(issuer); - assertThat(adapted.getScopes()).containsOnly("openid"); - assertThat(providerDetails.getAuthorizationUri()).isEqualTo("https://example.com/o/oauth2/v2/auth"); - assertThat(providerDetails.getTokenUri()).isEqualTo("https://example.com/oauth2/v4/token"); - assertThat(providerDetails.getJwkSetUri()).isEqualTo("https://example.com/oauth2/v3/certs"); - UserInfoEndpoint userInfoEndpoint = providerDetails.getUserInfoEndpoint(); - assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); - assertThat(userInfoEndpoint.getAuthenticationMethod()) - .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); - assertThat(this.server.getRequestCount()).isEqualTo(2); - - } - - private void testOAuthConfiguration(OAuth2ClientProperties.Registration registration, String providerId) - throws Exception { - this.server = new MockWebServer(); - this.server.start(); - String path = "test"; - String issuer = this.server.url(path).toString(); - setupMockResponseWithEmptyResponses(issuer, 2); + setupMockResponsesWithErrors(issuer, errorResponseCount); OAuth2ClientProperties properties = new OAuth2ClientProperties(); Provider provider = new Provider(); provider.setIssuerUri(issuer); @@ -397,7 +317,7 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo"); assertThat(userInfoEndpoint.getAuthenticationMethod()) .isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER); - assertThat(this.server.getRequestCount()).isEqualTo(3); + assertThat(this.server.getRequestCount()).isEqualTo(numberOfRequests); } private void setupMockResponse(String issuer) throws JsonProcessingException { @@ -407,9 +327,8 @@ class OAuth2ClientPropertiesRegistrationAdapterTests { this.server.enqueue(mockResponse); } - private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) - throws JsonProcessingException { - for (int i = 0; i < amountOfEmptyResponse; i++) { + private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException { + for (int i = 0; i < errorResponseCount; i++) { MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); this.server.enqueue(emptyResponse); } diff --git a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java index 5253e5f879a..b63e5eea3d3 100644 --- a/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java +++ b/spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java @@ -64,6 +64,7 @@ import static org.mockito.Mockito.mock; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ class ReactiveOAuth2ResourceServerAutoConfigurationTests { @@ -111,12 +112,11 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception { this.server = new MockWebServer(); this.server.start(); - String path = "test"; - String issuer = this.server.url(path).toString(); + String issuer = this.server.url("").toString(); String cleanIssuerPath = cleanIssuerPath(issuer); - setupMockResponseWithEmptyResponses(cleanIssuerPath, 1); + setupMockResponsesWithErrors(cleanIssuerPath, 1); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" - + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + + this.server.getHostName() + ":" + this.server.getPort()).run((context) -> { assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class); assertFilterConfiguredWithJwtAuthenticationManager(context); assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); @@ -128,12 +128,11 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception { this.server = new MockWebServer(); this.server.start(); - String path = "test"; - String issuer = this.server.url(path).toString(); + String issuer = this.server.url("").toString(); String cleanIssuerPath = cleanIssuerPath(issuer); - setupMockResponseWithEmptyResponses(cleanIssuerPath, 2); + setupMockResponsesWithErrors(cleanIssuerPath, 2); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" - + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { + + this.server.getHostName() + ":" + this.server.getPort()).run((context) -> { assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class); assertFilterConfiguredWithJwtAuthenticationManager(context); assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue(); @@ -359,9 +358,8 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests { this.server.enqueue(mockResponse); } - private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) - throws JsonProcessingException { - for (int i = 0; i < amountOfEmptyResponse; i++) { + private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException { + for (int i = 0; i < errorResponseCount; i++) { MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); this.server.enqueue(emptyResponse); } 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 f62afd6f530..6ab7a67ac5e 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 @@ -58,6 +58,7 @@ import static org.mockito.Mockito.mock; * * @author Madhura Bhave * @author Artsiom Yudovin + * @author HaiTao Zhang */ class OAuth2ResourceServerAutoConfigurationTests { @@ -133,7 +134,7 @@ class OAuth2ResourceServerAutoConfigurationTests { String path = "test"; String issuer = this.server.url(path).toString(); String cleanIssuerPath = cleanIssuerPath(issuer); - setupMockResponseWithEmptyResponses(cleanIssuerPath, 1); + setupMockResponsesWithErrors(cleanIssuerPath, 1); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { assertThat(context).hasSingleBean(JwtDecoder.class); @@ -149,7 +150,7 @@ class OAuth2ResourceServerAutoConfigurationTests { String path = "test"; String issuer = this.server.url(path).toString(); String cleanIssuerPath = cleanIssuerPath(issuer); - setupMockResponseWithEmptyResponses(cleanIssuerPath, 2); + setupMockResponsesWithErrors(cleanIssuerPath, 2); this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://" + this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> { assertThat(context).hasSingleBean(JwtDecoder.class); @@ -340,9 +341,8 @@ class OAuth2ResourceServerAutoConfigurationTests { this.server.enqueue(mockResponse); } - private void setupMockResponseWithEmptyResponses(String issuer, int amountOfEmptyResponse) - throws JsonProcessingException { - for (int i = 0; i < amountOfEmptyResponse; i++) { + private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException { + for (int i = 0; i < errorResponseCount; i++) { MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value()); this.server.enqueue(emptyResponse); }