Skip to content

Commit ce38891

Browse files
committed
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
2 parents 4e29f7b + e06b06d commit ce38891

File tree

8 files changed

+129
-14
lines changed

8 files changed

+129
-14
lines changed

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientProperties.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -214,7 +214,8 @@ public static class Provider {
214214
private String jwkSetUri;
215215

216216
/**
217-
* URI that an OpenID Connect Provider asserts as its Issuer Identifier.
217+
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
218+
* Authorization Server Metadata endpoint defined by RFC 8414.
218219
*/
219220
private String issuerUri;
220221

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapter.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -80,7 +80,7 @@ private static Builder getBuilderFromIssuerIfPossible(String registrationId, Str
8080
Provider provider = providers.get(providerId);
8181
String issuer = provider.getIssuerUri();
8282
if (issuer != null) {
83-
Builder builder = ClientRegistrations.fromOidcIssuerLocation(issuer).registrationId(registrationId);
83+
Builder builder = ClientRegistrations.fromIssuerLocation(issuer).registrationId(registrationId);
8484
return getBuilder(builder, provider);
8585
}
8686
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/OAuth2ResourceServerProperties.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,8 @@ public static class Jwt {
8282
private String jwsAlgorithm = "RS256";
8383

8484
/**
85-
* URI that an OpenID Connect Provider asserts as its Issuer Identifier.
85+
* URI that can either be an OpenID Connect discovery endpoint or an OAuth 2.0
86+
* Authorization Server Metadata endpoint defined by RFC 8414.
8687
*/
8788
private String issuerUri;
8889

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerJwkConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ private byte[] getKeySpec(String keyValue) {
7979
@Bean
8080
@Conditional(IssuerUriCondition.class)
8181
ReactiveJwtDecoder jwtDecoderByIssuerUri() {
82-
return ReactiveJwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
82+
return ReactiveJwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
8383
}
8484

8585
}

spring-boot-project/spring-boot-autoconfigure/src/main/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerJwtConfiguration.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ private byte[] getKeySpec(String keyValue) {
8181
@Bean
8282
@Conditional(IssuerUriCondition.class)
8383
JwtDecoder jwtDecoderByIssuerUri() {
84-
return JwtDecoders.fromOidcIssuerLocation(this.properties.getIssuerUri());
84+
return JwtDecoders.fromIssuerLocation(this.properties.getIssuerUri());
8585
}
8686

8787
}

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/client/OAuth2ClientPropertiesRegistrationAdapterTests.java

Lines changed: 31 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
* @author Phillip Webb
4949
* @author Madhura Bhave
5050
* @author Thiago Hirata
51+
* @author HaiTao Zhang
5152
*/
5253
class OAuth2ClientPropertiesRegistrationAdapterTests {
5354

@@ -209,7 +210,7 @@ void oidcProviderConfigurationWhenProviderNotSpecifiedOnRegistration() throws Ex
209210
Registration login = new OAuth2ClientProperties.Registration();
210211
login.setClientId("clientId");
211212
login.setClientSecret("clientSecret");
212-
testOidcConfiguration(login, "okta");
213+
testIssuerConfiguration(login, "okta", 0, 1);
213214
}
214215

215216
@Test
@@ -218,7 +219,23 @@ void oidcProviderConfigurationWhenProviderSpecifiedOnRegistration() throws Excep
218219
login.setProvider("okta-oidc");
219220
login.setClientId("clientId");
220221
login.setClientSecret("clientSecret");
221-
testOidcConfiguration(login, "okta-oidc");
222+
testIssuerConfiguration(login, "okta-oidc", 0, 1);
223+
}
224+
225+
@Test
226+
void issuerUriConfigurationTriesOidcRfc8414UriSecond() throws Exception {
227+
OAuth2ClientProperties.Registration login = new Registration();
228+
login.setClientId("clientId");
229+
login.setClientSecret("clientSecret");
230+
testIssuerConfiguration(login, "okta", 1, 2);
231+
}
232+
233+
@Test
234+
void issuerUriConfigurationTriesOAuthMetadataUriThird() throws Exception {
235+
OAuth2ClientProperties.Registration login = new Registration();
236+
login.setClientId("clientId");
237+
login.setClientSecret("clientSecret");
238+
testIssuerConfiguration(login, "okta", 2, 3);
222239
}
223240

224241
@Test
@@ -273,12 +290,12 @@ private OAuth2ClientProperties.Registration createRegistration(String provider)
273290
return registration;
274291
}
275292

276-
private void testOidcConfiguration(OAuth2ClientProperties.Registration registration, String providerId)
277-
throws Exception {
293+
private void testIssuerConfiguration(OAuth2ClientProperties.Registration registration, String providerId,
294+
int errorResponseCount, int numberOfRequests) throws Exception {
278295
this.server = new MockWebServer();
279296
this.server.start();
280297
String issuer = this.server.url("").toString();
281-
setupMockResponse(issuer);
298+
setupMockResponsesWithErrors(issuer, errorResponseCount);
282299
OAuth2ClientProperties properties = new OAuth2ClientProperties();
283300
Provider provider = new Provider();
284301
provider.setIssuerUri(issuer);
@@ -300,6 +317,7 @@ private void testOidcConfiguration(OAuth2ClientProperties.Registration registrat
300317
assertThat(userInfoEndpoint.getUri()).isEqualTo("https://example.com/oauth2/v3/userinfo");
301318
assertThat(userInfoEndpoint.getAuthenticationMethod())
302319
.isEqualTo(org.springframework.security.oauth2.core.AuthenticationMethod.HEADER);
320+
assertThat(this.server.getRequestCount()).isEqualTo(numberOfRequests);
303321
}
304322

305323
private void setupMockResponse(String issuer) throws JsonProcessingException {
@@ -309,6 +327,14 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
309327
this.server.enqueue(mockResponse);
310328
}
311329

330+
private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException {
331+
for (int i = 0; i < errorResponseCount; i++) {
332+
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
333+
this.server.enqueue(emptyResponse);
334+
}
335+
setupMockResponse(issuer);
336+
}
337+
312338
private Map<String, Object> getResponse(String issuer) {
313339
Map<String, Object> response = new HashMap<>();
314340
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/reactive/ReactiveOAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 45 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@
6464
*
6565
* @author Madhura Bhave
6666
* @author Artsiom Yudovin
67+
* @author HaiTao Zhang
6768
*/
6869
class ReactiveOAuth2ResourceServerAutoConfigurationTests {
6970

@@ -94,14 +95,49 @@ void autoConfigurationShouldConfigureResourceServer() {
9495
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws IOException {
9596
this.server = new MockWebServer();
9697
this.server.start();
97-
String issuer = this.server.url("").toString();
98+
String path = "test";
99+
String issuer = this.server.url(path).toString();
98100
String cleanIssuerPath = cleanIssuerPath(issuer);
99101
setupMockResponse(cleanIssuerPath);
102+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
103+
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
104+
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
105+
assertFilterConfiguredWithJwtAuthenticationManager(context);
106+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
107+
});
108+
assertThat(this.server.getRequestCount()).isEqualTo(1);
109+
}
110+
111+
@Test
112+
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
113+
this.server = new MockWebServer();
114+
this.server.start();
115+
String issuer = this.server.url("").toString();
116+
String cleanIssuerPath = cleanIssuerPath(issuer);
117+
setupMockResponsesWithErrors(cleanIssuerPath, 1);
118+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
119+
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
120+
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
121+
assertFilterConfiguredWithJwtAuthenticationManager(context);
122+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
123+
});
124+
assertThat(this.server.getRequestCount()).isEqualTo(2);
125+
}
126+
127+
@Test
128+
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
129+
this.server = new MockWebServer();
130+
this.server.start();
131+
String issuer = this.server.url("").toString();
132+
String cleanIssuerPath = cleanIssuerPath(issuer);
133+
setupMockResponsesWithErrors(cleanIssuerPath, 2);
100134
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
101135
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
102136
assertThat(context).hasSingleBean(NimbusReactiveJwtDecoder.class);
103137
assertFilterConfiguredWithJwtAuthenticationManager(context);
138+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
104139
});
140+
assertThat(this.server.getRequestCount()).isEqualTo(3);
105141
}
106142

