Răsfoiți Sursa

Polish gh-489

Steve Riesenberg 3 ani în urmă
părinte
comite
5412f10ff8

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -138,16 +138,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
 			return claim(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, jwkSetUrl);
 		}
 
-		/**
-		 * Use this {@code userinfo_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL.
-		 *
-		 * @param userInfoEndpoint the {@code URL} of the OAuth 2.0 UserInfo Endpoint
-		 * @return the {@link AbstractBuilder} for further configuration
-		 */
-		public B userInfoEndpoint(String userInfoEndpoint) {
-			return claim(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT, userInfoEndpoint);
-		}
-
 		/**
 		 * Add this OAuth 2.0 {@code scope} to the collection of {@code scopes_supported}
 		 * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, RECOMMENDED.
@@ -353,9 +343,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
 			if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) != null) {
 				validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI), "jwksUri must be a valid URL");
 			}
-			if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT) != null) {
-				validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL");
-			}
 			if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) != null) {
 				Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes must be of type List");
 				Assert.notEmpty((List<?>) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes cannot be empty");
@@ -404,7 +391,7 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth
 			valuesConsumer.accept(values);
 		}
 
-		private static void validateURL(Object url, String errorMessage) {
+		protected static void validateURL(Object url, String errorMessage) {
 			if (URL.class.isAssignableFrom(url.getClass())) {
 				return;
 			}

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

@@ -51,11 +51,6 @@ public interface OAuth2AuthorizationServerMetadataClaimNames {
 	 */
 	String JWKS_URI = "jwks_uri";
 
-	/**
-	 * {@code userinfo_endpoint} - the {@code URL} of the OAuth 2.0 UserInfo Endpoint
-	 */
-	String USER_INFO_ENDPOINT = "userinfo_endpoint";
-
 	/**
 	 * {@code scopes_supported} - the OAuth 2.0 {@code scope} values supported
 	 */

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -118,6 +118,17 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
 			return this;
 		}
 
+		/**
+		 * Use this {@code userinfo_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL.
+		 *
+		 * @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
+		 * @return the {@link Builder} for further configuration
+		 * @since 0.2.2
+		 */
+		public Builder userInfoEndpoint(String userInfoEndpoint) {
+			return claim(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, userInfoEndpoint);
+		}
+
 		/**
 		 * Validate the claims and build the {@link OidcProviderConfiguration}.
 		 * <p>
@@ -144,6 +155,9 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization
 			Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be null");
 			Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms must be of type List");
 			Assert.notEmpty((List<?>) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be empty");
+			if (getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT) != null) {
+				validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL");
+			}
 		}
 
 		@SuppressWarnings("unchecked")

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,6 +16,7 @@
 package org.springframework.security.oauth2.core.oidc;
 
 
+import java.net.URL;
 import java.util.List;
 
 import org.springframework.security.oauth2.core.ClaimAccessor;
@@ -56,4 +57,14 @@ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationSe
 		return getClaimAsStringList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED);
 	}
 
+	/**
+	 * Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint {@code (userinfo_endpoint)}.
+	 *
+	 * @return the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
+	 * @since 0.2.2
+	 */
+	default URL getUserInfoEndpoint() {
+		return getClaimAsURL(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT);
+	}
+
 }

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -39,4 +39,10 @@ public interface OidcProviderMetadataClaimNames extends OAuth2AuthorizationServe
 	 */
 	String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = "id_token_signing_alg_values_supported";
 
+	/**
+	 * {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint
+	 * @since 0.2.2
+	 */
+	String USER_INFO_ENDPOINT = "userinfo_endpoint";
+
 }

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

@@ -88,7 +88,7 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques
 				.tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint()))
 				.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
 				.jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint()))
-				.userInfoEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getOidcUserInfoEndpoint()))
+				.userInfoEndpoint(asUrl(issuer, this.providerSettings.getOidcUserInfoEndpoint()))
 				.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
 				.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
 				.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())

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

@@ -86,7 +86,6 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP
 				.tokenEndpoint(asUrl(issuer, this.providerSettings.getTokenEndpoint()))
 				.tokenEndpointAuthenticationMethods(clientAuthenticationMethods())
 				.jwkSetUrl(asUrl(issuer, this.providerSettings.getJwkSetEndpoint()))
