Merge pull request #17761 from htztomic
* pr/17761: Polish "Support RFC 8414 in JwtDecoders and ClientRegistrations" Support RFC 8414 in JwtDecoders and ClientRegistrations Closes gh-17761
This commit is contained in:
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -79,7 +79,7 @@ class ReactiveOAuth2ResourceServerJwkConfiguration {
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
|
||||
return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ class OAuth2ResourceServerJwtConfiguration {
|
||||
@Bean
|
||||
@Conditional(IssuerUriCondition.class)
|
||||
JwtDecoder jwtDecoderByIssuerUri() {
|
||||
return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
|
||||
return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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,7 +210,7 @@ class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||
Registration login = new OAuth2ClientProperties.Registration();
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
testOidcConfiguration(login, "okta");
|
||||
testIssuerConfiguration(login, "okta", 0, 1);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -218,7 +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 issuerUriConfigurationTriesOidcRfc8414UriSecond() throws Exception {
|
||||
OAuth2ClientProperties.Registration login = new Registration();
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
testIssuerConfiguration(login, "okta", 1, 2);
|
||||
}
|
||||
|
||||
@Test
|
||||
void issuerUriConfigurationTriesOAuthMetadataUriThird() throws Exception {
|
||||
OAuth2ClientProperties.Registration login = new Registration();
|
||||
login.setClientId("clientId");
|
||||
login.setClientSecret("clientSecret");
|
||||
testIssuerConfiguration(login, "okta", 2, 3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -273,12 +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);
|
||||
setupMockResponsesWithErrors(issuer, errorResponseCount);
|
||||
OAuth2ClientProperties properties = new OAuth2ClientProperties();
|
||||
Provider provider = new Provider();
|
||||
provider.setIssuerUri(issuer);
|
||||
@@ -300,6 +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(numberOfRequests);
|
||||
}
|
||||
|
||||
private void setupMockResponse(String issuer) throws JsonProcessingException {
|
||||
@@ -309,6 +327,14 @@ class OAuth2ClientPropertiesRegistrationAdapterTests {
|
||||
this.server.enqueue(mockResponse);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
setupMockResponse(issuer);
|
||||
}
|
||||
|
||||
private Map<String, Object> getResponse(String issuer) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
|
||||
|
||||
@@ -64,6 +64,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Artsiom Yudovin
|
||||
* @author HaiTao Zhang
|
||||
*/
|
||||
class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
||||
|
||||
@@ -94,14 +95,49 @@ 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() + "/" + 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 issuer = this.server.url("").toString();
|
||||
String cleanIssuerPath = cleanIssuerPath(issuer);
|
||||
setupMockResponsesWithErrors(cleanIssuerPath, 1);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort()).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 issuer = this.server.url("").toString();
|
||||
String cleanIssuerPath = cleanIssuerPath(issuer);
|
||||
setupMockResponsesWithErrors(cleanIssuerPath, 2);
|
||||
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
|
||||
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
|
||||
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
|
||||
assertFilterConfiguredWithJwtAuthenticationManager(context);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
});
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -322,6 +358,14 @@ class ReactiveOAuth2ResourceServerAutoConfigurationTests {
|
||||
this.server.enqueue(mockResponse);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
setupMockResponse(issuer);
|
||||
}
|
||||
|
||||
private Map<String, Object> getResponse(String issuer) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
|
||||
|
||||
@@ -58,6 +58,7 @@ import static org.mockito.Mockito.mock;
|
||||
*
|
||||
* @author Madhura Bhave
|
||||
* @author Artsiom Yudovin
|
||||
* @author HaiTao Zhang
|
||||
*/
|
||||
class OAuth2ResourceServerAutoConfigurationTests {
|
||||
|
||||
@@ -114,14 +115,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);
|
||||
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);
|
||||
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);
|
||||
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);
|
||||
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
|
||||
});
|
||||
assertThat(this.server.getRequestCount()).isEqualTo(3);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -306,6 +341,14 @@ class OAuth2ResourceServerAutoConfigurationTests {
|
||||
this.server.enqueue(mockResponse);
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
setupMockResponse(issuer);
|
||||
}
|
||||
|
||||
private Map<String, Object> getResponse(String issuer) {
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");
|
||||
|
||||
Reference in New Issue
Block a user