Skip to content

Commit 4c4ef9d

Browse files
author
Steve Riesenberg
committed
1 parent d7149a1 commit 4c4ef9d

File tree

9 files changed

+82
-68
lines changed

9 files changed

+82
-68
lines changed

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/AbstractOAuth2AuthorizationServerMetadata.java

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -15,18 +15,14 @@
1515
*/
1616
package org.springframework.security.oauth2.core;
1717

18+
import org.springframework.util.Assert;
19+
1820
import java.io.Serializable;
1921
import java.net.URI;
2022
import java.net.URL;
21-
import java.util.Collections;
22-
import java.util.LinkedHashMap;
23-
import java.util.LinkedList;
24-
import java.util.List;
25-
import java.util.Map;
23+
import java.util.*;
2624
import java.util.function.Consumer;
2725

28-
import org.springframework.util.Assert;
29-
3026
/**
3127
* A base representation of OAuth 2.0 Authorization Server metadata,
3228
* returned by an endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0.
@@ -138,16 +134,6 @@ public B jwkSetUrl(String jwkSetUrl) {
138134
return claim(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, jwkSetUrl);
139135
}
140136

141-
/**
142-
* Use this {@code userinfo_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
143-
*
144-
* @param userInfoEndpoint the {@code URL} of the OAuth 2.0 UserInfo Endpoint
145-
* @return the {@link AbstractBuilder} for further configuration
146-
*/
147-
public B userInfoEndpoint(String userInfoEndpoint) {
148-
return claim(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT, userInfoEndpoint);
149-
}
150-
151137
/**
152138
* Add this OAuth 2.0 {@code scope} to the collection of {@code scopes_supported}
153139
* in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, RECOMMENDED.
@@ -353,9 +339,6 @@ protected void validate() {
353339
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) != null) {
354340
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI), "jwksUri must be a valid URL");
355341
}
356-
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT) != null) {
357-
validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL");
358-
}
359342
if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) != null) {
360343
Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes must be of type List");
361344
Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes cannot be empty");
@@ -404,7 +387,7 @@ private void acceptClaimValues(String name, Consumer<List<String>> valuesConsume
404387
valuesConsumer.accept(values);
405388
}
406389

407-
private static void validateURL(Object url, String errorMessage) {
390+
protected static void validateURL(Object url, String errorMessage) {
408391
if (URL.class.isAssignableFrom(url.getClass())) {
409392
return;
410393
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/OAuth2AuthorizationServerMetadataClaimNames.java

Lines changed: 0 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,6 @@ public interface OAuth2AuthorizationServerMetadataClaimNames {
5151
*/
5252
String JWKS_URI = "jwks_uri";
5353

54-
/**
55-
* {@code userinfo_endpoint} - the {@code URL} of the OAuth 2.0 UserInfo Endpoint
56-
*/
57-
String USER_INFO_ENDPOINT = "userinfo_endpoint";
58-
5954
/**
6055
* {@code scopes_supported} - the OAuth 2.0 {@code scope} values supported
6156
*/

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfiguration.java

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,15 @@
1515
*/
1616
package org.springframework.security.oauth2.core.oidc;
1717

18+
import org.springframework.security.oauth2.core.AbstractOAuth2AuthorizationServerMetadata;
19+
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
20+
import org.springframework.util.Assert;
21+
1822
import java.util.LinkedList;
1923
import java.util.List;
2024
import java.util.Map;
2125
import java.util.function.Consumer;
2226

23-
import org.springframework.security.oauth2.core.AbstractOAuth2AuthorizationServerMetadata;
24-
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
25-
import org.springframework.util.Assert;
26-
2727
/**
2828
* A representation of an OpenID Provider Configuration Response,
2929
* which is returned from an Issuer's Discovery Endpoint,
@@ -118,6 +118,16 @@ public Builder idTokenSigningAlgorithms(Consumer<List<String>> signingAlgorithms
118118
return this;
119119
}
120120

121+
/**
122+
* Use this {@code userinfo_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL.
123+
*
124+
* @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
125+
* @return the {@link Builder} for further configuration
126+
*/
127+
public Builder userInfoEndpoint(String userInfoEndpoint) {
128+
return claim(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, userInfoEndpoint);
129+
}
130+
121131
/**
122132
* Validate the claims and build the {@link OidcProviderConfiguration}.
123133
* <p>
@@ -144,6 +154,9 @@ protected void validate() {
144154
Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be null");
145155
Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms must be of type List");
146156
Assert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be empty");
157+
if (getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT) != null) {
158+
validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL");
159+
}
147160
}
148161

149162
@SuppressWarnings("unchecked")

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimAccessor.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,13 +16,14 @@
1616
package org.springframework.security.oauth2.core.oidc;
1717

1818

19-
import java.util.List;
20-
2119
import org.springframework.security.oauth2.core.ClaimAccessor;
2220
import org.springframework.security.oauth2.core.OAuth2AuthorizationServerMetadataClaimAccessor;
2321
import org.springframework.security.oauth2.jose.jws.JwsAlgorithm;
2422
import org.springframework.security.oauth2.jwt.Jwt;
2523

24+
import java.net.URL;
25+
import java.util.List;
26+
2627
/**
2728
* A {@link ClaimAccessor} for the "claims" that can be returned
2829
* in the OpenID Provider Configuration Response.
@@ -56,4 +57,13 @@ default List<String> getIdTokenSigningAlgorithms() {
5657
return getClaimAsStringList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
5758
}
5859

60+
/**
61+
* Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint {@code (userinfo_endpoint)}.
62+
*
63+
* @return the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
64+
*/
65+
default URL getUserInfoEndpoint() {
66+
return getClaimAsURL(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT);
67+
}
68+
5969
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimNames.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,4 +39,9 @@ public interface OidcProviderMetadataClaimNames extends OAuth2AuthorizationServe
3939
*/
4040
String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = "id_token_signing_alg_values_supported";
4141

