diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java index a9db7b7..3adbe88 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OAuth2AuthorizationServerConfigurer.java @@ -54,9 +54,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; import org.springframework.util.Assert; import org.springframework.util.StringUtils; -import java.net.MalformedURLException; import java.net.URI; -import java.net.URISyntaxException; import java.util.Arrays; import java.util.LinkedHashMap; import java.util.List; @@ -66,12 +64,16 @@ import java.util.Map; * An {@link AbstractHttpConfigurer} for OAuth 2.0 Authorization Server support. * * @author Joe Grandja + * @author Daniel Garnier-Moiroux * @since 0.0.1 * @see AbstractHttpConfigurer * @see RegisteredClientRepository * @see OAuth2AuthorizationService * @see OAuth2AuthorizationEndpointFilter * @see OAuth2TokenEndpointFilter + * @see OAuth2TokenRevocationEndpointFilter + * @see JwkSetEndpointFilter + * @see OidcProviderConfigurationEndpointFilter * @see OAuth2ClientAuthenticationFilter */ public final class OAuth2AuthorizationServerConfigurer> @@ -147,15 +149,17 @@ public final class OAuth2AuthorizationServerConfigurer getEndpointMatchers() { - // TODO: use ProviderSettings instead + // TODO Initialize matchers using URI's from ProviderSettings return Arrays.asList(this.authorizationEndpointMatcher, this.tokenEndpointMatcher, - this.tokenRevocationEndpointMatcher, this.jwkSetEndpointMatcher, this.oidcProviderConfigurationEndpointMatcher); + this.tokenRevocationEndpointMatcher, this.jwkSetEndpointMatcher, + this.oidcProviderConfigurationEndpointMatcher); } @Override public void init(B builder) { ProviderSettings providerSettings = getProviderSettings(builder); validateProviderSettings(providerSettings); + OAuth2ClientAuthenticationProvider clientAuthenticationProvider = new OAuth2ClientAuthenticationProvider( getRegisteredClientRepository(builder), @@ -208,14 +212,16 @@ public final class OAuth2AuthorizationServerConfigurerIssue gh-9146 - */ -final public class ObjectToSetStringConverter2 implements ConditionalGenericConverter { - - @Override - public Set getConvertibleTypes() { - Set convertibleTypes = new LinkedHashSet<>(); - convertibleTypes.add(new GenericConverter.ConvertiblePair(Object.class, Set.class)); - return convertibleTypes; - } - - @Override - public boolean matches(TypeDescriptor sourceType, TypeDescriptor targetType) { - if (targetType.getElementTypeDescriptor() == null - || targetType.getElementTypeDescriptor().getType().equals(String.class) || sourceType == null - || ClassUtils.isAssignable(sourceType.getType(), targetType.getElementTypeDescriptor().getType())) { - return true; - } - return false; - } - - @Override - public Object convert(Object source, TypeDescriptor sourceType, TypeDescriptor targetType) { - if (source == null) { - return null; - } - if (source instanceof Set) { - Set sourceList = (Set) source; - for (Object entry: sourceList) { - if (entry instanceof String) { - return source; - } - } - } - if (source instanceof Collection) { - Collection results = new LinkedHashSet<>(); - for (Object object : ((Collection) source)) { - if (object != null) { - results.add(object.toString()); - } - } - return results; - } - return Collections.singleton(source.toString()); - } -} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfiguration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfiguration.java index 28de8b1..5c0c2af 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfiguration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfiguration.java @@ -23,9 +23,9 @@ import java.net.URI; import java.net.URL; import java.util.Collections; import java.util.LinkedHashMap; -import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; import java.util.Map; -import java.util.Set; import java.util.function.Consumer; /** @@ -39,13 +39,11 @@ import java.util.function.Consumer; * @see OidcProviderMetadataClaimAccessor * @see 4.2. OpenID Provider Configuration Response */ -public class OidcProviderConfiguration implements OidcProviderMetadataClaimAccessor, Serializable { +public final class OidcProviderConfiguration implements OidcProviderMetadataClaimAccessor, Serializable { private static final long serialVersionUID = Version.SERIAL_VERSION_UID; - private final Map claims; private OidcProviderConfiguration(Map claims) { - Assert.notEmpty(claims, "claims cannot be empty"); this.claims = Collections.unmodifiableMap(new LinkedHashMap<>(claims)); } @@ -60,7 +58,7 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces } /** - * Constructs a new empty {@link Builder}. + * Constructs a new {@link Builder} with empty claims. * * @return the {@link Builder} */ @@ -68,7 +66,6 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces return new Builder(); } - /** * Constructs a new {@link Builder} with the provided claims. * @@ -82,13 +79,8 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces /** * Helps configure an {@link OidcProviderConfiguration} - * - * @author Daniel Garnier-Moiroux - * @since 0.1.0 - * @see OpenID Connect Discovery 1.0 - * for required claims */ - public static final class Builder { + public static class Builder { private final Map claims = new LinkedHashMap<>(); private Builder() { @@ -97,7 +89,7 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces /** * Use this {@code issuer} in the resulting {@link OidcProviderConfiguration}, REQUIRED. * - * @param issuer the issuer URI + * @param issuer the URL of the OpenID Provider's Issuer Identifier * @return the {@link Builder} for further configuration */ public Builder issuer(String issuer) { @@ -124,6 +116,29 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces return claim(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, tokenEndpoint); } + /** + * Add this Authentication Method to the collection of {@code token_endpoint_auth_methods_supported} + * in the resulting {@link OidcProviderConfiguration}, OPTIONAL. + * + * @param authenticationMethod the OAuth 2.0 Authentication Method supported by the Token endpoint + * @return the {@link Builder} for further configuration + */ + public Builder tokenEndpointAuthenticationMethod(String authenticationMethod) { + addClaimToClaimList(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod); + return this; + } + + /** + * A {@code Consumer} of the Token Endpoint Authentication Method(s) allowing the ability to add, replace, or remove. + * + * @param authenticationMethodsConsumer a {@code Consumer} of the Token Endpoint Authentication Method(s) + * @return the {@link Builder} for further configuration + */ + public Builder tokenEndpointAuthenticationMethods(Consumer> authenticationMethodsConsumer) { + acceptClaimValues(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer); + return this; + } + /** * Use this {@code jwks_uri} in the resulting {@link OidcProviderConfiguration}, REQUIRED. * @@ -138,11 +153,11 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces * Add this Response Type to the collection of {@code response_types_supported} in the resulting * {@link OidcProviderConfiguration}, REQUIRED. * - * @param responseType the OAuth 2.0 {@code response_type} values that the OpenID Provider supports + * @param responseType the OAuth 2.0 {@code response_type} value that the OpenID Provider supports * @return the {@link Builder} for further configuration */ public Builder responseType(String responseType) { - addClaimToClaimSet(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseType); + addClaimToClaimList(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseType); return this; } @@ -152,54 +167,8 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces * @param responseTypesConsumer a {@code Consumer} of the Response Type(s) * @return the {@link Builder} for further configuration */ - public Builder responseTypes(Consumer> responseTypesConsumer) { - applyToClaim(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseTypesConsumer); - return this; - } - - /** - * Add this Subject Type to the collection of {@code subject_types_supported} in the resulting - * {@link OidcProviderConfiguration}, REQUIRED. - * - * @param subjectType the Subject Identifiers that the OpenID Provider supports - * @return the {@link Builder} for further configuration - */ - public Builder subjectType(String subjectType) { - addClaimToClaimSet(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectType); - return this; - } - - /** - * A {@code Consumer} of the Subject Types(s) allowing the ability to add, replace, or remove. - * - * @param subjectTypesConsumer a {@code Consumer} of the Subject Types(s) - * @return the {@link Builder} for further configuration - */ - public Builder subjectTypes(Consumer> subjectTypesConsumer) { - applyToClaim(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectTypesConsumer); - return this; - } - - /** - * Add this Scope to the collection of {@code scopes_supported} in the resulting - * {@link OidcProviderConfiguration}, RECOMMENDED. - * - * @param scope the OAuth 2.0 {@code scopes} values that the OpenID Provider supports - * @return the {@link Builder} for further configuration - */ - public Builder scope(String scope) { - addClaimToClaimSet(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, scope); - return this; - } - - /** - * A {@code Consumer} of the Scopes(s) allowing the ability to add, replace, or remove. - * - * @param scopesConsumer a {@code Consumer} of the Scopes(s) - * @return the {@link Builder} for further configuration - */ - public Builder scopes(Consumer> scopesConsumer) { - applyToClaim(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, scopesConsumer); + public Builder responseTypes(Consumer> responseTypesConsumer) { + acceptClaimValues(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseTypesConsumer); return this; } @@ -207,11 +176,11 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces * Add this Grant Type to the collection of {@code grant_types_supported} in the resulting * {@link OidcProviderConfiguration}, OPTIONAL. * - * @param grantType the OAuth 2.0 {@code grant_type} values that the OpenID Provider supports + * @param grantType the OAuth 2.0 {@code grant_type} value that the OpenID Provider supports * @return the {@link Builder} for further configuration */ public Builder grantType(String grantType) { - addClaimToClaimSet(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantType); + addClaimToClaimList(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantType); return this; } @@ -221,39 +190,62 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces * @param grantTypesConsumer a {@code Consumer} of the Grant Type(s) * @return the {@link Builder} for further configuration */ - public Builder grantTypes(Consumer> grantTypesConsumer) { - applyToClaim(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantTypesConsumer); + public Builder grantTypes(Consumer> grantTypesConsumer) { + acceptClaimValues(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, grantTypesConsumer); return this; } /** - * Add this Authentication Method to the collection of {@code token_endpoint_auth_methods_supported} - * in the resulting {@link OidcProviderConfiguration}, OPTIONAL. + * Add this Subject Type to the collection of {@code subject_types_supported} in the resulting + * {@link OidcProviderConfiguration}, REQUIRED. * - * @param authenticationMethod the OAuth 2.0 Authentication Method supported by the Token endpoint + * @param subjectType the Subject Type that the OpenID Provider supports * @return the {@link Builder} for further configuration */ - public Builder tokenEndpointAuthenticationMethod(String authenticationMethod) { - addClaimToClaimSet(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod); + public Builder subjectType(String subjectType) { + addClaimToClaimList(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectType); return this; } /** - * A {@code Consumer} of the Token Endpoint Authentication Method(s) allowing the ability to add, replace, or remove. + * A {@code Consumer} of the Subject Types(s) allowing the ability to add, replace, or remove. * - * @param authenticationMethodsConsumer a {@code Consumer} of the Token Endpoint Authentication Method(s) + * @param subjectTypesConsumer a {@code Consumer} of the Subject Types(s) * @return the {@link Builder} for further configuration */ - public Builder tokenEndpointAuthenticationMethods(Consumer> authenticationMethodsConsumer) { - applyToClaim(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer); + public Builder subjectTypes(Consumer> subjectTypesConsumer) { + acceptClaimValues(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, subjectTypesConsumer); return this; } /** - * Use this claim in the resulting {@link OidcProviderConfiguration} + * Add this Scope to the collection of {@code scopes_supported} in the resulting + * {@link OidcProviderConfiguration}, RECOMMENDED. * - * @param name The claim name - * @param value The claim value + * @param scope the OAuth 2.0 {@code scope} value that the OpenID Provider supports + * @return the {@link Builder} for further configuration + */ + public Builder scope(String scope) { + addClaimToClaimList(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, scope); + return this; + } + + /** + * A {@code Consumer} of the Scopes(s) allowing the ability to add, replace, or remove. + * + * @param scopesConsumer a {@code Consumer} of the Scopes(s) + * @return the {@link Builder} for further configuration + */ + public Builder scopes(Consumer> scopesConsumer) { + acceptClaimValues(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, scopesConsumer); + return this; + } + + /** + * Use this claim in the resulting {@link OidcProviderConfiguration}. + * + * @param name the claim name + * @param value the claim value * @return the {@link Builder} for further configuration */ public Builder claim(String name, Object value) { @@ -267,7 +259,7 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces * Provides access to every {@link #claim(String, Object)} declared so far with * the possibility to add, replace, or remove. * - * @param claimsConsumer the consumer + * @param claimsConsumer a {@code Consumer} of the claims * @return the {@link Builder} for further configurations */ public Builder claims(Consumer> claimsConsumer) { @@ -276,15 +268,13 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces } /** - * Validate the claims and build the {@link OidcProviderConfiguration}. The following claims are REQUIRED: - * - issuer - * - authorization_endpoint - * - token_endpoint - * - jwks_uri - * - response_types_supported - * - subject_types_supported + * Validate the claims and build the {@link OidcProviderConfiguration}. + *

+ * The following claims are REQUIRED: + * {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri}, + * {@code response_types_supported} and {@code subject_types_supported}. * - * @return The constructed {@link OidcProviderConfiguration} + * @return the {@link OidcProviderConfiguration} */ public OidcProviderConfiguration build() { validateClaims(); @@ -298,34 +288,43 @@ public class OidcProviderConfiguration implements OidcProviderMetadataClaimAcces validateURL(this.claims.get(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint must be a valid URL"); Assert.notNull(this.claims.get(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint cannot be null"); validateURL(this.claims.get(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint must be a valid URL"); - Assert.notNull(this.claims.get(OidcProviderMetadataClaimNames.JWKS_URI), "jwkSetUri cannot be null"); - validateURL(this.claims.get(OidcProviderMetadataClaimNames.JWKS_URI), "jwkSetUri must be a valid URL"); - Assert.notEmpty((Set) this.claims.get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be empty"); - Assert.notEmpty((Set) this.claims.get(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be empty"); + Assert.notNull(this.claims.get(OidcProviderMetadataClaimNames.JWKS_URI), "jwksUri cannot be null"); + validateURL(this.claims.get(OidcProviderMetadataClaimNames.JWKS_URI), "jwksUri must be a valid URL"); + Assert.notNull(this.claims.get(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be null"); + Assert.isInstanceOf(List.class, this.claims.get(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be empty"); + Assert.notNull(this.claims.get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be null"); + Assert.isInstanceOf(List.class, this.claims.get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be empty"); } - private void validateURL(Object url, String errorMessage) { - if (url.getClass().isAssignableFrom(URL.class)) return; + private static void validateURL(Object url, String errorMessage) { + if (URL.class.isAssignableFrom(url.getClass())) { + return; + } try { new URI(url.toString()).toURL(); - } catch (Exception e) { - throw new IllegalArgumentException(errorMessage); + } catch (Exception ex) { + throw new IllegalArgumentException(errorMessage, ex); } - } @SuppressWarnings("unchecked") - private void addClaimToClaimSet(String name, String value) { - this.claims.putIfAbsent(name, new LinkedHashSet()); - ((Set) this.claims.get(name)).add(value); + private void addClaimToClaimList(String name, String value) { + Assert.hasText(name, "name cannot be empty"); + Assert.notNull(value, "value cannot be null"); + this.claims.computeIfAbsent(name, k -> new LinkedList()); + ((List) this.claims.get(name)).add(value); } @SuppressWarnings("unchecked") - private void applyToClaim(String name, Consumer> consumer) { - this.claims.putIfAbsent(name, new LinkedHashSet()); - Set values = (Set) this.claims.get(name); - consumer.accept(values); + private void acceptClaimValues(String name, Consumer> valuesConsumer) { + Assert.hasText(name, "name cannot be empty"); + Assert.notNull(valuesConsumer, "valuesConsumer cannot be null"); + this.claims.computeIfAbsent(name, k -> new LinkedList()); + List values = (List) this.claims.get(name); + valuesConsumer.accept(values); } } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimAccessor.java index 9287bab..2b213c0 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimAccessor.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimAccessor.java @@ -35,30 +35,30 @@ import java.util.List; public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { /** - * Returns the URL the OpenID Provider asserts as its Issuer Identifier {@code (issuer)}. + * Returns the {@code URL} the OpenID Provider asserts as its Issuer Identifier {@code (issuer)}. * - * @return the URL the OpenID Provider asserts as its Issuer Identifier + * @return the {@code URL} the OpenID Provider asserts as its Issuer Identifier */ default URL getIssuer() { - return this.getClaimAsURL(OidcProviderMetadataClaimNames.ISSUER); + return getClaimAsURL(OidcProviderMetadataClaimNames.ISSUER); } /** - * Returns the URL of the OAuth 2.0 Authorization Endpoint {@code (authorization_endpoint)}. + * Returns the {@code URL} of the OAuth 2.0 Authorization Endpoint {@code (authorization_endpoint)}. * - * @return the URL of the OAuth 2.0 Authorization Endpoint + * @return the {@code URL} of the OAuth 2.0 Authorization Endpoint */ default URL getAuthorizationEndpoint() { - return this.getClaimAsURL(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT); + return getClaimAsURL(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT); } /** - * Returns the URL of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}. + * Returns the {@code URL} of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}. * - * @return the URL of the OAuth 2.0 Token Endpoint + * @return the {@code URL} of the OAuth 2.0 Token Endpoint */ default URL getTokenEndpoint() { - return this.getClaimAsURL(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT); + return getClaimAsURL(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT); } /** @@ -67,16 +67,16 @@ public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { * @return the client authentication methods supported by the OAuth 2.0 Token Endpoint */ default List getTokenEndpointAuthenticationMethods() { - return this.getClaimAsStringList(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED); + return getClaimAsStringList(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED); } /** - * Returns the URL of the JSON Web Key Set {@code (jwks_uri)}. + * Returns the {@code URL} of the JSON Web Key Set {@code (jwks_uri)}. * - * @return the URL of the JSON Web Key Set + * @return the {@code URL} of the JSON Web Key Set */ default URL getJwksUri() { - return this.getClaimAsURL(OidcProviderMetadataClaimNames.JWKS_URI); + return getClaimAsURL(OidcProviderMetadataClaimNames.JWKS_URI); } /** @@ -85,7 +85,7 @@ public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { * @return the OAuth 2.0 {@code response_type} values supported */ default List getResponseTypes() { - return this.getClaimAsStringList(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); + return getClaimAsStringList(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); } /** @@ -94,7 +94,7 @@ public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { * @return the OAuth 2.0 {@code grant_type} values supported */ default List getGrantTypes() { - return this.getClaimAsStringList(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED); + return getClaimAsStringList(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED); } /** @@ -103,7 +103,7 @@ public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { * @return the Subject Identifier types supported */ default List getSubjectTypes() { - return this.getClaimAsStringList(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); + return getClaimAsStringList(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); } /** @@ -112,7 +112,7 @@ public interface OidcProviderMetadataClaimAccessor extends ClaimAccessor { * @return the OAuth 2.0 {@code scope} values supported */ default List getScopes() { - return this.getClaimAsStringList(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED); + return getClaimAsStringList(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimNames.java index 03b6545..b906484 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/OidcProviderMetadataClaimNames.java @@ -16,7 +16,7 @@ package org.springframework.security.oauth2.core.oidc; /** - * The names of the "claims" defined by the OpenID Connect Discovery 1.0 that can be returned + * The names of the "claims" defined by OpenID Connect Discovery 1.0 that can be returned * in the OpenID Provider Configuration Response. * * @author Daniel Garnier-Moiroux @@ -26,17 +26,17 @@ package org.springframework.security.oauth2.core.oidc; public interface OidcProviderMetadataClaimNames { /** - * {@code issuer} - the URL the OpenID Provider asserts as its Issuer Identifier + * {@code issuer} - the {@code URL} the OpenID Provider asserts as its Issuer Identifier */ String ISSUER = "issuer"; /** - * {@code authorization_endpoint} - the URL of the OAuth 2.0 Authorization Endpoint + * {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization Endpoint */ String AUTHORIZATION_ENDPOINT = "authorization_endpoint"; /** - * {@code token_endpoint} - the URL of the OAuth 2.0 Token Endpoint + * {@code token_endpoint} - the {@code URL} of the OAuth 2.0 Token Endpoint */ String TOKEN_ENDPOINT = "token_endpoint"; @@ -46,7 +46,7 @@ public interface OidcProviderMetadataClaimNames { String TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED = "token_endpoint_auth_methods_supported"; /** - * {@code jwks_uri} - the URL of the JSON Web Key Set + * {@code jwks_uri} - the {@code URL} of the JSON Web Key Set */ String JWKS_URI = "jwks_uri"; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/HttpMessageConverters.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/HttpMessageConverters.java new file mode 100644 index 0000000..c96daaa --- /dev/null +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/HttpMessageConverters.java @@ -0,0 +1,67 @@ +/* + * Copyright 2002-2018 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. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.springframework.security.oauth2.core.oidc.http.converter; + +import org.springframework.http.converter.GenericHttpMessageConverter; +import org.springframework.http.converter.HttpMessageConverter; +import org.springframework.http.converter.json.GsonHttpMessageConverter; +import org.springframework.http.converter.json.JsonbHttpMessageConverter; +import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter; +import org.springframework.util.ClassUtils; + +/** + * TODO + * This class is a straight copy from Spring Security. + * It should be consolidated when merging this codebase into Spring Security. + * + * Utility methods for {@link HttpMessageConverter}'s. + * + * @author Joe Grandja + * @since 5.1 + */ +final class HttpMessageConverters { + + private static final boolean jackson2Present; + + private static final boolean gsonPresent; + + private static final boolean jsonbPresent; + + static { + ClassLoader classLoader = HttpMessageConverters.class.getClassLoader(); + jackson2Present = ClassUtils.isPresent("com.fasterxml.jackson.databind.ObjectMapper", classLoader) + && ClassUtils.isPresent("com.fasterxml.jackson.core.JsonGenerator", classLoader); + gsonPresent = ClassUtils.isPresent("com.google.gson.Gson", classLoader); + jsonbPresent = ClassUtils.isPresent("javax.json.bind.Jsonb", classLoader); + } + + private HttpMessageConverters() { + } + + static GenericHttpMessageConverter getJsonMessageConverter() { + if (jackson2Present) { + return new MappingJackson2HttpMessageConverter(); + } + if (gsonPresent) { + return new GsonHttpMessageConverter(); + } + if (jsonbPresent) { + return new JsonbHttpMessageConverter(); + } + return null; + } + +} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java similarity index 77% rename from oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverter.java rename to oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java index 20a7ea8..df068f2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.oauth2.core.http.converter; +package org.springframework.security.oauth2.core.oidc.http.converter; import org.springframework.core.ParameterizedTypeReference; import org.springframework.core.convert.TypeDescriptor; @@ -28,20 +28,17 @@ import org.springframework.http.converter.HttpMessageNotReadableException; import org.springframework.http.converter.HttpMessageNotWritableException; import org.springframework.security.oauth2.core.converter.ClaimConversionService; import org.springframework.security.oauth2.core.converter.ClaimTypeConverter; -import org.springframework.security.oauth2.core.converter.ObjectToSetStringConverter2; import org.springframework.security.oauth2.core.oidc.OidcProviderConfiguration; import org.springframework.security.oauth2.core.oidc.OidcProviderMetadataClaimNames; import org.springframework.util.Assert; -import java.io.IOException; import java.net.URL; +import java.util.Collection; import java.util.HashMap; import java.util.Map; -import java.util.Set; - /** - * A {@link HttpMessageConverter} for an {@link OidcProviderConfiguration OpenID Provider Configuration Metadata}. + * A {@link HttpMessageConverter} for an {@link OidcProviderConfiguration OpenID Provider Configuration Response}. * * @author Daniel Garnier-Moiroux * @since 0.1.0 @@ -50,9 +47,9 @@ import java.util.Set; */ public class OidcProviderConfigurationHttpMessageConverter extends AbstractHttpMessageConverter { + private static final ParameterizedTypeReference> STRING_OBJECT_MAP = - new ParameterizedTypeReference>() { - }; + new ParameterizedTypeReference>() {}; private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); @@ -73,7 +70,8 @@ public class OidcProviderConfigurationHttpMessageConverter protected OidcProviderConfiguration readInternal(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException { try { - Map providerConfigurationParameters = (Map) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, inputMessage); + Map providerConfigurationParameters = + (Map) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.providerConfigurationConverter.convert(providerConfigurationParameters); } catch (Exception ex) { throw new HttpMessageNotReadableException( @@ -82,7 +80,8 @@ public class OidcProviderConfigurationHttpMessageConverter } @Override - protected void writeInternal(OidcProviderConfiguration providerConfiguration, HttpOutputMessage outputMessage) throws IOException, HttpMessageNotWritableException { + protected void writeInternal(OidcProviderConfiguration providerConfiguration, HttpOutputMessage outputMessage) + throws HttpMessageNotWritableException { try { Map providerConfigurationResponseParameters = this.providerConfigurationParametersConverter.convert(providerConfiguration); @@ -98,6 +97,18 @@ public class OidcProviderConfigurationHttpMessageConverter } } + /** + * Sets the {@link Converter} used for converting the OpenID Provider Configuration parameters + * to an {@link OidcProviderConfiguration}. + * + * @param providerConfigurationConverter the {@link Converter} used for converting to an + * {@link OidcProviderConfiguration} + */ + public final void setProviderConfigurationConverter(Converter, OidcProviderConfiguration> providerConfigurationConverter) { + Assert.notNull(providerConfigurationConverter, "providerConfigurationConverter cannot be null"); + this.providerConfigurationConverter = providerConfigurationConverter; + } + /** * Sets the {@link Converter} used for converting the {@link OidcProviderConfiguration} to a * {@code Map} representation of the OpenID Provider Configuration. @@ -111,18 +122,6 @@ public class OidcProviderConfigurationHttpMessageConverter this.providerConfigurationParametersConverter = providerConfigurationParametersConverter; } - /** - * Sets the {@link Converter} used for converting the OpenID Provider Configuration parameters - * to an {@link OidcProviderConfiguration}. - * - * @param providerConfigurationConverter the {@link Converter} used for converting to an - * {@link OidcProviderConfiguration} - */ - public final void setProviderConfigurationConverter(Converter, OidcProviderConfiguration> providerConfigurationConverter) { - Assert.notNull(providerConfigurationConverter, "providerConfigurationConverter cannot be null"); - this.providerConfigurationConverter = providerConfigurationConverter; - } - private static final class OidcProviderConfigurationConverter implements Converter, OidcProviderConfiguration> { private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); @@ -130,22 +129,22 @@ public class OidcProviderConfigurationHttpMessageConverter private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class); private final ClaimTypeConverter claimTypeConverter; - OidcProviderConfigurationConverter() { - CLAIM_CONVERSION_SERVICE.addConverter(new ObjectToSetStringConverter2()); - Map> claimNameToConverter = new HashMap<>(); - Converter setStringConverter = getConverter(TypeDescriptor.collection(Set.class, STRING_TYPE_DESCRIPTOR)); + private OidcProviderConfigurationConverter() { + Converter collectionStringConverter = getConverter( + TypeDescriptor.collection(Collection.class, STRING_TYPE_DESCRIPTOR)); Converter urlConverter = getConverter(URL_TYPE_DESCRIPTOR); - claimNameToConverter.put(OidcProviderMetadataClaimNames.ISSUER, urlConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, urlConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.JWKS_URI, urlConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, setStringConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, setStringConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, setStringConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, setStringConverter); - claimNameToConverter.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, setStringConverter); - this.claimTypeConverter = new ClaimTypeConverter(claimNameToConverter); + Map> claimConverters = new HashMap<>(); + claimConverters.put(OidcProviderMetadataClaimNames.ISSUER, urlConverter); + claimConverters.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter); + claimConverters.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, urlConverter); + claimConverters.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter); + claimConverters.put(OidcProviderMetadataClaimNames.JWKS_URI, urlConverter); + claimConverters.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, collectionStringConverter); + claimConverters.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, collectionStringConverter); + claimConverters.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, collectionStringConverter); + claimConverters.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, collectionStringConverter); + this.claimTypeConverter = new ClaimTypeConverter(claimConverters); } @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettings.java index 7bf2e76..cd51d5e 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettings.java @@ -15,57 +15,61 @@ */ package org.springframework.security.oauth2.server.authorization.config; - -import org.springframework.util.Assert; - import java.util.HashMap; import java.util.Map; /** - * A facility for OpenID Connect Provider Configuration settings. + * A facility for provider configuration settings. * * @author Daniel Garnier-Moiroux * @since 0.1.0 * @see Settings - * @see OpenID Connect Discovery 1.0 */ public class ProviderSettings extends Settings { private static final String PROVIDER_SETTING_BASE = "setting.provider."; public static final String ISSUER = PROVIDER_SETTING_BASE.concat("issuer"); public static final String AUTHORIZATION_ENDPOINT = PROVIDER_SETTING_BASE.concat("authorization-endpoint"); public static final String TOKEN_ENDPOINT = PROVIDER_SETTING_BASE.concat("token-endpoint"); - public static final String JWK_SET_ENDPOINT = PROVIDER_SETTING_BASE.concat("jwk-set-endpoint"); + public static final String JWKS_ENDPOINT = PROVIDER_SETTING_BASE.concat("jwks-endpoint"); public static final String TOKEN_REVOCATION_ENDPOINT = PROVIDER_SETTING_BASE.concat("token-revocation-endpoint"); /** * Constructs a {@code ProviderSettings}. */ public ProviderSettings() { - super(defaultSettings()); + this(defaultSettings()); } /** - * Returns the URL for the OpenID Issuer. + * Constructs a {@code ProviderSettings} using the provided parameters. * - * @return the URL for the OpenID Issuer + * @param settings the initial settings + */ + public ProviderSettings(Map settings) { + super(settings); + } + + /** + * Returns the URL of the Provider's Issuer Identifier + * + * @return the URL of the Provider's Issuer Identifier */ public String issuer() { return setting(ISSUER); } /** - * Sets the URL the Provider uses as its Issuer Identity. + * Sets the URL the Provider uses as its Issuer Identifier. * - * @param issuer the URL the Provider uses as its Issuer Identity. + * @param issuer the URL the Provider uses as its Issuer Identifier. * @return the {@link ProviderSettings} for further configuration */ public ProviderSettings issuer(String issuer) { - Assert.notNull(issuer, "issuer cannot be null"); return setting(ISSUER, issuer); } /** - * Returns the provider's OAuth 2.0 Authorization endpoint. The default is {@code /oauth2/authorize}. + * Returns the Provider's OAuth 2.0 Authorization endpoint. The default is {@code /oauth2/authorize}. * * @return the Authorization endpoint */ @@ -74,18 +78,17 @@ public class ProviderSettings extends Settings { } /** - * Sets the provider's OAuth 2.0 Authorization endpoint. + * Sets the Provider's OAuth 2.0 Authorization endpoint. * * @param authorizationEndpoint the Authorization endpoint * @return the {@link ProviderSettings} for further configuration */ public ProviderSettings authorizationEndpoint(String authorizationEndpoint) { - Assert.hasText(authorizationEndpoint, "authorizationEndpoint cannot be empty"); return setting(AUTHORIZATION_ENDPOINT, authorizationEndpoint); } /** - * Returns the provider's OAuth 2.0 Token endpoint. The default is {@code /oauth2/token}. + * Returns the Provider's OAuth 2.0 Token endpoint. The default is {@code /oauth2/token}. * * @return the Token endpoint */ @@ -94,38 +97,36 @@ public class ProviderSettings extends Settings { } /** - * Sets the provider's OAuth 2.0 Token endpoint. + * Sets the Provider's OAuth 2.0 Token endpoint. * * @param tokenEndpoint the Token endpoint * @return the {@link ProviderSettings} for further configuration */ public ProviderSettings tokenEndpoint(String tokenEndpoint) { - Assert.hasText(tokenEndpoint, "tokenEndpoint cannot be empty"); return setting(TOKEN_ENDPOINT, tokenEndpoint); } /** - * Returns the provider's JWK Set endpoint. The default is {@code /oauth2/jwks}. + * Returns the Provider's JWK Set endpoint. The default is {@code /oauth2/jwks}. * * @return the JWK Set endpoint */ - public String jwkSetEndpoint() { - return setting(JWK_SET_ENDPOINT); + public String jwksEndpoint() { + return setting(JWKS_ENDPOINT); } /** - * Sets the provider's OAuth 2.0 JWK Set endpoint. + * Sets the Provider's JWK Set endpoint. * - * @param jwkSetEndpoint the JWK Set endpoint + * @param jwksEndpoint the JWK Set endpoint * @return the {@link ProviderSettings} for further configuration */ - public ProviderSettings jwkSetEndpoint(String jwkSetEndpoint) { - Assert.hasText(jwkSetEndpoint, "jwkSetEndpoint cannot be empty"); - return setting(JWK_SET_ENDPOINT, jwkSetEndpoint); + public ProviderSettings jwksEndpoint(String jwksEndpoint) { + return setting(JWKS_ENDPOINT, jwksEndpoint); } /** - * Returns the provider's Token Revocation endpoint. The default is {@code /oauth2/revoke}. + * Returns the Provider's OAuth 2.0 Token Revocation endpoint. The default is {@code /oauth2/revoke}. * * @return the Token Revocation endpoint */ @@ -134,13 +135,12 @@ public class ProviderSettings extends Settings { } /** - * Sets the provider's OAuth 2.0 Token Revocation endpoint. + * Sets the Provider's OAuth 2.0 Token Revocation endpoint. * * @param tokenRevocationEndpoint the Token Revocation endpoint * @return the {@link ProviderSettings} for further configuration */ public ProviderSettings tokenRevocationEndpoint(String tokenRevocationEndpoint) { - Assert.hasText(tokenRevocationEndpoint, "tokenRevocationEndpoint cannot be empty"); return setting(TOKEN_REVOCATION_ENDPOINT, tokenRevocationEndpoint); } @@ -148,7 +148,7 @@ public class ProviderSettings extends Settings { Map settings = new HashMap<>(); settings.put(AUTHORIZATION_ENDPOINT, "/oauth2/authorize"); settings.put(TOKEN_ENDPOINT, "/oauth2/token"); - settings.put(JWK_SET_ENDPOINT, "/oauth2/jwks"); + settings.put(JWKS_ENDPOINT, "/oauth2/jwks"); settings.put(TOKEN_REVOCATION_ENDPOINT, "/oauth2/revoke"); return settings; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilter.java index f9d6bd4..0a75bb6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilter.java @@ -20,9 +20,9 @@ import org.springframework.http.MediaType; import org.springframework.http.server.ServletServerHttpResponse; import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.endpoint.OAuth2AuthorizationResponseType; -import org.springframework.security.oauth2.core.http.converter.OidcProviderConfigurationHttpMessageConverter; import org.springframework.security.oauth2.core.oidc.OidcProviderConfiguration; import org.springframework.security.oauth2.core.oidc.OidcScopes; +import org.springframework.security.oauth2.core.oidc.http.converter.OidcProviderConfigurationHttpMessageConverter; import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; import org.springframework.security.web.util.matcher.AntPathRequestMatcher; import org.springframework.security.web.util.matcher.RequestMatcher; @@ -37,12 +37,13 @@ import javax.servlet.http.HttpServletResponse; import java.io.IOException; /** - * A {@code Filter} that processes OpenID Provider Configuration Request. + * A {@code Filter} that processes OpenID Provider Configuration Requests. * * @author Daniel Garnier-Moiroux * @since 0.1.0 + * @see OidcProviderConfiguration * @see ProviderSettings - * @see OpenID Connect Discovery 1.0 + * @see 4.1. OpenID Provider Configuration Request */ public class OidcProviderConfigurationEndpointFilter extends OncePerRequestFilter { /** @@ -50,22 +51,24 @@ public class OidcProviderConfigurationEndpointFilter extends OncePerRequestFilte */ public static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration"; - private final RequestMatcher requestMatcher; private final ProviderSettings providerSettings; - private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = new OidcProviderConfigurationHttpMessageConverter(); + private final RequestMatcher requestMatcher; + private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = + new OidcProviderConfigurationHttpMessageConverter(); public OidcProviderConfigurationEndpointFilter(ProviderSettings providerSettings) { Assert.notNull(providerSettings, "providerSettings cannot be null"); + this.providerSettings = providerSettings; this.requestMatcher = new AntPathRequestMatcher( DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name() ); - this.providerSettings = providerSettings; } @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { + if (!this.requestMatcher.matches(request)) { filterChain.doFilter(request, response); return; @@ -75,20 +78,23 @@ public class OidcProviderConfigurationEndpointFilter extends OncePerRequestFilte .issuer(this.providerSettings.issuer()) .authorizationEndpoint(asUrl(this.providerSettings.issuer(), this.providerSettings.authorizationEndpoint())) .tokenEndpoint(asUrl(this.providerSettings.issuer(), this.providerSettings.tokenEndpoint())) - .jwksUri(asUrl(this.providerSettings.issuer(), this.providerSettings.jwkSetEndpoint())) - .subjectType("public") + .tokenEndpointAuthenticationMethod("client_secret_basic") // TODO: Use ClientAuthenticationMethod.CLIENT_SECRET_BASIC in Spring Security 5.5.0 + .tokenEndpointAuthenticationMethod("client_secret_post") // TODO: Use ClientAuthenticationMethod.CLIENT_SECRET_POST in Spring Security 5.5.0 + .jwksUri(asUrl(this.providerSettings.issuer(), this.providerSettings.jwksEndpoint())) .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) - .scope(OidcScopes.OPENID) .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .tokenEndpointAuthenticationMethod("client_secret_basic") // TODO: move this ClientAuthenticationMethod + .grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue()) + .subjectType("public") + .scope(OidcScopes.OPENID) .build(); - ServletServerHttpResponse resp = new ServletServerHttpResponse(response); - this.providerConfigurationHttpMessageConverter.write(providerConfiguration, MediaType.APPLICATION_JSON, resp); + ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); + this.providerConfigurationHttpMessageConverter.write( + providerConfiguration, MediaType.APPLICATION_JSON, httpResponse); } - private String asUrl(String issuer, String endpoint) { + private static String asUrl(String issuer, String endpoint) { return UriComponentsBuilder.fromUriString(issuer).path(endpoint).build().toUriString(); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java index db800a7..12a8791 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/config/annotation/web/configurers/oauth2/server/authorization/OidcTests.java @@ -24,13 +24,11 @@ import org.springframework.security.config.annotation.web.configuration.EnableWe import org.springframework.security.config.annotation.web.configuration.OAuth2AuthorizationServerConfiguration; import org.springframework.security.config.test.SpringTestRule; import org.springframework.security.crypto.key.CryptoKeySource; -import org.springframework.security.crypto.key.StaticKeyGeneratingCryptoKeySource; import org.springframework.security.oauth2.server.authorization.client.RegisteredClientRepository; import org.springframework.security.oauth2.server.authorization.config.ProviderSettings; import org.springframework.security.oauth2.server.authorization.web.OidcProviderConfigurationEndpointFilter; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.MvcResult; -import org.springframework.test.web.servlet.request.MockMvcRequestBuilders; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -40,7 +38,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; /** - * Integration tests for the OpenID Connect. + * Integration tests for OpenID Connect 1.0. * * @author Daniel Garnier-Moiroux */ @@ -54,17 +52,17 @@ public class OidcTests { private MockMvc mvc; @Test - public void requestWhenIssuerSetAndOpenIDProviderConfigurationRequestThenReturnProviderConfigurationResponse() throws Exception { + public void requestWhenConfigurationRequestAndIssuerSetThenReturnConfigurationResponse() throws Exception { this.spring.register(AuthorizationServerConfigurationWithIssuer.class).autowire(); - this.mvc.perform(MockMvcRequestBuilders.get(OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)) + this.mvc.perform(get(OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)) .andExpect(status().is2xxSuccessful()) .andExpect(jsonPath("issuer").value(issuerUrl)) .andReturn(); } @Test - public void requestWhenIssuerNotSetAndOpenIDProviderConfigurationRequestThenRedirectsToLogin() throws Exception { + public void requestWhenConfigurationRequestAndIssuerNotSetThenRedirectToLogin() throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); MvcResult mvcResult = this.mvc.perform(get(OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)) @@ -74,16 +72,16 @@ public class OidcTests { } @Test - public void requestWhenIssuerNotValidUrlThenThrowException() { + public void loadContextWhenIssuerNotValidUrlThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithInvalidUrlIssuer.class).autowire() + () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire() ); } @Test - public void requestWhenIssuerNotValidUriThenThrowException() { + public void loadContextWhenIssuerNotValidUriThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithInvalidUriIssuer.class).autowire() + () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire() ); } @@ -98,20 +96,15 @@ public class OidcTests { @Bean CryptoKeySource keySource() { - return new StaticKeyGeneratingCryptoKeySource(); - } - - @Bean - ProviderSettings providerSettings() { - return new ProviderSettings(); + return mock(CryptoKeySource.class); } } @EnableWebSecurity @Import(OAuth2AuthorizationServerConfiguration.class) static class AuthorizationServerConfigurationWithIssuer extends AuthorizationServerConfiguration { + @Bean - @Override ProviderSettings providerSettings() { return new ProviderSettings().issuer(issuerUrl); } @@ -119,9 +112,9 @@ public class OidcTests { @EnableWebSecurity @Import(OAuth2AuthorizationServerConfiguration.class) - static class AuthorizationServerConfigurationWithInvalidUrlIssuer extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithInvalidIssuerUrl extends AuthorizationServerConfiguration { + @Bean - @Override ProviderSettings providerSettings() { return new ProviderSettings().issuer("urn:example"); } @@ -129,9 +122,9 @@ public class OidcTests { @EnableWebSecurity @Import(OAuth2AuthorizationServerConfiguration.class) - static class AuthorizationServerConfigurationWithInvalidUriIssuer extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithInvalidIssuerUri extends AuthorizationServerConfiguration { + @Bean - @Override ProviderSettings providerSettings() { return new ProviderSettings().issuer("https://not a valid uri"); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/converter/ObjectToSetStringConverter2Test.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/converter/ObjectToSetStringConverter2Test.java deleted file mode 100644 index ce10dc4..0000000 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/converter/ObjectToSetStringConverter2Test.java +++ /dev/null @@ -1,76 +0,0 @@ -/* - * Copyright 2020 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. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.core.converter; - -import org.junit.Test; - -import java.util.Arrays; -import java.util.Collections; -import java.util.HashSet; -import java.util.Set; - -import static org.assertj.core.api.Assertions.assertThat; - -/** - * TODO - * This class is temporary and will be removed after upgrading to Spring Security 5.5.0 GA. - * These tests will probably be folded into tests for {@link ClaimConversionService}. - * - * Tests for {@link ObjectToSetStringConverter2}. - * - * @author Daniel Garnier-Moiroux - */ -public class ObjectToSetStringConverter2Test { - @Test - @SuppressWarnings("unchecked") - public void convertFromNullThenReturnNull() { - ObjectToSetStringConverter2 converter = new ObjectToSetStringConverter2(); - Set result = (Set) converter.convert(null, null, null); - assertThat(result).isNull(); - } - - @Test - @SuppressWarnings("unchecked") - public void convertFromStringThenReturnSet() { - ObjectToSetStringConverter2 converter = new ObjectToSetStringConverter2(); - Set result = (Set) converter.convert("Hello", null, null); - assertThat(result).containsExactly("Hello"); - } - - @Test - @SuppressWarnings("unchecked") - public void convertFromSetThenReturnSet() { - ObjectToSetStringConverter2 converter = new ObjectToSetStringConverter2(); - Set result = (Set) converter.convert(new HashSet<>(Arrays.asList("Hello", "world")), null, null); - assertThat(result).containsExactlyInAnyOrder("Hello", "world"); - } - - @Test - @SuppressWarnings("unchecked") - public void convertFromCollectionThenReturnSet() { - ObjectToSetStringConverter2 converter = new ObjectToSetStringConverter2(); - Set result = (Set) converter.convert(Arrays.asList("Hello", "world"), null, null); - assertThat(result).containsExactlyInAnyOrder("Hello", "world"); - } - - @Test - @SuppressWarnings("unchecked") - public void convertFromEmptyCollectionThenReturnEmptySet() { - ObjectToSetStringConverter2 converter = new ObjectToSetStringConverter2(); - Set result = (Set) converter.convert(Collections.emptyList(), null, null); - assertThat(result).isEmpty(); - } -} diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfigurationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfigurationTests.java index caadaaa..0f8b667 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfigurationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/OidcProviderConfigurationTests.java @@ -17,11 +17,12 @@ package org.springframework.security.oauth2.core.oidc; import org.junit.Test; -import java.net.MalformedURLException; import java.net.URL; import java.util.Collections; import java.util.HashMap; import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -54,7 +55,7 @@ public class OidcProviderConfigurationTests { .grantType("authorization_code") .grantType("client_credentials") .subjectType("public") - .tokenEndpointAuthenticationMethod("basic") + .tokenEndpointAuthenticationMethod("client_secret_basic") .claim("a-claim", "a-value") .build(); @@ -66,8 +67,8 @@ public class OidcProviderConfigurationTests { assertThat(providerConfiguration.getResponseTypes()).containsExactly("code"); assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); - assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly("basic"); - assertThat(providerConfiguration.getClaimAsString("a-claim")).isEqualTo("a-value"); + assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly("client_secret_basic"); + assertThat(providerConfiguration.getClaim("a-claim")).isEqualTo("a-value"); } @Test @@ -94,15 +95,15 @@ public class OidcProviderConfigurationTests { } @Test - public void buildFromClaimsThenCreated() { - HashMap claims = new HashMap<>(); + public void buildWhenClaimsProvidedThenCreated() { + Map claims = new HashMap<>(); claims.put(OidcProviderMetadataClaimNames.ISSUER, "https://example.com/issuer1"); claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, "https://example.com/issuer1/oauth2/authorize"); claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, "https://example.com/issuer1/oauth2/token"); claims.put(OidcProviderMetadataClaimNames.JWKS_URI, "https://example.com/issuer1/oauth2/jwks"); - claims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singleton("openid")); - claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singleton("code")); - claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singleton("public")); + claims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList("openid")); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code")); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public")); claims.put("some-claim", "some-value"); OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build(); @@ -116,19 +117,19 @@ public class OidcProviderConfigurationTests { assertThat(providerConfiguration.getGrantTypes()).isNull(); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull(); - assertThat(providerConfiguration.getClaimAsString("some-claim")).isEqualTo("some-value"); + assertThat(providerConfiguration.getClaim("some-claim")).isEqualTo("some-value"); } @Test - public void buildFromClaimsWhenUsingUrlsThenCreated() { - HashMap claims = new HashMap<>(); + public void buildWhenClaimsProvidedWithUrlsThenCreated() { + Map claims = new HashMap<>(); claims.put(OidcProviderMetadataClaimNames.ISSUER, url("https://example.com/issuer1")); claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, url("https://example.com/issuer1/oauth2/authorize")); claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, url("https://example.com/issuer1/oauth2/token")); claims.put(OidcProviderMetadataClaimNames.JWKS_URI, url("https://example.com/issuer1/oauth2/jwks")); - claims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singleton("openid")); - claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singleton("code")); - claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singleton("public")); + claims.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList("openid")); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code")); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.singletonList("public")); claims.put("some-claim", "some-value"); OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build(); @@ -142,45 +143,46 @@ public class OidcProviderConfigurationTests { assertThat(providerConfiguration.getGrantTypes()).isNull(); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull(); - assertThat(providerConfiguration.getClaimAsString("some-claim")).isEqualTo("some-value"); + assertThat(providerConfiguration.getClaim("some-claim")).isEqualTo("some-value"); } @Test - public void withClaimsWhenNullThenThrowsException() { + public void withClaimsWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OidcProviderConfiguration.withClaims(null)) .isInstanceOf(IllegalArgumentException.class); } @Test - public void withClaimsWhenMissingRequiredClaimsThenThrowsException() { + public void withClaimsWhenMissingRequiredClaimsThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OidcProviderConfiguration.withClaims(Collections.emptyMap())) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("claims cannot be empty"); } @Test public void buildWhenCalledTwiceThenGeneratesTwoConfigurations() { - OidcProviderConfiguration first = minimalConfigurationBuilder + OidcProviderConfiguration first = this.minimalConfigurationBuilder .grantType("client_credentials") .build(); - OidcProviderConfiguration second = minimalConfigurationBuilder + OidcProviderConfiguration second = this.minimalConfigurationBuilder .claims((claims) -> { - LinkedHashSet newGrantTypes = new LinkedHashSet<>(); + Set newGrantTypes = new LinkedHashSet<>(); newGrantTypes.add("authorization_code"); - newGrantTypes.add("implicit"); + newGrantTypes.add("custom_grant"); claims.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes); } ) .build(); assertThat(first.getGrantTypes()).containsExactly("client_credentials"); - assertThat(second.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "implicit"); + assertThat(second.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "custom_grant"); } @Test - public void buildWhenMissingIssuerThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenMissingIssuerThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ISSUER)); assertThatThrownBy(builder::build) @@ -189,18 +191,18 @@ public class OidcProviderConfigurationTests { } @Test - public void buildWhenIssuerIsNotAnUrlThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenIssuerNotUrlThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.ISSUER, "not an url")); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("issuer must be a valid URL"); + .hasMessage("issuer must be a valid URL"); } @Test - public void buildWhenMissingAuthorizationEndpointThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenMissingAuthorizationEndpointThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT)); assertThatThrownBy(builder::build) @@ -209,8 +211,8 @@ public class OidcProviderConfigurationTests { } @Test - public void buildWhenAuthorizationEndpointIsNotAnUrlThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenAuthorizationEndpointNotUrlThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, "not an url")); assertThatThrownBy(builder::build) @@ -219,8 +221,8 @@ public class OidcProviderConfigurationTests { } @Test - public void buildWhenMissingTokenEndpointThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenMissingTokenEndpointThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT)); assertThatThrownBy(builder::build) @@ -229,8 +231,8 @@ public class OidcProviderConfigurationTests { } @Test - public void buildWhenTokenEndpointIsNotAnUrlThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenTokenEndpointNotUrlThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, "not an url")); assertThatThrownBy(builder::build) @@ -239,48 +241,100 @@ public class OidcProviderConfigurationTests { } @Test - public void buildWhenMissingJwksUriThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenMissingJwksUriThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.JWKS_URI)); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwkSetUri cannot be null"); + .hasMessage("jwksUri cannot be null"); } @Test - public void buildWheJwksUriIsNotAnUrlThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.JWKS_URI, "not an url")); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class) - .hasMessageStartingWith("jwkSetUri must be a valid URL"); + .hasMessageStartingWith("jwksUri must be a valid URL"); } @Test - public void buildWhenMissingResponseTypesThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenMissingResponseTypesThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("responseTypes cannot be empty"); + .hasMessage("responseTypes cannot be null"); } @Test - public void buildWhenMissingSubjectTypesThenThrowsException() { - OidcProviderConfiguration.Builder builder = minimalConfigurationBuilder + public void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder + .claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, "code"); + }); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("responseTypes must be of type List"); + } + + @Test + public void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder + .claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList()); + }); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("responseTypes cannot be empty"); + } + + @Test + public void buildWhenMissingSubjectTypesThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED)); assertThatThrownBy(builder::build) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("subjectTypes cannot be empty"); + .hasMessage("subjectTypes cannot be null"); + } + + @Test + public void buildWhenSubjectTypesNotListThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder + .claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, "public"); + }); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("subjectTypes must be of type List"); + } + + @Test + public void buildWhenSubjectTypesEmptyListThenThrowIllegalArgumentException() { + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder + .claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.emptyList()); + }); + + assertThatThrownBy(builder::build) + .isInstanceOf(IllegalArgumentException.class) + .hasMessageContaining("subjectTypes cannot be empty"); } @Test public void responseTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .responseType("should-be-removed") .responseTypes(responseTypes -> { responseTypes.clear(); @@ -293,7 +347,7 @@ public class OidcProviderConfigurationTests { @Test public void responseTypesWhenNotPresentAndAddingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .claims(claims -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)) .responseTypes(responseTypes -> responseTypes.add("some-response-type")) .build(); @@ -303,7 +357,7 @@ public class OidcProviderConfigurationTests { @Test public void subjectTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .subjectType("should-be-removed") .subjectTypes(subjectTypes -> { subjectTypes.clear(); @@ -316,7 +370,7 @@ public class OidcProviderConfigurationTests { @Test public void scopesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .scope("should-be-removed") .scopes(scopes -> { scopes.clear(); @@ -329,7 +383,7 @@ public class OidcProviderConfigurationTests { @Test public void grantTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .grantType("should-be-removed") .grantTypes(grantTypes -> { grantTypes.clear(); @@ -342,7 +396,7 @@ public class OidcProviderConfigurationTests { @Test public void tokenEndpointAuthenticationMethodsWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = minimalConfigurationBuilder + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder .tokenEndpointAuthenticationMethod("should-be-removed") .tokenEndpointAuthenticationMethods(authMethods -> { authMethods.clear(); @@ -355,24 +409,22 @@ public class OidcProviderConfigurationTests { @Test public void claimWhenNameIsNullThenThrowIllegalArgumentException() { - OidcProviderConfiguration.Builder builder = OidcProviderConfiguration.withClaims(); - assertThatThrownBy(() -> builder.claim(null, "value")) + assertThatThrownBy(() -> OidcProviderConfiguration.withClaims().claim(null, "value")) .isInstanceOf(IllegalArgumentException.class) .hasMessage("name cannot be empty"); } @Test public void claimWhenValueIsNullThenThrowIllegalArgumentException() { - OidcProviderConfiguration.Builder builder = OidcProviderConfiguration.withClaims(); - assertThatThrownBy(() -> builder.claim("claim-name", null)) + assertThatThrownBy(() -> OidcProviderConfiguration.withClaims().claim("claim-name", null)) .isInstanceOf(IllegalArgumentException.class) .hasMessage("value cannot be null"); } @Test - public void claimsWhenRemovingAClaimThenIsNotPresent() { + public void claimsWhenRemovingClaimThenNotPresent() { OidcProviderConfiguration configuration = - minimalConfigurationBuilder + this.minimalConfigurationBuilder .grantType("some-grant-type") .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED)) .build(); @@ -380,9 +432,9 @@ public class OidcProviderConfigurationTests { } @Test - public void claimsWhenAddingAClaimThenIsPresent() { + public void claimsWhenAddingClaimThenPresent() { OidcProviderConfiguration configuration = - minimalConfigurationBuilder + this.minimalConfigurationBuilder .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, "authorization_code")) .build(); assertThat(configuration.getGrantTypes()).containsExactly("authorization_code"); @@ -391,7 +443,7 @@ public class OidcProviderConfigurationTests { private static URL url(String urlString) { try { return new URL(urlString); - } catch (MalformedURLException e) { + } catch (Exception ex) { throw new IllegalArgumentException("urlString must be a valid URL and valid URI"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java similarity index 88% rename from oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java rename to oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java index df081ba..72c3675 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/core/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java @@ -13,7 +13,7 @@ * See the License for the specific language governing permissions and * limitations under the License. */ -package org.springframework.security.oauth2.core.http.converter; +package org.springframework.security.oauth2.core.oidc.http.converter; import org.junit.Test; import org.springframework.core.convert.converter.Converter; @@ -24,7 +24,6 @@ import org.springframework.mock.http.MockHttpOutputMessage; import org.springframework.mock.http.client.MockClientHttpResponse; import org.springframework.security.oauth2.core.oidc.OidcProviderConfiguration; -import java.net.MalformedURLException; import java.net.URL; import java.util.Arrays; import java.util.Map; @@ -48,17 +47,17 @@ public class OidcProviderConfigurationHttpMessageConverterTests { } @Test - public void setProviderConfigurationParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() { + public void setProviderConfigurationParametersConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setProviderConfigurationParametersConverter(null)); } @Test - public void setProviderConfigurationConverterWhenConverterIsNullThenThrowIllegalArgumentException() { + public void setProviderConfigurationConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setProviderConfigurationConverter(null)); } @Test - public void readInternalWhenSuccessfulProviderConfigurationOnlyRequiredParametersThenReadOidcProviderConfiguration() throws Exception { + public void readInternalWhenRequiredParametersThenSuccess() throws Exception { // @formatter:off String providerConfigurationResponse = "{\n" + " \"issuer\": \"https://example.com/issuer1\",\n" @@ -85,7 +84,7 @@ public class OidcProviderConfigurationHttpMessageConverterTests { } @Test - public void readInternalWhenSuccessfulProviderConfigurationThenReadOidcProviderConfiguration() throws Exception { + public void readInternalWhenValidParametersThenSuccess() throws Exception { // @formatter:off String providerConfigurationResponse = "{\n" + " \"issuer\": \"https://example.com/issuer1\",\n" @@ -96,7 +95,7 @@ public class OidcProviderConfigurationHttpMessageConverterTests { + " \"response_types_supported\": [\"code\"],\n" + " \"grant_types_supported\": [\"authorization_code\", \"client_credentials\"],\n" + " \"subject_types_supported\": [\"public\"],\n" - + " \"token_endpoint_auth_methods_supported\": [\"basic\"],\n" + + " \"token_endpoint_auth_methods_supported\": [\"client_secret_basic\"],\n" + " \"custom_claim\": \"value\",\n" + " \"custom_collection_claim\": [\"value1\", \"value2\"]\n" + "}\n"; @@ -113,8 +112,8 @@ public class OidcProviderConfigurationHttpMessageConverterTests { assertThat(providerConfiguration.getResponseTypes()).containsExactly("code"); assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); - assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly("basic"); - assertThat(providerConfiguration.getClaimAsString("custom_claim")).isEqualTo("value"); + assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly("client_secret_basic"); + assertThat(providerConfiguration.getClaim("custom_claim")).isEqualTo("value"); assertThat(providerConfiguration.getClaimAsStringList("custom_collection_claim")).containsExactlyInAnyOrder("value1", "value2"); } @@ -144,7 +143,7 @@ public class OidcProviderConfigurationHttpMessageConverterTests { } @Test - public void writeInternalWhenOidcProviderConfigurationThenWriteTokenResponse() throws Exception { + public void writeInternalWhenProviderConfigurationThenSuccess() { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims() .issuer("https://example.com/issuer1") @@ -156,7 +155,7 @@ public class OidcProviderConfigurationHttpMessageConverterTests { .grantType("authorization_code") .grantType("client_credentials") .subjectType("public") - .tokenEndpointAuthenticationMethod("basic") + .tokenEndpointAuthenticationMethod("client_secret_basic") .claim("custom_claim", "value") .claim("custom_collection_claim", Arrays.asList("value1", "value2")) .build(); @@ -173,14 +172,13 @@ public class OidcProviderConfigurationHttpMessageConverterTests { assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]"); assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\"]"); assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]"); - assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"basic\"]"); + assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); assertThat(providerConfigurationResponse).contains("\"custom_claim\":\"value\""); assertThat(providerConfigurationResponse).contains("\"custom_collection_claim\":[\"value1\",\"value2\"]"); } @Test - @SuppressWarnings("unchecked") - public void writeInternalWhenWriteFailsThenThrowsException() throws MalformedURLException { + public void writeInternalWhenWriteFailsThenThrowsException() { String errorMessage = "this is not a valid converter"; Converter> failingConverter = source -> { @@ -190,10 +188,10 @@ public class OidcProviderConfigurationHttpMessageConverterTests { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com") - .tokenEndpoint("https://example.com") - .jwksUri("https://example.com") + .issuer("https://example.com/issuer1") + .authorizationEndpoint("https://example.com/issuer1/oauth2/authorize") + .tokenEndpoint("https://example.com/issuer1/oauth2/token") + .jwksUri("https://example.com/issuer1/oauth2/jwks") .responseType("code") .subjectType("public") .build(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettingsTests.java index 040d298..cc33a77 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettingsTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/ProviderSettingsTests.java @@ -17,8 +17,6 @@ package org.springframework.security.oauth2.server.authorization.config; import org.junit.Test; -import java.net.MalformedURLException; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -28,41 +26,42 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Daniel Garnier-Moiroux */ public class ProviderSettingsTests { + @Test - public void constructorWhenDefaultThenDefaultsAreSetAndIssuerIsNotSet() { + public void constructorWhenDefaultThenDefaultsAreSet() { ProviderSettings providerSettings = new ProviderSettings(); assertThat(providerSettings.issuer()).isNull(); assertThat(providerSettings.authorizationEndpoint()).isEqualTo("/oauth2/authorize"); assertThat(providerSettings.tokenEndpoint()).isEqualTo("/oauth2/token"); - assertThat(providerSettings.jwkSetEndpoint()).isEqualTo("/oauth2/jwks"); + assertThat(providerSettings.jwksEndpoint()).isEqualTo("/oauth2/jwks"); assertThat(providerSettings.tokenRevocationEndpoint()).isEqualTo("/oauth2/revoke"); } @Test - public void settingsWhenProvidedThenSet() throws MalformedURLException { - String authorizationEndpoint = "/my-endpoints/authorize"; - String tokenEndpoint = "/my-endpoints/token"; - String jwksEndpoint = "/my-endpoints/jwks"; - String tokenRevocationEndpoint = "/my-endpoints/revoke"; - String issuer = "https://example.com/9000"; + public void settingsWhenProvidedThenSet() { + String authorizationEndpoint = "/oauth2/v1/authorize"; + String tokenEndpoint = "/oauth2/v1/token"; + String jwksEndpoint = "/oauth2/v1/jwks"; + String tokenRevocationEndpoint = "/oauth2/v1/revoke"; + String issuer = "https://example.com:9000"; ProviderSettings providerSettings = new ProviderSettings() .issuer(issuer) .authorizationEndpoint(authorizationEndpoint) .tokenEndpoint(tokenEndpoint) - .jwkSetEndpoint(jwksEndpoint) + .jwksEndpoint(jwksEndpoint) .tokenRevocationEndpoint(tokenRevocationEndpoint); assertThat(providerSettings.issuer()).isEqualTo(issuer); assertThat(providerSettings.authorizationEndpoint()).isEqualTo(authorizationEndpoint); assertThat(providerSettings.tokenEndpoint()).isEqualTo(tokenEndpoint); - assertThat(providerSettings.jwkSetEndpoint()).isEqualTo(jwksEndpoint); + assertThat(providerSettings.jwksEndpoint()).isEqualTo(jwksEndpoint); assertThat(providerSettings.tokenRevocationEndpoint()).isEqualTo(tokenRevocationEndpoint); } @Test - public void settingWhenCalledThenReturnTokenSettings() { + public void settingWhenCustomThenReturnAllSettings() { ProviderSettings providerSettings = new ProviderSettings() .setting("name1", "value1") .settings(settings -> settings.put("name2", "value2")); @@ -73,54 +72,42 @@ public class ProviderSettingsTests { } @Test - public void issuerWhenNullThenThrowsIllegalArgumentException() { + public void issuerWhenNullThenThrowIllegalArgumentException() { ProviderSettings settings = new ProviderSettings(); assertThatThrownBy(() -> settings.issuer(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("issuer cannot be null"); + .hasMessage("value cannot be null"); } @Test - public void authorizationEndpointWhenNullThenThrowsIllegalArgumentException() { + public void authorizationEndpointWhenNullThenThrowIllegalArgumentException() { ProviderSettings settings = new ProviderSettings(); assertThatThrownBy(() -> settings.authorizationEndpoint(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationEndpoint cannot be empty"); - assertThatThrownBy(() -> settings.authorizationEndpoint("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationEndpoint cannot be empty"); + .hasMessage("value cannot be null"); } @Test - public void tokenEndpointWhenNullThenThrowsIllegalArgumentException() { + public void tokenEndpointWhenNullThenThrowIllegalArgumentException() { ProviderSettings settings = new ProviderSettings(); assertThatThrownBy(() -> settings.tokenEndpoint(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenEndpoint cannot be empty"); - assertThatThrownBy(() -> settings.tokenEndpoint("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenEndpoint cannot be empty"); + .hasMessage("value cannot be null"); } @Test - public void tokenRevocationEndpointWhenNullThenThrowsIllegalArgumentException() { + public void tokenRevocationEndpointWhenNullThenThrowIllegalArgumentException() { ProviderSettings settings = new ProviderSettings(); assertThatThrownBy(() -> settings.tokenRevocationEndpoint(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenRevocationEndpoint cannot be empty"); - assertThatThrownBy(() -> settings.tokenRevocationEndpoint("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenRevocationEndpoint cannot be empty"); + .hasMessage("value cannot be null"); } @Test - public void jwkSetEndpointWhenNullThenThrowsIllegalArgumentException() { + public void jwksEndpointWhenNullThenThrowIllegalArgumentException() { ProviderSettings settings = new ProviderSettings(); - assertThatThrownBy(() -> settings.jwkSetEndpoint(null)) + assertThatThrownBy(() -> settings.jwksEndpoint(null)) .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwkSetEndpoint cannot be empty"); - assertThatThrownBy(() -> settings.jwkSetEndpoint("")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwkSetEndpoint cannot be empty"); + .hasMessage("value cannot be null"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilterTests.java index d6da8ef..012bd07 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OidcProviderConfigurationEndpointFilterTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Daniel Garnier-Moiroux */ public class OidcProviderConfigurationEndpointFilterTests { + @Test public void constructorWhenProviderSettingsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OidcProviderConfigurationEndpointFilter(null)) @@ -46,8 +47,10 @@ public class OidcProviderConfigurationEndpointFilterTests { } @Test - public void doFilterWhenRequestDoesNotMatchThenNotProcessed() throws Exception { - OidcProviderConfigurationEndpointFilter filter = new OidcProviderConfigurationEndpointFilter(new ProviderSettings()); + public void doFilterWhenNotConfigurationRequestThenNotProcessed() throws Exception { + OidcProviderConfigurationEndpointFilter filter = + new OidcProviderConfigurationEndpointFilter(new ProviderSettings()); + String requestUri = "/path"; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); request.setServletPath(requestUri); @@ -60,20 +63,38 @@ public class OidcProviderConfigurationEndpointFilterTests { } @Test - public void doFilterWhenSuccessThenConfigurationResponse() throws Exception { - String authorizationEndpoint = "/my-endpoints/authorize"; - String tokenEndpoint = "/my-endpoints/token"; - String jwksEndpoint = "/my-endpoints/jwks"; + public void doFilterWhenConfigurationRequestPostThenNotProcessed() throws Exception { + OidcProviderConfigurationEndpointFilter filter = + new OidcProviderConfigurationEndpointFilter(new ProviderSettings()); + + String requestUri = OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; + MockHttpServletRequest request = new MockHttpServletRequest("POST", requestUri); + request.setServletPath(requestUri); + MockHttpServletResponse response = new MockHttpServletResponse(); + FilterChain filterChain = mock(FilterChain.class); + + filter.doFilter(request, response, filterChain); + + verify(filterChain).doFilter(any(HttpServletRequest.class), any(HttpServletResponse.class)); + } + + @Test + public void doFilterWhenConfigurationRequestThenConfigurationResponse() throws Exception { + String authorizationEndpoint = "/oauth2/v1/authorize"; + String tokenEndpoint = "/oauth2/v1/token"; + String jwksEndpoint = "/oauth2/v1/jwks"; ProviderSettings providerSettings = new ProviderSettings() .issuer("https://example.com/issuer1") .authorizationEndpoint(authorizationEndpoint) .tokenEndpoint(tokenEndpoint) - .jwkSetEndpoint(jwksEndpoint); - OidcProviderConfigurationEndpointFilter filter = new OidcProviderConfigurationEndpointFilter(providerSettings); + .jwksEndpoint(jwksEndpoint); + OidcProviderConfigurationEndpointFilter filter = + new OidcProviderConfigurationEndpointFilter(providerSettings); - MockHttpServletRequest request = new MockHttpServletRequest("GET", org.springframework.security.oauth2.server.authorization.web.OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI); - request.setServletPath(org.springframework.security.oauth2.server.authorization.web.OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI); + String requestUri = OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; + MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); + request.setServletPath(requestUri); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -84,29 +105,31 @@ public class OidcProviderConfigurationEndpointFilterTests { assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE); String providerConfigurationResponse = response.getContentAsString(); assertThat(providerConfigurationResponse).contains("\"issuer\":\"https://example.com/issuer1\""); - assertThat(providerConfigurationResponse).contains("\"authorization_endpoint\":\"https://example.com/issuer1/my-endpoints/authorize\""); - assertThat(providerConfigurationResponse).contains("\"token_endpoint\":\"https://example.com/issuer1/my-endpoints/token\""); - assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/issuer1/my-endpoints/jwks\""); + assertThat(providerConfigurationResponse).contains("\"authorization_endpoint\":\"https://example.com/issuer1/oauth2/v1/authorize\""); + assertThat(providerConfigurationResponse).contains("\"token_endpoint\":\"https://example.com/issuer1/oauth2/v1/token\""); + assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/issuer1/oauth2/v1/jwks\""); assertThat(providerConfigurationResponse).contains("\"scopes_supported\":[\"openid\"]"); assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]"); - assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\"]"); + assertThat(providerConfigurationResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\"]"); assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]"); - assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); + assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\"]"); } - @Test - public void doFilterWhenProviderSettingsWithInvalidIssuerThenThrowIllegalArgumentException() { + public void doFilterWhenProviderSettingsWithInvalidIssuerThenThrowIllegalArgumentException() { ProviderSettings providerSettings = new ProviderSettings() .issuer("https://this is an invalid URL"); - OidcProviderConfigurationEndpointFilter filter = new OidcProviderConfigurationEndpointFilter(providerSettings); + OidcProviderConfigurationEndpointFilter filter = + new OidcProviderConfigurationEndpointFilter(providerSettings); - MockHttpServletRequest request = new MockHttpServletRequest("GET", org.springframework.security.oauth2.server.authorization.web.OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI); - request.setServletPath(org.springframework.security.oauth2.server.authorization.web.OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI); + String requestUri = OidcProviderConfigurationEndpointFilter.DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; + MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); + request.setServletPath(requestUri); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); assertThatThrownBy(() -> filter.doFilter(request, response, filterChain)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("issuer must be a valid URL"); } }