107143
@Test
@@ -322,6 +358,14 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
322358
this.server.enqueue(mockResponse);
323359
}
324360

361+
private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException {
362+
for (int i = 0; i < errorResponseCount; i++) {
363+
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
364+
this.server.enqueue(emptyResponse);
365+
}
366+
setupMockResponse(issuer);
367+
}
368+
325369
private Map<String, Object> getResponse(String issuer) {
326370
Map<String, Object> response = new HashMap<>();
327371
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");

spring-boot-project/spring-boot-autoconfigure/src/test/java/org/springframework/boot/autoconfigure/security/oauth2/resource/servlet/OAuth2ResourceServerAutoConfigurationTests.java

Lines changed: 46 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@
5858
*
5959
* @author Madhura Bhave
6060
* @author Artsiom Yudovin
61+
* @author HaiTao Zhang
6162
*/
6263
class OAuth2ResourceServerAutoConfigurationTests {
6364

@@ -114,14 +115,48 @@ void autoConfigurationShouldConfigureResourceServerWithJwsAlgorithm() {
114115
void autoConfigurationShouldConfigureResourceServerUsingOidcIssuerUri() throws Exception {
115116
this.server = new MockWebServer();
116117
this.server.start();
117-
String issuer = this.server.url("").toString();
118+
String path = "test";
119+
String issuer = this.server.url(path).toString();
118120
String cleanIssuerPath = cleanIssuerPath(issuer);
119121
setupMockResponse(cleanIssuerPath);
120122
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
121-
+ this.server.getHostName() + ":" + this.server.getPort()).run((context) -> {
123+
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
122124
assertThat(context).hasSingleBean(JwtDecoder.class);
123-
assertThat(getBearerTokenFilter(context)).isNotNull();
125+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
126+
});
127+
assertThat(this.server.getRequestCount()).isEqualTo(1);
128+
}
129+
130+
@Test
131+
void autoConfigurationShouldConfigureResourceServerUsingOidcRfc8414IssuerUri() throws Exception {
132+
this.server = new MockWebServer();
133+
this.server.start();
134+
String path = "test";
135+
String issuer = this.server.url(path).toString();
136+
String cleanIssuerPath = cleanIssuerPath(issuer);
137+
setupMockResponsesWithErrors(cleanIssuerPath, 1);
138+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
139+
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
140+
assertThat(context).hasSingleBean(JwtDecoder.class);
141+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
124142
});
143+
assertThat(this.server.getRequestCount()).isEqualTo(2);
144+
}
145+
146+
@Test
147+
void autoConfigurationShouldConfigureResourceServerUsingOAuthIssuerUri() throws Exception {
148+
this.server = new MockWebServer();
149+
this.server.start();
150+
String path = "test";
151+
String issuer = this.server.url(path).toString();
152+
String cleanIssuerPath = cleanIssuerPath(issuer);
153+
setupMockResponsesWithErrors(cleanIssuerPath, 2);
154+
this.contextRunner.withPropertyValues("spring.security.oauth2.resourceserver.jwt.issuer-uri=http://"
155+
+ this.server.getHostName() + ":" + this.server.getPort() + "/" + path).run((context) -> {
156+
assertThat(context).hasSingleBean(JwtDecoder.class);
157+
assertThat(context.containsBean("jwtDecoderByIssuerUri")).isTrue();
158+
});
159+
assertThat(this.server.getRequestCount()).isEqualTo(3);
125160
}
126161

127162
@Test
@@ -306,6 +341,14 @@ private void setupMockResponse(String issuer) throws JsonProcessingException {
306341
this.server.enqueue(mockResponse);
307342
}
308343

344+
private void setupMockResponsesWithErrors(String issuer, int errorResponseCount) throws JsonProcessingException {
345+
for (int i = 0; i < errorResponseCount; i++) {
346+
MockResponse emptyResponse = new MockResponse().setResponseCode(HttpStatus.NOT_FOUND.value());
347+
this.server.enqueue(emptyResponse);
348+
}
349+
setupMockResponse(issuer);
350+
}
351+
309352
private Map<String, Object> getResponse(String issuer) {
310353
Map<String, Object> response = new HashMap<>();
311354
response.put("authorization_endpoint", "https://example.com/o/oauth2/v2/auth");

0 commit comments

Comments
 (0)