42+
/**
43+
* {@code userinfo_endpoint} - the {@code URL} of the OAuth 2.0 UserInfo Endpoint
44+
*/
45+
String USER_INFO_ENDPOINT = "userinfo_endpoint";
46+
4247
}

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java

Lines changed: 9 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,6 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.oidc.web;
1717

18-
import java.io.IOException;
19-
import java.util.List;
20-
import java.util.function.Consumer;
21-
22-
import javax.servlet.FilterChain;
23-
import javax.servlet.ServletException;
24-
import javax.servlet.http.HttpServletRequest;
25-
import javax.servlet.http.HttpServletResponse;
26-
2718
import org.springframework.http.HttpMethod;
2819
import org.springframework.http.MediaType;
2920
import org.springframework.http.server.ServletServerHttpResponse;
@@ -42,6 +33,14 @@
4233
import org.springframework.web.filter.OncePerRequestFilter;
4334
import org.springframework.web.util.UriComponentsBuilder;
4435

36+
import javax.servlet.FilterChain;
37+
import javax.servlet.ServletException;
38+
import javax.servlet.http.HttpServletRequest;
39+
import javax.servlet.http.HttpServletResponse;
40+
import java.io.IOException;
41+
import java.util.List;
42+
import java.util.function.Consumer;
43+
4544
/**
4645
* A {@code Filter} that processes OpenID Provider Configuration Requests.
4746
*
@@ -88,7 +87,7 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
8887
.tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint()))
8988
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
9089
.jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint()))
91-
.userInfoEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getOidcUserInfoEndpoint()))
90+
.userInfoEndpoint(asUrl(issuer, this.providerSettings.getOidcUserInfoEndpoint()))
9291
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
9392
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
9493
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())

oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,15 +15,6 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.web;
1717

18-
import java.io.IOException;
19-
import java.util.List;
20-
import java.util.function.Consumer;
21-
22-
import javax.servlet.FilterChain;
23-
import javax.servlet.ServletException;
24-
import javax.servlet.http.HttpServletRequest;
25-
import javax.servlet.http.HttpServletResponse;
26-
2718
import org.springframework.http.HttpMethod;
2819
import org.springframework.http.MediaType;
2920
import org.springframework.http.server.ServletServerHttpResponse;
@@ -40,6 +31,14 @@
4031
import org.springframework.web.filter.OncePerRequestFilter;
4132
import org.springframework.web.util.UriComponentsBuilder;
4233

34+
import javax.servlet.FilterChain;
35+
import javax.servlet.ServletException;
36+
import javax.servlet.http.HttpServletRequest;
37+
import javax.servlet.http.HttpServletResponse;
38+
import java.io.IOException;
39+
import java.util.List;
40+
import java.util.function.Consumer;
41+
4342
/**
4443
* A {@code Filter} that processes OAuth 2.0 Authorization Server Metadata Requests.
4544
*
@@ -86,7 +85,6 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
8685
.tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint()))
8786
.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
8887
.jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint()))
89-
.userInfoEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getOidcUserInfoEndpoint()))
9088
.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
9189
.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
9290
.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfigurationTests.java

Lines changed: 19 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,17 +15,12 @@
1515
*/
1616
package org.springframework.security.oauth2.core.oidc;
1717

18-
import java.net.URL;
19-
import java.util.ArrayList;
20-
import java.util.Collections;
21-
import java.util.HashMap;
22-
import java.util.List;
23-
import java.util.Map;
24-
2518
import org.junit.Test;
26-
2719
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
2820

21+
import java.net.URL;
22+
import java.util.*;
23+
2924
import static org.assertj.core.api.Assertions.assertThat;
3025
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3126