-				.userInfoEndpoint(asUrl(this.providerSettings.getIssuer(), this.providerSettings.getOidcUserInfoEndpoint()))
 				.responseType(OAuth2AuthorizationResponseType.CODE.getValue())
 				.grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue())
 				.grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())

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

@@ -1,5 +1,5 @@
 /*
- * Copyright 2020-2021 the original author or authors.
+ * Copyright 2020-2022 the original author or authors.
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -59,6 +59,7 @@ public class OidcProviderConfigurationTests {
 				.grantType("client_credentials")
 				.subjectType("public")
 				.idTokenSigningAlgorithm("RS256")
+				.userInfoEndpoint("https://example.com/issuer1/userinfo")
 				.tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue())
 				.claim("a-claim", "a-value")
 				.build();
@@ -72,6 +73,7 @@ public class OidcProviderConfigurationTests {
 		assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials");
 		assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
 		assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
+		assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
 		assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue());
 		assertThat(providerConfiguration.<String>getClaim("a-claim")).isEqualTo("a-value");
 	}
@@ -112,6 +114,7 @@ public class OidcProviderConfigurationTests {
 		claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code"));
 		claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
 		claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
+		claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "https://example.com/issuer1/userinfo");
 		claims.put("some-claim", "some-value");
 
 		OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
@@ -125,6 +128,7 @@ public class OidcProviderConfigurationTests {
 		assertThat(providerConfiguration.getGrantTypes()).isNull();
 		assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
 		assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
+		assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
 		assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
 		assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
 	}
@@ -140,6 +144,7 @@ public class OidcProviderConfigurationTests {
 		claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code"));
 		claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public"));
 		claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256"));
+		claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, url("https://example.com/issuer1/userinfo"));
 		claims.put("some-claim", "some-value");
 
 		OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build();
@@ -153,6 +158,7 @@ public class OidcProviderConfigurationTests {
 		assertThat(providerConfiguration.getGrantTypes()).isNull();
 		assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public");
 		assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256");
+		assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/issuer1/userinfo"));
 		assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull();
 		assertThat(providerConfiguration.<String>getClaim("some-claim")).isEqualTo("some-value");
 	}
@@ -380,6 +386,16 @@ public class OidcProviderConfigurationTests {
 				.withMessageContaining("idTokenSigningAlgorithms cannot be empty");
 	}
 
+	@Test
+	public void buildWhenUserInfoEndpointNotUrlThenThrowIllegalArgumentException() {
+		OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder
+				.claims((claims) -> claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "not an url"));
+
+		assertThatIllegalArgumentException()
+				.isThrownBy(builder::build)
+				.withMessage("userInfoEndpoint must be a valid URL");
+	}
+
 	@Test
 	public void responseTypesWhenAddingOrRemovingThenCorrectValues() {
 		OidcProviderConfiguration configuration = this.minimalConfigurationBuilder

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

@@ -94,12 +94,14 @@ public class OidcProviderConfigurationEndpointFilterTests {
 		String authorizationEndpoint = "/oauth2/v1/authorize";
 		String tokenEndpoint = "/oauth2/v1/token";
 		String jwkSetEndpoint = "/oauth2/v1/jwks";
+		String userInfoEndpoint = "/userinfo";
 
 		ProviderSettings providerSettings = ProviderSettings.builder()
 				.issuer(issuer)
 				.authorizationEndpoint(authorizationEndpoint)
 				.tokenEndpoint(tokenEndpoint)
 				.jwkSetEndpoint(jwkSetEndpoint)
+				.oidcUserInfoEndpoint(userInfoEndpoint)
 				.build();
 		ProviderContextHolder.setProviderContext(new ProviderContext(providerSettings, null));
 		OidcProviderConfigurationEndpointFilter filter =
@@ -126,6 +128,7 @@ public class OidcProviderConfigurationEndpointFilterTests {
 		assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]");
 		assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]");
 		assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]");
+		assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/issuer1/userinfo\"");
 		assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]");
 	}