@@ -59,6 +54,7 @@ public void buildWhenAllRequiredClaimsAndAdditionalClaimsThenCreated() {
5954
.grantType("client_credentials")
6055
.subjectType("public")
6156
.idTokenSigningAlgorithm("RS256")
57+
.userInfoEndpoint("https://example.com/issuer1/userinfo")
6258
.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
6359
.claim("a-claim", "a-value")
6460
.build();
@@ -72,6 +68,7 @@ public void buildWhenAllRequiredClaimsAndAdditionalClaimsThenCreated() {
7268
assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials");
7369
assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
7470
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
71+
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
7572
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
7673
assertThat(providerConfiguration.<String>getClaim("a-claim")).isEqualTo("a-value");
7774
}
@@ -112,6 +109,7 @@ public void buildWhenClaimsProvidedThenCreated() {
112109
claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code"));
113110
claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
114111
claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
112+
claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "https://example.com/issuer1/userinfo");
115113
claims.put("some-claim", "some-value");
116114

117115
OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
@@ -125,6 +123,7 @@ public void buildWhenClaimsProvidedThenCreated() {
125123
assertThat(providerConfiguration.getGrantTypes()).isNull();
126124
assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
127125
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
126+
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
128127
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
129128
assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
130129
}
@@ -140,6 +139,7 @@ public void buildWhenClaimsProvidedWithUrlsThenCreated() {
140139
claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code"));
141140
claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
142141
claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
142+
claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, url("https://example.com/issuer1/userinfo"));
143143
claims.put("some-claim", "some-value");
144144

145145
OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
@@ -153,6 +153,7 @@ public void buildWhenClaimsProvidedWithUrlsThenCreated() {
153153
assertThat(providerConfiguration.getGrantTypes()).isNull();
154154
assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
155155
assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
156+
assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
156157
assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
157158
assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
158159
}
@@ -380,6 +381,16 @@ public void buildWhenIdTokenSigningAlgorithmsEmptyListThenThrowIllegalArgumentEx
380381
.withMessageContaining("idTokenSigningAlgorithms cannot be empty");
381382
}
382383

384+
@Test
385+
public void buildWhenUserInfoEndpointNotUrlThenThrowIllegalArgumentException() {
386+
OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder
387+
.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "not an url"));
388+
389+
assertThatIllegalArgumentException()
390+
.isThrownBy(builder::build)
391+
.withMessageStartingWith("userInfoEndpoint must be a valid URL");
392+
}
393+
383394
@Test
384395
public void responseTypesWhenAddingOrRemovingThenCorrectValues() {
385396
OidcProviderConfiguration configuration = this.minimalConfigurationBuilder

oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,23 @@
1515
*/
1616
package org.springframework.security.oauth2.server.authorization.oidc.web;
1717

18-
import javax.servlet.FilterChain;
19-
import javax.servlet.http.HttpServletRequest;
20-
import javax.servlet.http.HttpServletResponse;
21-
2218
import org.junit.After;
2319
import org.junit.Test;
24-
2520
import org.springframework.http.MediaType;
2621
import org.springframework.mock.web.MockHttpServletRequest;
2722
import org.springframework.mock.web.MockHttpServletResponse;
2823
import org.springframework.security.oauth2.server.authorization.config.ProviderSettings;
2924
import org.springframework.security.oauth2.server.authorization.context.ProviderContext;
3025
import org.springframework.security.oauth2.server.authorization.context.ProviderContextHolder;
3126

27+
import javax.servlet.FilterChain;
28+
import javax.servlet.http.HttpServletRequest;
29+
import javax.servlet.http.HttpServletResponse;
30+
3231
import static org.assertj.core.api.Assertions.assertThat;
3332
import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException;
3433
import static org.mockito.ArgumentMatchers.any;
35-
import static org.mockito.Mockito.mock;
36-
import static org.mockito.Mockito.verify;
37-
import static org.mockito.Mockito.verifyNoInteractions;
34+
import static org.mockito.Mockito.*;
3835

3936
/**
4037
* Tests for {@link OidcProviderConfigurationEndpointFilter}.
@@ -94,12 +91,14 @@ public void doFilterWhenConfigurationRequestThenConfigurationResponse() throws E
9491
String authorizationEndpoint = "/oauth2/v1/authorize";
9592
String tokenEndpoint = "/oauth2/v1/token";
9693
String jwkSetEndpoint = "/oauth2/v1/jwks";
94+
String userInfoEndpoint = "/userinfo";
9795

9896
ProviderSettings providerSettings = ProviderSettings.builder()
9997
.issuer(issuer)
10098
.authorizationEndpoint(authorizationEndpoint)
10199
.tokenEndpoint(tokenEndpoint)
102100
.jwkSetEndpoint(jwkSetEndpoint)
101+
.oidcUserInfoEndpoint(userInfoEndpoint)
103102
.build();
104103
ProviderContextHolder.setProviderContext(new ProviderContext(providerSettings, null));
105104
OidcProviderConfigurationEndpointFilter filter =
@@ -126,6 +125,7 @@ public void doFilterWhenConfigurationRequestThenConfigurationResponse() throws E
126125
assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]");
127126
assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
128127
assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
128+
assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/issuer1/userinfo\"");
129129
assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
130130
}
131131

0 commit comments

Comments
 (0)