diff --git a/buildSrc/build.gradle b/buildSrc/build.gradle index e97acdfb..2cdda564 100644 --- a/buildSrc/build.gradle +++ b/buildSrc/build.gradle @@ -15,7 +15,7 @@ repositories { dependencies { implementation "com.github.ben-manes:gradle-versions-plugin:0.38.0" implementation "io.github.gradle-nexus:publish-plugin:1.1.0" - implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.31" + implementation "io.spring.javaformat:spring-javaformat-gradle-plugin:0.0.41" implementation "io.spring.nohttp:nohttp-gradle:0.0.10" implementation "org.asciidoctor:asciidoctor-gradle-jvm:3.3.2" implementation "org.asciidoctor:asciidoctor-gradle-jvm-pdf:3.3.2" diff --git a/buildSrc/src/main/java/org/springframework/gradle/checkstyle/SpringJavaCheckstylePlugin.java b/buildSrc/src/main/java/org/springframework/gradle/checkstyle/SpringJavaCheckstylePlugin.java index 6a9bd19d..a5ea0b7e 100644 --- a/buildSrc/src/main/java/org/springframework/gradle/checkstyle/SpringJavaCheckstylePlugin.java +++ b/buildSrc/src/main/java/org/springframework/gradle/checkstyle/SpringJavaCheckstylePlugin.java @@ -37,7 +37,7 @@ import org.gradle.api.plugins.quality.CheckstylePlugin; public class SpringJavaCheckstylePlugin implements Plugin { private static final String CHECKSTYLE_DIR = "etc/checkstyle"; private static final String SPRING_JAVAFORMAT_VERSION_PROPERTY = "springJavaformatVersion"; - private static final String DEFAULT_SPRING_JAVAFORMAT_VERSION = "0.0.31"; + private static final String DEFAULT_SPRING_JAVAFORMAT_VERSION = "0.0.41"; private static final String NOHTTP_CHECKSTYLE_VERSION_PROPERTY = "nohttpCheckstyleVersion"; private static final String DEFAULT_NOHTTP_CHECKSTYLE_VERSION = "0.0.10"; private static final String CHECKSTYLE_TOOL_VERSION_PROPERTY = "checkstyleToolVersion"; diff --git a/gradle.properties b/gradle.properties index 6c90b295..9dd665f4 100644 --- a/gradle.properties +++ b/gradle.properties @@ -4,8 +4,7 @@ org.gradle.parallel=true org.gradle.caching=true springFrameworkVersion=6.1.6 springSecurityVersion=6.2.4 -springJavaformatVersion=0.0.38 -springJavaformatExcludePackages=org/springframework/security/config org/springframework/security/oauth2 +springJavaformatVersion=0.0.41 checkstyleToolVersion=8.34 nohttpCheckstyleVersion=0.0.11 jacocoToolVersion=0.8.7 diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java index 1c382949..09168646 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/AbstractOAuth2AuthorizationServerMetadata.java @@ -29,19 +29,27 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * A base representation of OAuth 2.0 Authorization Server metadata, - * returned by an endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0. - * The metadata endpoint returns a set of claims an Authorization Server describes about its configuration. + * A base representation of OAuth 2.0 Authorization Server metadata, returned by an + * endpoint defined in OAuth 2.0 Authorization Server Metadata and OpenID Connect + * Discovery 1.0. The metadata endpoint returns a set of claims an Authorization Server + * describes about its configuration. * * @author Daniel Garnier-Moiroux * @see OAuth2AuthorizationServerMetadataClaimAccessor * @since 0.1.1 - * @see 3.2. Authorization Server Metadata Response - * @see 4.2. OpenID Provider Configuration Response - * @see 4. Device Authorization Grant Metadata + * @see 3.2. + * Authorization Server Metadata Response + * @see 4.2. + * OpenID Provider Configuration Response + * @see 4. + * Device Authorization Grant Metadata */ -public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable { +public abstract class AbstractOAuth2AuthorizationServerMetadata + implements OAuth2AuthorizationServerMetadataClaimAccessor, Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Map claims; protected AbstractOAuth2AuthorizationServerMetadata(Map claims) { @@ -51,7 +59,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth /** * Returns the metadata as claims. - * * @return a {@code Map} of the metadata as claims */ @Override @@ -63,6 +70,7 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth * A builder for subclasses of {@link AbstractOAuth2AuthorizationServerMetadata}. */ protected static abstract class AbstractBuilder> { + private final Map claims = new LinkedHashMap<>(); protected AbstractBuilder() { @@ -74,12 +82,14 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth @SuppressWarnings("unchecked") protected final B getThis() { - return (B) this; // avoid unchecked casts in subclasses by using "getThis()" instead of "(B) this" + // avoid unchecked casts in subclasses by using "getThis()" instead of "(B) + // this" + return (B) this; } /** - * Use this {@code issuer} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. - * + * Use this {@code issuer} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. * @param issuer the {@code URL} of the Authorization Server's Issuer Identifier * @return the {@link AbstractBuilder} for further configuration */ @@ -88,9 +98,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Use this {@code authorization_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. - * - * @param authorizationEndpoint the {@code URL} of the OAuth 2.0 Authorization Endpoint + * Use this {@code authorization_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. + * @param authorizationEndpoint the {@code URL} of the OAuth 2.0 Authorization + * Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B authorizationEndpoint(String authorizationEndpoint) { @@ -98,19 +109,21 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Use this {@code device_authorization_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device Authorization Endpoint + * Use this {@code device_authorization_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param deviceAuthorizationEndpoint the {@code URL} of the OAuth 2.0 Device + * Authorization Endpoint * @return the {@link AbstractBuilder} for further configuration * @since 1.1 */ public B deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) { - return claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, deviceAuthorizationEndpoint); + return claim(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, + deviceAuthorizationEndpoint); } /** - * Use this {@code token_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. - * + * Use this {@code token_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. * @param tokenEndpoint the {@code URL} of the OAuth 2.0 Token Endpoint * @return the {@link AbstractBuilder} for further configuration */ @@ -119,31 +132,35 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Add this client authentication method to the collection of {@code token_endpoint_auth_methods_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Endpoint + * Add this client authentication method to the collection of + * {@code token_endpoint_auth_methods_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param authenticationMethod the client authentication method supported by the + * OAuth 2.0 Token Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenEndpointAuthenticationMethod(String authenticationMethod) { - addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod); + addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethod); return getThis(); } /** - * A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove. - * - * @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Endpoint + * A {@code Consumer} of the client authentication method(s) allowing the ability + * to add, replace, or remove. + * @param authenticationMethodsConsumer a {@code Consumer} of the client + * authentication method(s) supported by the OAuth 2.0 Token Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenEndpointAuthenticationMethods(Consumer> authenticationMethodsConsumer) { - acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer); + acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethodsConsumer); return getThis(); } /** - * Use this {@code jwks_uri} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * + * Use this {@code jwks_uri} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. * @param jwkSetUrl the {@code URL} of the JSON Web Key Set * @return the {@link AbstractBuilder} for further configuration */ @@ -153,8 +170,8 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth /** * Add this OAuth 2.0 {@code scope} to the collection of {@code scopes_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, RECOMMENDED. - * + * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, + * RECOMMENDED. * @param scope the OAuth 2.0 {@code scope} value supported * @return the {@link AbstractBuilder} for further configuration */ @@ -164,9 +181,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * A {@code Consumer} of the OAuth 2.0 {@code scope} values supported allowing the ability to add, replace, or remove. - * - * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values supported + * A {@code Consumer} of the OAuth 2.0 {@code scope} values supported allowing the + * ability to add, replace, or remove. + * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values + * supported * @return the {@link AbstractBuilder} for further configuration */ public B scopes(Consumer> scopesConsumer) { @@ -175,9 +193,9 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Add this OAuth 2.0 {@code response_type} to the collection of {@code response_types_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. - * + * Add this OAuth 2.0 {@code response_type} to the collection of + * {@code response_types_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, REQUIRED. * @param responseType the OAuth 2.0 {@code response_type} value supported * @return the {@link AbstractBuilder} for further configuration */ @@ -187,20 +205,22 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * A {@code Consumer} of the OAuth 2.0 {@code response_type} values supported allowing the ability to add, replace, or remove. - * - * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code response_type} values supported + * A {@code Consumer} of the OAuth 2.0 {@code response_type} values supported + * allowing the ability to add, replace, or remove. + * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 + * {@code response_type} values supported * @return the {@link AbstractBuilder} for further configuration */ public B responseTypes(Consumer> responseTypesConsumer) { - acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, responseTypesConsumer); + acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, + responseTypesConsumer); return getThis(); } /** - * Add this OAuth 2.0 {@code grant_type} to the collection of {@code grant_types_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * + * Add this OAuth 2.0 {@code grant_type} to the collection of + * {@code grant_types_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. * @param grantType the OAuth 2.0 {@code grant_type} value supported * @return the {@link AbstractBuilder} for further configuration */ @@ -210,9 +230,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported allowing the ability to add, replace, or remove. - * - * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported + * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values supported + * allowing the ability to add, replace, or remove. + * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 + * {@code grant_type} values supported * @return the {@link AbstractBuilder} for further configuration */ public B grantTypes(Consumer> grantTypesConsumer) { @@ -221,9 +242,10 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Use this {@code revocation_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param tokenRevocationEndpoint the {@code URL} of the OAuth 2.0 Token Revocation Endpoint + * Use this {@code revocation_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param tokenRevocationEndpoint the {@code URL} of the OAuth 2.0 Token + * Revocation Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenRevocationEndpoint(String tokenRevocationEndpoint) { @@ -231,65 +253,78 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Add this client authentication method to the collection of {@code revocation_endpoint_auth_methods_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Revocation Endpoint + * Add this client authentication method to the collection of + * {@code revocation_endpoint_auth_methods_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param authenticationMethod the client authentication method supported by the + * OAuth 2.0 Token Revocation Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenRevocationEndpointAuthenticationMethod(String authenticationMethod) { - addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod); + addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethod); return getThis(); } /** - * A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove. - * - * @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Revocation Endpoint + * A {@code Consumer} of the client authentication method(s) allowing the ability + * to add, replace, or remove. + * @param authenticationMethodsConsumer a {@code Consumer} of the client + * authentication method(s) supported by the OAuth 2.0 Token Revocation Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenRevocationEndpointAuthenticationMethods(Consumer> authenticationMethodsConsumer) { - acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer); + acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethodsConsumer); return getThis(); } /** - * Use this {@code introspection_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param tokenIntrospectionEndpoint the {@code URL} of the OAuth 2.0 Token Introspection Endpoint + * Use this {@code introspection_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param tokenIntrospectionEndpoint the {@code URL} of the OAuth 2.0 Token + * Introspection Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenIntrospectionEndpoint(String tokenIntrospectionEndpoint) { - return claim(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, tokenIntrospectionEndpoint); + return claim(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, + tokenIntrospectionEndpoint); } /** - * Add this client authentication method to the collection of {@code introspection_endpoint_auth_methods_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param authenticationMethod the client authentication method supported by the OAuth 2.0 Token Introspection Endpoint + * Add this client authentication method to the collection of + * {@code introspection_endpoint_auth_methods_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param authenticationMethod the client authentication method supported by the + * OAuth 2.0 Token Introspection Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenIntrospectionEndpointAuthenticationMethod(String authenticationMethod) { - addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethod); + addClaimToClaimList( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethod); return getThis(); } /** - * A {@code Consumer} of the client authentication method(s) allowing the ability to add, replace, or remove. - * - * @param authenticationMethodsConsumer a {@code Consumer} of the client authentication method(s) supported by the OAuth 2.0 Token Introspection Endpoint + * A {@code Consumer} of the client authentication method(s) allowing the ability + * to add, replace, or remove. + * @param authenticationMethodsConsumer a {@code Consumer} of the client + * authentication method(s) supported by the OAuth 2.0 Token Introspection + * Endpoint * @return the {@link AbstractBuilder} for further configuration */ public B tokenIntrospectionEndpointAuthenticationMethods(Consumer> authenticationMethodsConsumer) { - acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, authenticationMethodsConsumer); + acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, + authenticationMethodsConsumer); return getThis(); } /** - * Use this {@code registration_endpoint} in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * - * @param clientRegistrationEndpoint the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint + * Use this {@code registration_endpoint} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. + * @param clientRegistrationEndpoint the {@code URL} of the OAuth 2.0 Dynamic + * Client Registration Endpoint * @return the {@link AbstractBuilder} for further configuration * @since 0.4.0 */ @@ -298,31 +333,35 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth } /** - * Add this Proof Key for Code Exchange (PKCE) {@code code_challenge_method} to the collection of {@code code_challenge_methods_supported} - * in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. - * + * Add this Proof Key for Code Exchange (PKCE) {@code code_challenge_method} to + * the collection of {@code code_challenge_methods_supported} in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}, OPTIONAL. * @param codeChallengeMethod the {@code code_challenge_method} value supported * @return the {@link AbstractBuilder} for further configuration */ public B codeChallengeMethod(String codeChallengeMethod) { - addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, codeChallengeMethod); + addClaimToClaimList(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, + codeChallengeMethod); return getThis(); } /** - * A {@code Consumer} of the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported allowing the ability to add, replace, or remove. - * - * @param codeChallengeMethodsConsumer a {@code Consumer} of the {@code code_challenge_method} values supported + * A {@code Consumer} of the Proof Key for Code Exchange (PKCE) + * {@code code_challenge_method} values supported allowing the ability to add, + * replace, or remove. + * @param codeChallengeMethodsConsumer a {@code Consumer} of the + * {@code code_challenge_method} values supported * @return the {@link AbstractBuilder} for further configuration */ public B codeChallengeMethods(Consumer> codeChallengeMethodsConsumer) { - acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, codeChallengeMethodsConsumer); + acceptClaimValues(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, + codeChallengeMethodsConsumer); return getThis(); } /** - * Use this claim in the resulting {@link AbstractOAuth2AuthorizationServerMetadata}. - * + * Use this claim in the resulting + * {@link AbstractOAuth2AuthorizationServerMetadata}. * @param name the claim name * @param value the claim value * @return the {@link AbstractBuilder} for further configuration @@ -337,7 +376,6 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth /** * Provides access to every {@link #claim(String, Object)} declared so far with * the possibility to add, replace, or remove. - * * @param claimsConsumer a {@code Consumer} of the claims * @return the {@link AbstractBuilder} for further configurations */ @@ -348,59 +386,105 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth /** * Creates the {@link AbstractOAuth2AuthorizationServerMetadata}. - * * @return the {@link AbstractOAuth2AuthorizationServerMetadata} */ public abstract T build(); protected void validate() { - Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), "issuer cannot be null"); - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), "issuer must be a valid URL"); - Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint cannot be null"); - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), "authorizationEndpoint must be a valid URL"); + Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), + "issuer cannot be null"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.ISSUER), + "issuer must be a valid URL"); + Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), + "authorizationEndpoint cannot be null"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT), + "authorizationEndpoint must be a valid URL"); if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT) != null) { - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT), "deviceAuthorizationEndpoint must be a valid URL"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT), + "deviceAuthorizationEndpoint must be a valid URL"); } - Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint cannot be null"); - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), "tokenEndpoint must be a valid URL"); - if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenEndpointAuthenticationMethods must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenEndpointAuthenticationMethods cannot be empty"); + Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), + "tokenEndpoint cannot be null"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT), + "tokenEndpoint must be a valid URL"); + if (getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { + Assert.isInstanceOf(List.class, + getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenEndpointAuthenticationMethods must be of type List"); + Assert.notEmpty( + (List) getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenEndpointAuthenticationMethods cannot be empty"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI) != null) { - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI), "jwksUri must be a valid URL"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI), + "jwksUri must be a valid URL"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), "scopes cannot be empty"); + Assert.isInstanceOf(List.class, + getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), + "scopes must be of type List"); + Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED), + "scopes cannot be empty"); } - Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be null"); - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), "responseTypes cannot be empty"); + Assert.notNull(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), + "responseTypes cannot be null"); + Assert.isInstanceOf(List.class, + getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), + "responseTypes must be of type List"); + Assert.notEmpty( + (List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED), + "responseTypes cannot be empty"); if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), "grantTypes must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), "grantTypes cannot be empty"); + Assert.isInstanceOf(List.class, + getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), + "grantTypes must be of type List"); + Assert.notEmpty( + (List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED), + "grantTypes cannot be empty"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT) != null) { - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT), "tokenRevocationEndpoint must be a valid URL"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT), + "tokenRevocationEndpoint must be a valid URL"); } - if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenRevocationEndpointAuthenticationMethods must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenRevocationEndpointAuthenticationMethods cannot be empty"); + if (getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { + Assert.isInstanceOf(List.class, + getClaims().get( + OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenRevocationEndpointAuthenticationMethods must be of type List"); + Assert.notEmpty( + (List) getClaims().get( + OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenRevocationEndpointAuthenticationMethods cannot be empty"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT) != null) { - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT), "tokenIntrospectionEndpoint must be a valid URL"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT), + "tokenIntrospectionEndpoint must be a valid URL"); } - if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenIntrospectionEndpointAuthenticationMethods must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), "tokenIntrospectionEndpointAuthenticationMethods cannot be empty"); + if (getClaims().get( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED) != null) { + Assert.isInstanceOf(List.class, getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenIntrospectionEndpointAuthenticationMethods must be of type List"); + Assert.notEmpty((List) getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED), + "tokenIntrospectionEndpointAuthenticationMethods cannot be empty"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT) != null) { - validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT), "clientRegistrationEndpoint must be a valid URL"); + validateURL(getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.REGISTRATION_ENDPOINT), + "clientRegistrationEndpoint must be a valid URL"); } if (getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED) != null) { - Assert.isInstanceOf(List.class, getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), "codeChallengeMethods must be of type List"); - Assert.notEmpty((List) getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), "codeChallengeMethods cannot be empty"); + Assert.isInstanceOf(List.class, + getClaims().get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), + "codeChallengeMethods must be of type List"); + Assert.notEmpty( + (List) getClaims() + .get(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED), + "codeChallengeMethods cannot be empty"); } } @@ -428,10 +512,12 @@ public abstract class AbstractOAuth2AuthorizationServerMetadata implements OAuth try { new URI(url.toString()).toURL(); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(errorMessage, ex); } } } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentService.java index 42bfc034..a2b351b7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentService.java @@ -26,7 +26,8 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * An {@link OAuth2AuthorizationConsentService} that stores {@link OAuth2AuthorizationConsent}'s in-memory. + * An {@link OAuth2AuthorizationConsentService} that stores + * {@link OAuth2AuthorizationConsent}'s in-memory. * *

* NOTE: This implementation should ONLY be used during development/testing. @@ -36,6 +37,7 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationConsentService */ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2AuthorizationConsentService { + private final Map authorizationConsents = new ConcurrentHashMap<>(); /** @@ -46,8 +48,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au } /** - * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided parameters. - * + * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided + * parameters. * @param authorizationConsents the authorization consent(s) */ public InMemoryOAuth2AuthorizationConsentService(OAuth2AuthorizationConsent... authorizationConsents) { @@ -55,8 +57,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au } /** - * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided parameters. - * + * Constructs an {@code InMemoryOAuth2AuthorizationConsentService} using the provided + * parameters. * @param authorizationConsents the authorization consent(s) */ public InMemoryOAuth2AuthorizationConsentService(List authorizationConsents) { @@ -66,8 +68,8 @@ public final class InMemoryOAuth2AuthorizationConsentService implements OAuth2Au int id = getId(authorizationConsent); Assert.isTrue(!this.authorizationConsents.containsKey(id), "The authorizationConsent must be unique. Found duplicate, with registered client id: [" - + authorizationConsent.getRegisteredClientId() - + "] and principal name: [" + authorizationConsent.getPrincipalName() + "]"); + + authorizationConsent.getRegisteredClientId() + "] and principal name: [" + + authorizationConsent.getPrincipalName() + "]"); this.authorizationConsents.put(id, authorizationConsent); }); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java index 701042ad..e69dad95 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationService.java @@ -33,7 +33,8 @@ import org.springframework.security.oauth2.core.oidc.endpoint.OidcParameterNames import org.springframework.util.Assert; /** - * An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s in-memory. + * An {@link OAuth2AuthorizationService} that stores {@link OAuth2Authorization}'s + * in-memory. * *

* NOTE: This implementation should ONLY be used during development/testing. @@ -44,15 +45,17 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationService */ public final class InMemoryOAuth2AuthorizationService implements OAuth2AuthorizationService { + private int maxInitializedAuthorizations = 100; /* - * Stores "initialized" (uncompleted) authorizations, where an access token has not yet been granted. - * This state occurs with the authorization_code grant flow during the user consent step OR - * when the code is returned in the authorization response but the access token request is not yet initiated. + * Stores "initialized" (uncompleted) authorizations, where an access token has not + * yet been granted. This state occurs with the authorization_code grant flow during + * the user consent step OR when the code is returned in the authorization response + * but the access token request is not yet initiated. */ - private Map initializedAuthorizations = - Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations)); + private Map initializedAuthorizations = Collections + .synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations)); /* * Stores "completed" authorizations, where an access token has been granted. @@ -64,7 +67,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza */ InMemoryOAuth2AuthorizationService(int maxInitializedAuthorizations) { this.maxInitializedAuthorizations = maxInitializedAuthorizations; - this.initializedAuthorizations = Collections.synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations)); + this.initializedAuthorizations = Collections + .synchronizedMap(new MaxSizeHashMap<>(this.maxInitializedAuthorizations)); } /** @@ -75,8 +79,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza } /** - * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters. - * + * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided + * parameters. * @param authorizations the authorization(s) */ public InMemoryOAuth2AuthorizationService(OAuth2Authorization... authorizations) { @@ -84,8 +88,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza } /** - * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided parameters. - * + * Constructs an {@code InMemoryOAuth2AuthorizationService} using the provided + * parameters. * @param authorizations the authorization(s) */ public InMemoryOAuth2AuthorizationService(List authorizations) { @@ -103,7 +107,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza Assert.notNull(authorization, "authorization cannot be null"); if (isComplete(authorization)) { this.authorizations.put(authorization.getId(), authorization); - } else { + } + else { this.initializedAuthorizations.put(authorization.getId(), authorization); } } @@ -113,7 +118,8 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza Assert.notNull(authorization, "authorization cannot be null"); if (isComplete(authorization)) { this.authorizations.remove(authorization.getId(), authorization); - } else { + } + else { this.initializedAuthorizations.remove(authorization.getId(), authorization); } } @@ -123,9 +129,7 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza public OAuth2Authorization findById(String id) { Assert.hasText(id, "id cannot be empty"); OAuth2Authorization authorization = this.authorizations.get(id); - return authorization != null ? - authorization : - this.initializedAuthorizations.get(id); + return authorization != null ? authorization : this.initializedAuthorizations.get(id); } @Nullable @@ -149,7 +153,9 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza return authorization.getAccessToken() != null; } - private static boolean hasToken(OAuth2Authorization authorization, String token, @Nullable OAuth2TokenType tokenType) { + private static boolean hasToken(OAuth2Authorization authorization, String token, + @Nullable OAuth2TokenType tokenType) { + // @formatter:off if (tokenType == null) { return matchesState(authorization, token) || matchesAuthorizationCode(authorization, token) || @@ -173,6 +179,7 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza } else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) { return matchesUserCode(authorization, token); } + // @formatter:on return false; } @@ -181,42 +188,38 @@ public final class InMemoryOAuth2AuthorizationService implements OAuth2Authoriza } private static boolean matchesAuthorizationCode(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token authorizationCode = - authorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); return authorizationCode != null && authorizationCode.getToken().getTokenValue().equals(token); } private static boolean matchesAccessToken(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token accessToken = - authorization.getToken(OAuth2AccessToken.class); + OAuth2Authorization.Token accessToken = authorization.getToken(OAuth2AccessToken.class); return accessToken != null && accessToken.getToken().getTokenValue().equals(token); } private static boolean matchesRefreshToken(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token refreshToken = - authorization.getToken(OAuth2RefreshToken.class); + OAuth2Authorization.Token refreshToken = authorization.getToken(OAuth2RefreshToken.class); return refreshToken != null && refreshToken.getToken().getTokenValue().equals(token); } private static boolean matchesIdToken(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token idToken = - authorization.getToken(OidcIdToken.class); + OAuth2Authorization.Token idToken = authorization.getToken(OidcIdToken.class); return idToken != null && idToken.getToken().getTokenValue().equals(token); } private static boolean matchesDeviceCode(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token deviceCode = - authorization.getToken(OAuth2DeviceCode.class); + OAuth2Authorization.Token deviceCode = authorization.getToken(OAuth2DeviceCode.class); return deviceCode != null && deviceCode.getToken().getTokenValue().equals(token); } private static boolean matchesUserCode(OAuth2Authorization authorization, String token) { - OAuth2Authorization.Token userCode = - authorization.getToken(OAuth2UserCode.class); + OAuth2Authorization.Token userCode = authorization.getToken(OAuth2UserCode.class); return userCode != null && userCode.getToken().getTokenValue().equals(token); } private static final class MaxSizeHashMap extends LinkedHashMap { + private final int maxSize; private MaxSizeHashMap(int maxSize) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentService.java index 08d0ec61..cdbf9ad1 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentService.java @@ -47,16 +47,18 @@ import org.springframework.util.StringUtils; * {@link JdbcOperations} for {@link OAuth2AuthorizationConsent} persistence. * *

- * IMPORTANT: This {@code OAuth2AuthorizationConsentService} depends on the table definition - * described in - * "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql" and - * therefore MUST be defined in the database schema. + * IMPORTANT: This {@code OAuth2AuthorizationConsentService} depends on the table + * definition described in + * "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql" + * and therefore MUST be defined in the database schema. * *

- * NOTE: This {@code OAuth2AuthorizationConsentService} is a simplified JDBC implementation that MAY be used in a production environment. - * However, it does have limitations as it likely won't perform well in an environment requiring high throughput. - * The expectation is that the consuming application will provide their own implementation of {@code OAuth2AuthorizationConsentService} - * that meets the performance requirements for its deployment environment. + * NOTE: This {@code OAuth2AuthorizationConsentService} is a simplified JDBC + * implementation that MAY be used in a production environment. However, it does have + * limitations as it likely won't perform well in an environment requiring high + * throughput. The expectation is that the consuming application will provide their own + * implementation of {@code OAuth2AuthorizationConsentService} that meets the performance + * requirements for its deployment environment. * * @author Ovidiu Popa * @author Josh Long @@ -109,13 +111,15 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio private static final String REMOVE_AUTHORIZATION_CONSENT_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + PK_FILTER; private final JdbcOperations jdbcOperations; + private RowMapper authorizationConsentRowMapper; + private Function> authorizationConsentParametersMapper; /** - * Constructs a {@code JdbcOAuth2AuthorizationConsentService} using the provided parameters. - * - * @param jdbcOperations the JDBC operations + * Constructs a {@code JdbcOAuth2AuthorizationConsentService} using the provided + * parameters. + * @param jdbcOperations the JDBC operations * @param registeredClientRepository the registered client repository */ public JdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations, @@ -130,11 +134,12 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio @Override public void save(OAuth2AuthorizationConsent authorizationConsent) { Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); - OAuth2AuthorizationConsent existingAuthorizationConsent = findById( - authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName()); + OAuth2AuthorizationConsent existingAuthorizationConsent = findById(authorizationConsent.getRegisteredClientId(), + authorizationConsent.getPrincipalName()); if (existingAuthorizationConsent == null) { insertAuthorizationConsent(authorizationConsent); - } else { + } + else { updateAuthorizationConsent(authorizationConsent); } } @@ -160,8 +165,7 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); SqlParameterValue[] parameters = new SqlParameterValue[] { new SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()), - new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) - }; + new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); this.jdbcOperations.update(REMOVE_AUTHORIZATION_CONSENT_SQL, pss); } @@ -173,7 +177,7 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio Assert.hasText(principalName, "principalName cannot be empty"); SqlParameterValue[] parameters = new SqlParameterValue[] { new SqlParameterValue(Types.VARCHAR, registeredClientId), - new SqlParameterValue(Types.VARCHAR, principalName)}; + new SqlParameterValue(Types.VARCHAR, principalName) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); List result = this.jdbcOperations.query(LOAD_AUTHORIZATION_CONSENT_SQL, pss, this.authorizationConsentRowMapper); @@ -184,22 +188,21 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio * Sets the {@link RowMapper} used for mapping the current row in * {@code java.sql.ResultSet} to {@link OAuth2AuthorizationConsent}. The default is * {@link OAuth2AuthorizationConsentRowMapper}. - * - * @param authorizationConsentRowMapper the {@link RowMapper} used for mapping the current - * row in {@code ResultSet} to {@link OAuth2AuthorizationConsent} + * @param authorizationConsentRowMapper the {@link RowMapper} used for mapping the + * current row in {@code ResultSet} to {@link OAuth2AuthorizationConsent} */ - public final void setAuthorizationConsentRowMapper(RowMapper authorizationConsentRowMapper) { + public final void setAuthorizationConsentRowMapper( + RowMapper authorizationConsentRowMapper) { Assert.notNull(authorizationConsentRowMapper, "authorizationConsentRowMapper cannot be null"); this.authorizationConsentRowMapper = authorizationConsentRowMapper; } /** - * Sets the {@code Function} used for mapping {@link OAuth2AuthorizationConsent} to - * a {@code List} of {@link SqlParameterValue}. The default is + * Sets the {@code Function} used for mapping {@link OAuth2AuthorizationConsent} to a + * {@code List} of {@link SqlParameterValue}. The default is * {@link OAuth2AuthorizationConsentParametersMapper}. - * * @param authorizationConsentParametersMapper the {@code Function} used for mapping - * {@link OAuth2AuthorizationConsent} to a {@code List} of {@link SqlParameterValue} + * {@link OAuth2AuthorizationConsent} to a {@code List} of {@link SqlParameterValue} */ public final void setAuthorizationConsentParametersMapper( Function> authorizationConsentParametersMapper) { @@ -220,10 +223,11 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio } /** - * The default {@link RowMapper} that maps the current row in - * {@code ResultSet} to {@link OAuth2AuthorizationConsent}. + * The default {@link RowMapper} that maps the current row in {@code ResultSet} to + * {@link OAuth2AuthorizationConsent}. */ public static class OAuth2AuthorizationConsentRowMapper implements RowMapper { + private final RegisteredClientRepository registeredClientRepository; public OAuth2AuthorizationConsentRowMapper(RegisteredClientRepository registeredClientRepository) { @@ -236,13 +240,14 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio String registeredClientId = rs.getString("registered_client_id"); RegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId); if (registeredClient == null) { - throw new DataRetrievalFailureException( - "The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository."); + throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId + + "' was not found in the RegisteredClientRepository."); } String principalName = rs.getString("principal_name"); - OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, principalName); + OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, + principalName); String authorizationConsentAuthorities = rs.getString("authorities"); if (authorizationConsentAuthorities != null) { for (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) { @@ -262,7 +267,8 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio * The default {@code Function} that maps {@link OAuth2AuthorizationConsent} to a * {@code List} of {@link SqlParameterValue}. */ - public static class OAuth2AuthorizationConsentParametersMapper implements Function> { + public static class OAuth2AuthorizationConsentParametersMapper + implements Function> { @Override public List apply(OAuth2AuthorizationConsent authorizationConsent) { @@ -274,7 +280,8 @@ public class JdbcOAuth2AuthorizationConsentService implements OAuth2Authorizatio for (GrantedAuthority authority : authorizationConsent.getAuthorities()) { authorities.add(authority.getAuthority()); } - parameters.add(new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToDelimitedString(authorities, ","))); + parameters + .add(new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToDelimitedString(authorities, ","))); return parameters; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java index b2f6e83f..0dce3f3c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationService.java @@ -72,16 +72,18 @@ import org.springframework.util.StringUtils; * {@link JdbcOperations} for {@link OAuth2Authorization} persistence. * *

- * IMPORTANT: This {@code OAuth2AuthorizationService} depends on the table definition - * described in - * "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql" and - * therefore MUST be defined in the database schema. + * IMPORTANT: This {@code OAuth2AuthorizationService} depends on the table + * definition described in + * "classpath:org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql" + * and therefore MUST be defined in the database schema. * *

- * NOTE: This {@code OAuth2AuthorizationService} is a simplified JDBC implementation that MAY be used in a production environment. - * However, it does have limitations as it likely won't perform well in an environment requiring high throughput. - * The expectation is that the consuming application will provide their own implementation of {@code OAuth2AuthorizationService} - * that meets the performance requirements for its deployment environment. + * NOTE: This {@code OAuth2AuthorizationService} is a simplified JDBC + * implementation that MAY be used in a production environment. However, it does have + * limitations as it likely won't perform well in an environment requiring high + * throughput. The expectation is that the consuming application will provide their own + * implementation of {@code OAuth2AuthorizationService} that meets the performance + * requirements for its deployment environment. * * @author Ovidiu Popa * @author Joe Grandja @@ -144,16 +146,23 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic private static final String TABLE_NAME = "oauth2_authorization"; private static final String PK_FILTER = "id = ?"; + private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorization_code_value = ? OR " + "access_token_value = ? OR oidc_id_token_value = ? OR refresh_token_value = ? OR user_code_value = ? OR " + "device_code_value = ?"; private static final String STATE_FILTER = "state = ?"; + private static final String AUTHORIZATION_CODE_FILTER = "authorization_code_value = ?"; + private static final String ACCESS_TOKEN_FILTER = "access_token_value = ?"; + private static final String ID_TOKEN_FILTER = "oidc_id_token_value = ?"; + private static final String REFRESH_TOKEN_FILTER = "refresh_token_value = ?"; + private static final String USER_CODE_FILTER = "user_code_value = ?"; + private static final String DEVICE_CODE_FILTER = "device_code_value = ?"; // @formatter:off @@ -184,14 +193,16 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic private static Map columnMetadataMap; private final JdbcOperations jdbcOperations; + private final LobHandler lobHandler; + private RowMapper authorizationRowMapper; + private Function> authorizationParametersMapper; /** * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters. - * - * @param jdbcOperations the JDBC operations + * @param jdbcOperations the JDBC operations * @param registeredClientRepository the registered client repository */ public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations, @@ -201,10 +212,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic /** * Constructs a {@code JdbcOAuth2AuthorizationService} using the provided parameters. - * - * @param jdbcOperations the JDBC operations + * @param jdbcOperations the JDBC operations * @param registeredClientRepository the registered client repository - * @param lobHandler the handler for large binary fields and large text fields + * @param lobHandler the handler for large binary fields and large text fields */ public JdbcOAuth2AuthorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository, LobHandler lobHandler) { @@ -213,7 +223,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic Assert.notNull(lobHandler, "lobHandler cannot be null"); this.jdbcOperations = jdbcOperations; this.lobHandler = lobHandler; - OAuth2AuthorizationRowMapper authorizationRowMapper = new OAuth2AuthorizationRowMapper(registeredClientRepository); + OAuth2AuthorizationRowMapper authorizationRowMapper = new OAuth2AuthorizationRowMapper( + registeredClientRepository); authorizationRowMapper.setLobHandler(lobHandler); this.authorizationRowMapper = authorizationRowMapper; this.authorizationParametersMapper = new OAuth2AuthorizationParametersMapper(); @@ -226,7 +237,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic OAuth2Authorization existingAuthorization = findById(authorization.getId()); if (existingAuthorization == null) { insertAuthorization(authorization); - } else { + } + else { updateAuthorization(authorization); } } @@ -255,8 +267,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic public void remove(OAuth2Authorization authorization) { Assert.notNull(authorization, "authorization cannot be null"); SqlParameterValue[] parameters = new SqlParameterValue[] { - new SqlParameterValue(Types.VARCHAR, authorization.getId()) - }; + new SqlParameterValue(Types.VARCHAR, authorization.getId()) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); this.jdbcOperations.update(REMOVE_AUTHORIZATION_SQL, pss); } @@ -284,25 +295,32 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic parameters.add(mapToSqlParameter("user_code_value", token)); parameters.add(mapToSqlParameter("device_code_value", token)); return findBy(UNKNOWN_TOKEN_TYPE_FILTER, parameters); - } else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) { + } + else if (OAuth2ParameterNames.STATE.equals(tokenType.getValue())) { parameters.add(new SqlParameterValue(Types.VARCHAR, token)); return findBy(STATE_FILTER, parameters); - } else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) { + } + else if (OAuth2ParameterNames.CODE.equals(tokenType.getValue())) { parameters.add(mapToSqlParameter("authorization_code_value", token)); return findBy(AUTHORIZATION_CODE_FILTER, parameters); - } else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) { + } + else if (OAuth2TokenType.ACCESS_TOKEN.equals(tokenType)) { parameters.add(mapToSqlParameter("access_token_value", token)); return findBy(ACCESS_TOKEN_FILTER, parameters); - } else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) { + } + else if (OidcParameterNames.ID_TOKEN.equals(tokenType.getValue())) { parameters.add(mapToSqlParameter("oidc_id_token_value", token)); return findBy(ID_TOKEN_FILTER, parameters); - } else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) { + } + else if (OAuth2TokenType.REFRESH_TOKEN.equals(tokenType)) { parameters.add(mapToSqlParameter("refresh_token_value", token)); return findBy(REFRESH_TOKEN_FILTER, parameters); - } else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) { + } + else if (OAuth2ParameterNames.USER_CODE.equals(tokenType.getValue())) { parameters.add(mapToSqlParameter("user_code_value", token)); return findBy(USER_CODE_FILTER, parameters); - } else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) { + } + else if (OAuth2ParameterNames.DEVICE_CODE.equals(tokenType.getValue())) { parameters.add(mapToSqlParameter("device_code_value", token)); return findBy(DEVICE_CODE_FILTER, parameters); } @@ -313,7 +331,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic try (LobCreator lobCreator = getLobHandler().getLobCreator()) { PreparedStatementSetter pss = new LobCreatorArgumentPreparedStatementSetter(lobCreator, parameters.toArray()); - List result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, pss, getAuthorizationRowMapper()); + List result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, pss, + getAuthorizationRowMapper()); return !result.isEmpty() ? result.get(0) : null; } } @@ -322,9 +341,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic * Sets the {@link RowMapper} used for mapping the current row in * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. The default is * {@link OAuth2AuthorizationRowMapper}. - * * @param authorizationRowMapper the {@link RowMapper} used for mapping the current - * row in {@code ResultSet} to {@link OAuth2Authorization} + * row in {@code ResultSet} to {@link OAuth2Authorization} */ public final void setAuthorizationRowMapper(RowMapper authorizationRowMapper) { Assert.notNull(authorizationRowMapper, "authorizationRowMapper cannot be null"); @@ -332,12 +350,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic } /** - * Sets the {@code Function} used for mapping {@link OAuth2Authorization} to - * a {@code List} of {@link SqlParameterValue}. The default is + * Sets the {@code Function} used for mapping {@link OAuth2Authorization} to a + * {@code List} of {@link SqlParameterValue}. The default is * {@link OAuth2AuthorizationParametersMapper}. - * * @param authorizationParametersMapper the {@code Function} used for mapping - * {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue} + * {@link OAuth2Authorization} to a {@code List} of {@link SqlParameterValue} */ public final void setAuthorizationParametersMapper( Function> authorizationParametersMapper) { @@ -366,8 +383,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic * {@code java.sql.ResultSet} to {@link OAuth2Authorization}. */ public static class OAuth2AuthorizationRowMapper implements RowMapper { + private final RegisteredClientRepository registeredClientRepository; + private LobHandler lobHandler = new DefaultLobHandler(); + private ObjectMapper objectMapper = new ObjectMapper(); public OAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) { @@ -386,8 +406,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic String registeredClientId = rs.getString("registered_client_id"); RegisteredClient registeredClient = this.registeredClientRepository.findById(registeredClientId); if (registeredClient == null) { - throw new DataRetrievalFailureException( - "The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository."); + throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId + + "' was not found in the RegisteredClientRepository."); } OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient); @@ -402,10 +422,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic Map attributes = parseMap(getLobValue(rs, "attributes")); builder.id(id) - .principalName(principalName) - .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType)) - .authorizedScopes(authorizedScopes) - .attributes((attrs) -> attrs.putAll(attributes)); + .principalName(principalName) + .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType)) + .authorizedScopes(authorizedScopes) + .attributes((attrs) -> attrs.putAll(attributes)); String state = rs.getString("state"); if (StringUtils.hasText(state)) { @@ -419,10 +439,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic if (StringUtils.hasText(authorizationCodeValue)) { tokenIssuedAt = rs.getTimestamp("authorization_code_issued_at").toInstant(); tokenExpiresAt = rs.getTimestamp("authorization_code_expires_at").toInstant(); - Map authorizationCodeMetadata = parseMap(getLobValue(rs, "authorization_code_metadata")); + Map authorizationCodeMetadata = parseMap( + getLobValue(rs, "authorization_code_metadata")); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - authorizationCodeValue, tokenIssuedAt, tokenExpiresAt); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(authorizationCodeValue, + tokenIssuedAt, tokenExpiresAt); builder.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata)); } @@ -441,7 +462,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic if (accessTokenScopes != null) { scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes); } - OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, accessTokenValue, tokenIssuedAt, tokenExpiresAt, scopes); + OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, accessTokenValue, tokenIssuedAt, + tokenExpiresAt, scopes); builder.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata)); } @@ -451,8 +473,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic tokenExpiresAt = rs.getTimestamp("oidc_id_token_expires_at").toInstant(); Map oidcTokenMetadata = parseMap(getLobValue(rs, "oidc_id_token_metadata")); - OidcIdToken oidcToken = new OidcIdToken( - oidcIdTokenValue, tokenIssuedAt, tokenExpiresAt, (Map) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)); + OidcIdToken oidcToken = new OidcIdToken(oidcIdTokenValue, tokenIssuedAt, tokenExpiresAt, + (Map) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)); builder.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata)); } @@ -466,8 +488,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic } Map refreshTokenMetadata = parseMap(getLobValue(rs, "refresh_token_metadata")); - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - refreshTokenValue, tokenIssuedAt, tokenExpiresAt); + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(refreshTokenValue, tokenIssuedAt, + tokenExpiresAt); builder.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata)); } @@ -502,9 +524,11 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic if (columnValueBytes != null) { columnValue = new String(columnValueBytes, StandardCharsets.UTF_8); } - } else if (Types.CLOB == columnMetadata.getDataType()) { + } + else if (Types.CLOB == columnMetadata.getDataType()) { columnValue = this.lobHandler.getClobAsString(rs, columnName); - } else { + } + else { columnValue = rs.getString(columnName); } return columnValue; @@ -534,8 +558,10 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic private Map parseMap(String data) { try { - return this.objectMapper.readValue(data, new TypeReference>() {}); - } catch (Exception ex) { + return this.objectMapper.readValue(data, new TypeReference>() { + }); + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } @@ -546,7 +572,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic * The default {@code Function} that maps {@link OAuth2Authorization} to a * {@code List} of {@link SqlParameterValue}. */ - public static class OAuth2AuthorizationParametersMapper implements Function> { + public static class OAuth2AuthorizationParametersMapper + implements Function> { + private ObjectMapper objectMapper = new ObjectMapper(); public OAuth2AuthorizationParametersMapper() { @@ -580,46 +608,46 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic } parameters.add(new SqlParameterValue(Types.VARCHAR, state)); - OAuth2Authorization.Token authorizationCode = - authorization.getToken(OAuth2AuthorizationCode.class); - List authorizationCodeSqlParameters = toSqlParameterList( - "authorization_code_value", "authorization_code_metadata", authorizationCode); + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); + List authorizationCodeSqlParameters = toSqlParameterList("authorization_code_value", + "authorization_code_metadata", authorizationCode); parameters.addAll(authorizationCodeSqlParameters); - OAuth2Authorization.Token accessToken = - authorization.getToken(OAuth2AccessToken.class); - List accessTokenSqlParameters = toSqlParameterList( - "access_token_value", "access_token_metadata", accessToken); + OAuth2Authorization.Token accessToken = authorization.getToken(OAuth2AccessToken.class); + List accessTokenSqlParameters = toSqlParameterList("access_token_value", + "access_token_metadata", accessToken); parameters.addAll(accessTokenSqlParameters); String accessTokenType = null; String accessTokenScopes = null; if (accessToken != null) { accessTokenType = accessToken.getToken().getTokenType().getValue(); if (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) { - accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), ","); + accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), + ","); } } parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType)); parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenScopes)); OAuth2Authorization.Token oidcIdToken = authorization.getToken(OidcIdToken.class); - List oidcIdTokenSqlParameters = toSqlParameterList( - "oidc_id_token_value", "oidc_id_token_metadata", oidcIdToken); + List oidcIdTokenSqlParameters = toSqlParameterList("oidc_id_token_value", + "oidc_id_token_metadata", oidcIdToken); parameters.addAll(oidcIdTokenSqlParameters); OAuth2Authorization.Token refreshToken = authorization.getRefreshToken(); - List refreshTokenSqlParameters = toSqlParameterList( - "refresh_token_value", "refresh_token_metadata", refreshToken); + List refreshTokenSqlParameters = toSqlParameterList("refresh_token_value", + "refresh_token_metadata", refreshToken); parameters.addAll(refreshTokenSqlParameters); OAuth2Authorization.Token userCode = authorization.getToken(OAuth2UserCode.class); - List userCodeSqlParameters = toSqlParameterList( - "user_code_value", "user_code_metadata", userCode); + List userCodeSqlParameters = toSqlParameterList("user_code_value", "user_code_metadata", + userCode); parameters.addAll(userCodeSqlParameters); OAuth2Authorization.Token deviceCode = authorization.getToken(OAuth2DeviceCode.class); - List deviceCodeSqlParameters = toSqlParameterList( - "device_code_value", "device_code_metadata", deviceCode); + List deviceCodeSqlParameters = toSqlParameterList("device_code_value", + "device_code_metadata", deviceCode); parameters.addAll(deviceCodeSqlParameters); return parameters; @@ -634,8 +662,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic return this.objectMapper; } - private List toSqlParameterList( - String tokenColumnName, String tokenMetadataColumnName, OAuth2Authorization.Token token) { + private List toSqlParameterList(String tokenColumnName, + String tokenMetadataColumnName, OAuth2Authorization.Token token) { List parameters = new ArrayList<>(); String tokenValue = null; @@ -663,7 +691,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic private String writeMap(Map data) { try { return this.objectMapper.writeValueAsString(data); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } @@ -671,6 +700,7 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic } private static final class LobCreatorArgumentPreparedStatementSetter extends ArgumentPreparedStatementSetter { + private final LobCreator lobCreator; private LobCreatorArgumentPreparedStatementSetter(LobCreator lobCreator, Object[] args) { @@ -707,7 +737,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic } private static final class ColumnMetadata { + private final String columnName; + private final int dataType; private ColumnMetadata(String columnName, int dataType) { @@ -757,7 +789,8 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic columnMetadataMap.put(columnMetadata.getColumnName(), columnMetadata); } - private static ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName, int defaultDataType) { + private static ColumnMetadata getColumnMetadata(JdbcOperations jdbcOperations, String columnName, + int defaultDataType) { Integer dataType = jdbcOperations.execute((ConnectionCallback) conn -> { DatabaseMetaData databaseMetaData = conn.getMetaData(); ResultSet rs = databaseMetaData.getColumns(null, null, TABLE_NAME, columnName); @@ -765,10 +798,13 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic return rs.getInt("DATA_TYPE"); } // NOTE: (Applies to HSQL) - // When a database object is created with one of the CREATE statements or renamed with the ALTER statement, - // if the name is enclosed in double quotes, the exact name is used as the case-normal form. + // When a database object is created with one of the CREATE statements or + // renamed with the ALTER statement, + // if the name is enclosed in double quotes, the exact name is used as the + // case-normal form. // But if it is not enclosed in double quotes, - // the name is converted to uppercase and this uppercase version is stored in the database as the case-normal form. + // the name is converted to uppercase and this uppercase version is stored in + // the database as the case-normal form. rs = databaseMetaData.getColumns(null, null, TABLE_NAME.toUpperCase(), columnName.toUpperCase()); if (rs.next()) { return rs.getInt("DATA_TYPE"); @@ -780,9 +816,9 @@ public class JdbcOAuth2AuthorizationService implements OAuth2AuthorizationServic private static SqlParameterValue mapToSqlParameter(String columnName, String value) { ColumnMetadata columnMetadata = columnMetadataMap.get(columnName); - return Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value) ? - new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8)) : - new SqlParameterValue(columnMetadata.getDataType(), value); + return Types.BLOB == columnMetadata.getDataType() && StringUtils.hasText(value) + ? new SqlParameterValue(Types.BLOB, value.getBytes(StandardCharsets.UTF_8)) + : new SqlParameterValue(columnMetadata.getDataType(), value); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java index ef8bb69d..d40399cc 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2Authorization.java @@ -38,9 +38,10 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * A representation of an OAuth 2.0 Authorization, which holds state related to the authorization granted - * to a {@link #getRegisteredClientId() client}, by the {@link #getPrincipalName() resource owner} - * or itself in the case of the {@code client_credentials} grant type. + * A representation of an OAuth 2.0 Authorization, which holds state related to the + * authorization granted to a {@link #getRegisteredClientId() client}, by the + * {@link #getPrincipalName() resource owner} or itself in the case of the + * {@code client_credentials} grant type. * * @author Joe Grandja * @author Krisztian Toth @@ -52,13 +53,21 @@ import org.springframework.util.StringUtils; * @see OAuth2RefreshToken */ public class OAuth2Authorization implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private String id; + private String registeredClientId; + private String principalName; + private AuthorizationGrantType authorizationGrantType; + private Set authorizedScopes; + private Map, Token> tokens; + private Map attributes; protected OAuth2Authorization() { @@ -66,7 +75,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the identifier for the authorization. - * * @return the identifier for the authorization */ public String getId() { @@ -75,7 +83,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the identifier for the {@link RegisteredClient#getId() registered client}. - * * @return the {@link RegisteredClient#getId()} */ public String getRegisteredClientId() { @@ -84,7 +91,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the {@code Principal} name of the resource owner (or client). - * * @return the {@code Principal} name of the resource owner (or client) */ public String getPrincipalName() { @@ -92,8 +98,8 @@ public class OAuth2Authorization implements Serializable { } /** - * Returns the {@link AuthorizationGrantType authorization grant type} used for the authorization. - * + * Returns the {@link AuthorizationGrantType authorization grant type} used for the + * authorization. * @return the {@link AuthorizationGrantType} used for the authorization */ public AuthorizationGrantType getAuthorizationGrantType() { @@ -102,7 +108,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the authorized scope(s). - * * @return the {@code Set} of authorized scope(s) * @since 0.4.0 */ @@ -112,7 +117,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the {@link Token} of type {@link OAuth2AccessToken}. - * * @return the {@link Token} of type {@link OAuth2AccessToken} */ public Token getAccessToken() { @@ -121,8 +125,8 @@ public class OAuth2Authorization implements Serializable { /** * Returns the {@link Token} of type {@link OAuth2RefreshToken}. - * - * @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if not available + * @return the {@link Token} of type {@link OAuth2RefreshToken}, or {@code null} if + * not available */ @Nullable public Token getRefreshToken() { @@ -131,7 +135,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the {@link Token} of type {@code tokenType}. - * * @param tokenType the token type * @param the type of the token * @return the {@link Token}, or {@code null} if not available @@ -146,7 +149,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the {@link Token} matching the {@code tokenValue}. - * * @param tokenValue the token value * @param the type of the token * @return the {@link Token}, or {@code null} if not available @@ -165,7 +167,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the attribute(s) associated to the authorization. - * * @return a {@code Map} of the attribute(s) */ public Map getAttributes() { @@ -174,10 +175,10 @@ public class OAuth2Authorization implements Serializable { /** * Returns the value of an attribute associated to the authorization. - * * @param name the name of the attribute * @param the type of the attribute - * @return the value of an attribute associated to the authorization, or {@code null} if not available + * @return the value of an attribute associated to the authorization, or {@code null} + * if not available */ @Nullable @SuppressWarnings("unchecked") @@ -195,24 +196,22 @@ public class OAuth2Authorization implements Serializable { return false; } OAuth2Authorization that = (OAuth2Authorization) obj; - return Objects.equals(this.id, that.id) && - Objects.equals(this.registeredClientId, that.registeredClientId) && - Objects.equals(this.principalName, that.principalName) && - Objects.equals(this.authorizationGrantType, that.authorizationGrantType) && - Objects.equals(this.authorizedScopes, that.authorizedScopes) && - Objects.equals(this.tokens, that.tokens) && - Objects.equals(this.attributes, that.attributes); + return Objects.equals(this.id, that.id) && Objects.equals(this.registeredClientId, that.registeredClientId) + && Objects.equals(this.principalName, that.principalName) + && Objects.equals(this.authorizationGrantType, that.authorizationGrantType) + && Objects.equals(this.authorizedScopes, that.authorizedScopes) + && Objects.equals(this.tokens, that.tokens) && Objects.equals(this.attributes, that.attributes); } @Override public int hashCode() { - return Objects.hash(this.id, this.registeredClientId, this.principalName, - this.authorizationGrantType, this.authorizedScopes, this.tokens, this.attributes); + return Objects.hash(this.id, this.registeredClientId, this.principalName, this.authorizationGrantType, + this.authorizedScopes, this.tokens, this.attributes); } /** - * Returns a new {@link Builder}, initialized with the provided {@link RegisteredClient#getId()}. - * + * Returns a new {@link Builder}, initialized with the provided + * {@link RegisteredClient#getId()}. * @param registeredClient the {@link RegisteredClient} * @return the {@link Builder} */ @@ -222,20 +221,20 @@ public class OAuth2Authorization implements Serializable { } /** - * Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2Authorization}. - * - * @param authorization the {@code OAuth2Authorization} used for initializing the {@link Builder} + * Returns a new {@link Builder}, initialized with the values from the provided + * {@code OAuth2Authorization}. + * @param authorization the {@code OAuth2Authorization} used for initializing the + * {@link Builder} * @return the {@link Builder} */ public static Builder from(OAuth2Authorization authorization) { Assert.notNull(authorization, "authorization cannot be null"); - return new Builder(authorization.getRegisteredClientId()) - .id(authorization.getId()) - .principalName(authorization.getPrincipalName()) - .authorizationGrantType(authorization.getAuthorizationGrantType()) - .authorizedScopes(authorization.getAuthorizedScopes()) - .tokens(authorization.tokens) - .attributes(attrs -> attrs.putAll(authorization.getAttributes())); + return new Builder(authorization.getRegisteredClientId()).id(authorization.getId()) + .principalName(authorization.getPrincipalName()) + .authorizationGrantType(authorization.getAuthorizationGrantType()) + .authorizedScopes(authorization.getAuthorizedScopes()) + .tokens(authorization.tokens) + .attributes(attrs -> attrs.putAll(authorization.getAttributes())); } /** @@ -245,7 +244,9 @@ public class OAuth2Authorization implements Serializable { * @since 0.1.0 */ public static class Token implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + protected static final String TOKEN_METADATA_NAMESPACE = "metadata.token."; /** @@ -259,6 +260,7 @@ public class OAuth2Authorization implements Serializable { public static final String CLAIMS_METADATA_NAME = TOKEN_METADATA_NAMESPACE.concat("claims"); private final T token; + private final Map metadata; protected Token(T token) { @@ -272,7 +274,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the token of type {@link OAuth2Token}. - * * @return the token of type {@link OAuth2Token} */ public T getToken() { @@ -280,9 +281,8 @@ public class OAuth2Authorization implements Serializable { } /** - * Returns {@code true} if the token has been invalidated (e.g. revoked). - * The default is {@code false}. - * + * Returns {@code true} if the token has been invalidated (e.g. revoked). The + * default is {@code false}. * @return {@code true} if the token has been invalidated, {@code false} otherwise */ public boolean isInvalidated() { @@ -291,7 +291,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns {@code true} if the token has expired. - * * @return {@code true} if the token has expired, {@code false} otherwise */ public boolean isExpired() { @@ -300,8 +299,8 @@ public class OAuth2Authorization implements Serializable { /** * Returns {@code true} if the token is before the time it can be used. - * - * @return {@code true} if the token is before the time it can be used, {@code false} otherwise + * @return {@code true} if the token is before the time it can be used, + * {@code false} otherwise */ public boolean isBeforeUse() { Instant notBefore = null; @@ -313,7 +312,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns {@code true} if the token is currently active. - * * @return {@code true} if the token is currently active, {@code false} otherwise */ public boolean isActive() { @@ -322,7 +320,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the claims associated to the token. - * * @return a {@code Map} of the claims, or {@code null} if not available */ @Nullable @@ -332,7 +329,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the value of the metadata associated to the token. - * * @param name the name of the metadata * @param the value type of the metadata * @return the value of the metadata, or {@code null} if not available @@ -346,7 +342,6 @@ public class OAuth2Authorization implements Serializable { /** * Returns the metadata associated to the token. - * * @return a {@code Map} of the metadata */ public Map getMetadata() { @@ -368,27 +363,35 @@ public class OAuth2Authorization implements Serializable { return false; } Token that = (Token) obj; - return Objects.equals(this.token, that.token) && - Objects.equals(this.metadata, that.metadata); + return Objects.equals(this.token, that.token) && Objects.equals(this.metadata, that.metadata); } @Override public int hashCode() { return Objects.hash(this.token, this.metadata); } + } /** * A builder for {@link OAuth2Authorization}. */ public static class Builder implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private String id; + private final String registeredClientId; + private String principalName; + private AuthorizationGrantType authorizationGrantType; + private Set authorizedScopes; + private Map, Token> tokens = new HashMap<>(); + private final Map attributes = new HashMap<>(); protected Builder(String registeredClientId) { @@ -397,7 +400,6 @@ public class OAuth2Authorization implements Serializable { /** * Sets the identifier for the authorization. - * * @param id the identifier for the authorization * @return the {@link Builder} */ @@ -408,8 +410,8 @@ public class OAuth2Authorization implements Serializable { /** * Sets the {@code Principal} name of the resource owner (or client). - * - * @param principalName the {@code Principal} name of the resource owner (or client) + * @param principalName the {@code Principal} name of the resource owner (or + * client) * @return the {@link Builder} */ public Builder principalName(String principalName) { @@ -418,8 +420,8 @@ public class OAuth2Authorization implements Serializable { } /** - * Sets the {@link AuthorizationGrantType authorization grant type} used for the authorization. - * + * Sets the {@link AuthorizationGrantType authorization grant type} used for the + * authorization. * @param authorizationGrantType the {@link AuthorizationGrantType} * @return the {@link Builder} */ @@ -430,7 +432,6 @@ public class OAuth2Authorization implements Serializable { /** * Sets the authorized scope(s). - * * @param authorizedScopes the {@code Set} of authorized scope(s) * @return the {@link Builder} * @since 0.4.0 @@ -442,7 +443,6 @@ public class OAuth2Authorization implements Serializable { /** * Sets the {@link OAuth2AccessToken access token}. - * * @param accessToken the {@link OAuth2AccessToken} * @return the {@link Builder} */ @@ -452,7 +452,6 @@ public class OAuth2Authorization implements Serializable { /** * Sets the {@link OAuth2RefreshToken refresh token}. - * * @param refreshToken the {@link OAuth2RefreshToken} * @return the {@link Builder} */ @@ -462,25 +461,23 @@ public class OAuth2Authorization implements Serializable { /** * Sets the {@link OAuth2Token token}. - * * @param token the token * @param the type of the token * @return the {@link Builder} */ public Builder token(T token) { - return token(token, (metadata) -> {}); + return token(token, (metadata) -> { + }); } /** * Sets the {@link OAuth2Token token} and associated metadata. - * * @param token the token * @param metadataConsumer a {@code Consumer} of the metadata {@code Map} * @param the type of the token * @return the {@link Builder} */ - public Builder token(T token, - Consumer> metadataConsumer) { + public Builder token(T token, Consumer> metadataConsumer) { Assert.notNull(token, "token cannot be null"); Map metadata = Token.defaultMetadata(); @@ -501,7 +498,6 @@ public class OAuth2Authorization implements Serializable { /** * Adds an attribute associated to the authorization. - * * @param name the name of the attribute * @param value the value of the attribute * @return the {@link Builder} @@ -514,9 +510,8 @@ public class OAuth2Authorization implements Serializable { } /** - * A {@code Consumer} of the attributes {@code Map} - * allowing the ability to add, replace, or remove. - * + * A {@code Consumer} of the attributes {@code Map} allowing the ability to add, + * replace, or remove. * @param attributesConsumer a {@link Consumer} of the attributes {@code Map} * @return the {@link Builder} */ @@ -527,7 +522,6 @@ public class OAuth2Authorization implements Serializable { /** * Builds a new {@link OAuth2Authorization}. - * * @return the {@link OAuth2Authorization} */ public OAuth2Authorization build() { @@ -542,12 +536,8 @@ public class OAuth2Authorization implements Serializable { authorization.registeredClientId = this.registeredClientId; authorization.principalName = this.principalName; authorization.authorizationGrantType = this.authorizationGrantType; - authorization.authorizedScopes = - Collections.unmodifiableSet( - !CollectionUtils.isEmpty(this.authorizedScopes) ? - new HashSet<>(this.authorizedScopes) : - new HashSet<>() - ); + authorization.authorizedScopes = Collections.unmodifiableSet(!CollectionUtils.isEmpty(this.authorizedScopes) + ? new HashSet<>(this.authorizedScopes) : new HashSet<>()); authorization.tokens = Collections.unmodifiableMap(this.tokens); authorization.attributes = Collections.unmodifiableMap(this.attributes); return authorization; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java index 9ad711a7..4447962b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationCode.java @@ -20,13 +20,14 @@ import java.time.Instant; import org.springframework.security.oauth2.core.AbstractOAuth2Token; /** - * An implementation of an {@link AbstractOAuth2Token} - * representing an OAuth 2.0 Authorization Code Grant. + * An implementation of an {@link AbstractOAuth2Token} representing an OAuth 2.0 + * Authorization Code Grant. * * @author Joe Grandja * @since 0.0.3 * @see AbstractOAuth2Token - * @see Section 4.1 Authorization Code Grant + * @see Section + * 4.1 Authorization Code Grant */ public class OAuth2AuthorizationCode extends AbstractOAuth2Token { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsent.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsent.java index 3d13f4bd..5fb89538 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsent.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsent.java @@ -31,26 +31,33 @@ import org.springframework.util.Assert; import org.springframework.util.CollectionUtils; /** - * A representation of an OAuth 2.0 "consent" to an Authorization request, which holds state related to the - * set of {@link #getAuthorities() authorities} granted to a {@link #getRegisteredClientId() client} by the - * {@link #getPrincipalName() resource owner}. + * A representation of an OAuth 2.0 "consent" to an Authorization request, which holds + * state related to the set of {@link #getAuthorities() authorities} granted to a + * {@link #getRegisteredClientId() client} by the {@link #getPrincipalName() resource + * owner}. *

- * When authorizing access for a given client, the resource owner may only grant a subset of the authorities - * the client requested. The typical use-case is the {@code authorization_code} flow, in which the client - * requests a set of {@code scope}s. The resource owner then selects which scopes they grant to the client. + * When authorizing access for a given client, the resource owner may only grant a subset + * of the authorities the client requested. The typical use-case is the + * {@code authorization_code} flow, in which the client requests a set of {@code scope}s. + * The resource owner then selects which scopes they grant to the client. * * @author Daniel Garnier-Moiroux * @since 0.1.2 */ public final class OAuth2AuthorizationConsent implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private static final String AUTHORITIES_SCOPE_PREFIX = "SCOPE_"; private final String registeredClientId; + private final String principalName; + private final Set authorities; - private OAuth2AuthorizationConsent(String registeredClientId, String principalName, Set authorities) { + private OAuth2AuthorizationConsent(String registeredClientId, String principalName, + Set authorities) { this.registeredClientId = registeredClientId; this.principalName = principalName; this.authorities = Collections.unmodifiableSet(authorities); @@ -58,7 +65,6 @@ public final class OAuth2AuthorizationConsent implements Serializable { /** * Returns the identifier for the {@link RegisteredClient#getId() registered client}. - * * @return the {@link RegisteredClient#getId()} */ public String getRegisteredClientId() { @@ -67,7 +73,6 @@ public final class OAuth2AuthorizationConsent implements Serializable { /** * Returns the {@code Principal} name of the resource owner (or client). - * * @return the {@code Principal} name of the resource owner (or client) */ public String getPrincipalName() { @@ -75,18 +80,18 @@ public final class OAuth2AuthorizationConsent implements Serializable { } /** - * Returns the {@link GrantedAuthority authorities} granted to the client by the principal. - * - * @return the {@link GrantedAuthority authorities} granted to the client by the principal. + * Returns the {@link GrantedAuthority authorities} granted to the client by the + * principal. + * @return the {@link GrantedAuthority authorities} granted to the client by the + * principal. */ public Set getAuthorities() { return this.authorities; } /** - * Convenience method for obtaining the {@code scope}s granted to the client by the principal, - * extracted from the {@link #getAuthorities() authorities}. - * + * Convenience method for obtaining the {@code scope}s granted to the client by the + * principal, extracted from the {@link #getAuthorities() authorities}. * @return the {@code scope}s granted to the client by the principal. */ public Set getScopes() { @@ -108,9 +113,9 @@ public final class OAuth2AuthorizationConsent implements Serializable { return false; } OAuth2AuthorizationConsent that = (OAuth2AuthorizationConsent) obj; - return Objects.equals(this.registeredClientId, that.registeredClientId) && - Objects.equals(this.principalName, that.principalName) && - Objects.equals(this.authorities, that.authorities); + return Objects.equals(this.registeredClientId, that.registeredClientId) + && Objects.equals(this.principalName, that.principalName) + && Objects.equals(this.authorities, that.authorities); } @Override @@ -119,26 +124,24 @@ public final class OAuth2AuthorizationConsent implements Serializable { } /** - * Returns a new {@link Builder}, initialized with the values from the provided {@code OAuth2AuthorizationConsent}. - * - * @param authorizationConsent the {@code OAuth2AuthorizationConsent} used for initializing the {@link Builder} + * Returns a new {@link Builder}, initialized with the values from the provided + * {@code OAuth2AuthorizationConsent}. + * @param authorizationConsent the {@code OAuth2AuthorizationConsent} used for + * initializing the {@link Builder} * @return the {@link Builder} */ public static Builder from(OAuth2AuthorizationConsent authorizationConsent) { Assert.notNull(authorizationConsent, "authorizationConsent cannot be null"); - return new Builder( - authorizationConsent.getRegisteredClientId(), - authorizationConsent.getPrincipalName(), - authorizationConsent.getAuthorities() - ); + return new Builder(authorizationConsent.getRegisteredClientId(), authorizationConsent.getPrincipalName(), + authorizationConsent.getAuthorities()); } /** - * Returns a new {@link Builder}, initialized with the given {@link RegisteredClient#getClientId() registeredClientId} - * and {@code Principal} name. - * + * Returns a new {@link Builder}, initialized with the given + * {@link RegisteredClient#getClientId() registeredClientId} and {@code Principal} + * name. * @param registeredClientId the {@link RegisteredClient#getId()} - * @param principalName the {@code Principal} name + * @param principalName the {@code Principal} name * @return the {@link Builder} */ public static Builder withId(@NonNull String registeredClientId, @NonNull String principalName) { @@ -147,15 +150,17 @@ public final class OAuth2AuthorizationConsent implements Serializable { return new Builder(registeredClientId, principalName); } - /** * A builder for {@link OAuth2AuthorizationConsent}. */ public static final class Builder implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; private final String registeredClientId; + private final String principalName; + private final Set authorities = new HashSet<>(); private Builder(String registeredClientId, String principalName) { @@ -171,10 +176,10 @@ public final class OAuth2AuthorizationConsent implements Serializable { } /** - * Adds a scope to the collection of {@code authorities} in the resulting {@link OAuth2AuthorizationConsent}, - * wrapping it in a {@link SimpleGrantedAuthority}, prefixed by {@code SCOPE_}. For example, a + * Adds a scope to the collection of {@code authorities} in the resulting + * {@link OAuth2AuthorizationConsent}, wrapping it in a + * {@link SimpleGrantedAuthority}, prefixed by {@code SCOPE_}. For example, a * {@code message.write} scope would be stored as {@code SCOPE_message.write}. - * * @param scope the scope * @return the {@code Builder} for further configuration */ @@ -186,7 +191,6 @@ public final class OAuth2AuthorizationConsent implements Serializable { /** * Adds a {@link GrantedAuthority} to the collection of {@code authorities} in the * resulting {@link OAuth2AuthorizationConsent}. - * * @param authority the {@link GrantedAuthority} * @return the {@code Builder} for further configuration */ @@ -196,8 +200,8 @@ public final class OAuth2AuthorizationConsent implements Serializable { } /** - * A {@code Consumer} of the {@code authorities}, allowing the ability to add, replace or remove. - * + * A {@code Consumer} of the {@code authorities}, allowing the ability to add, + * replace or remove. * @param authoritiesConsumer a {@code Consumer} of the {@code authorities} * @return the {@code Builder} for further configuration */ @@ -209,12 +213,13 @@ public final class OAuth2AuthorizationConsent implements Serializable { /** * Validate the authorities and build the {@link OAuth2AuthorizationConsent}. * There must be at least one {@link GrantedAuthority}. - * * @return the {@link OAuth2AuthorizationConsent} */ public OAuth2AuthorizationConsent build() { Assert.notEmpty(this.authorities, "authorities cannot be empty"); return new OAuth2AuthorizationConsent(this.registeredClientId, this.principalName, this.authorities); } + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java index 04f6607c..19e0622b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentService.java @@ -21,8 +21,8 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import java.security.Principal; /** - * Implementations of this interface are responsible for the management - * of {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}. + * Implementations of this interface are responsible for the management of + * {@link OAuth2AuthorizationConsent OAuth 2.0 Authorization Consent(s)}. * * @author Daniel Garnier-Moiroux * @since 0.1.2 @@ -32,14 +32,12 @@ public interface OAuth2AuthorizationConsentService { /** * Saves the {@link OAuth2AuthorizationConsent}. - * * @param authorizationConsent the {@link OAuth2AuthorizationConsent} */ void save(OAuth2AuthorizationConsent authorizationConsent); /** * Removes the {@link OAuth2AuthorizationConsent}. - * * @param authorizationConsent the {@link OAuth2AuthorizationConsent} */ void remove(OAuth2AuthorizationConsent authorizationConsent); @@ -47,7 +45,6 @@ public interface OAuth2AuthorizationConsentService { /** * Returns the {@link OAuth2AuthorizationConsent} identified by the provided * {@code registeredClientId} and {@code principalName}, or {@code null} if not found. - * * @param registeredClientId the identifier for the {@link RegisteredClient} * @param principalName the name of the {@link Principal} * @return the {@link OAuth2AuthorizationConsent} if found, otherwise {@code null} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java index 210a3ac9..e62740a3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadata.java @@ -20,16 +20,16 @@ import java.util.Map; import org.springframework.util.Assert; /** - * A representation of an OAuth 2.0 Authorization Server Metadata response, - * which is returned from an OAuth 2.0 Authorization Server's Metadata Endpoint, - * and contains a set of claims about the Authorization Server's configuration. - * The claims are defined by the OAuth 2.0 Authorization Server Metadata - * specification (RFC 8414). + * A representation of an OAuth 2.0 Authorization Server Metadata response, which is + * returned from an OAuth 2.0 Authorization Server's Metadata Endpoint, and contains a set + * of claims about the Authorization Server's configuration. The claims are defined by the + * OAuth 2.0 Authorization Server Metadata specification (RFC 8414). * * @author Daniel Garnier-Moiroux * @since 0.1.1 * @see AbstractOAuth2AuthorizationServerMetadata - * @see 3.2. Authorization Server Metadata Response + * @see 3.2. + * Authorization Server Metadata Response */ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2AuthorizationServerMetadata { @@ -39,7 +39,6 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho /** * Constructs a new {@link Builder} with empty claims. - * * @return the {@link Builder} */ public static Builder builder() { @@ -48,14 +47,12 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho /** * Constructs a new {@link Builder} with the provided claims. - * * @param claims the claims to initialize the builder * @return the {@link Builder} */ public static Builder withClaims(Map claims) { Assert.notEmpty(claims, "claims cannot be empty"); - return new Builder() - .claims(c -> c.putAll(claims)); + return new Builder().claims(c -> c.putAll(claims)); } /** @@ -69,10 +66,9 @@ public final class OAuth2AuthorizationServerMetadata extends AbstractOAuth2Autho /** * Validate the claims and build the {@link OAuth2AuthorizationServerMetadata}. *

- * The following claims are REQUIRED: - * {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint} - * and {@code response_types_supported}. - * + * The following claims are REQUIRED: {@code issuer}, + * {@code authorization_endpoint}, {@code token_endpoint} and + * {@code response_types_supported}. * @return the {@link OAuth2AuthorizationServerMetadata} */ @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java index 089944d1..63f21a53 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimAccessor.java @@ -21,22 +21,27 @@ import java.util.List; import org.springframework.security.oauth2.core.ClaimAccessor; /** - * A {@link ClaimAccessor} for the "claims" an Authorization Server describes about its configuration, - * used in OAuth 2.0 Authorization Server Metadata and OpenID Connect Discovery 1.0. + * A {@link ClaimAccessor} for the "claims" an Authorization Server describes about its + * configuration, used in OAuth 2.0 Authorization Server Metadata and OpenID Connect + * Discovery 1.0. * * @author Daniel Garnier-Moiroux * @since 0.1.1 * @see ClaimAccessor * @see OAuth2AuthorizationServerMetadataClaimNames - * @see 2. Authorization Server Metadata - * @see 3. OpenID Provider Metadata - * @see 4. Device Authorization Grant Metadata + * @see 2. + * Authorization Server Metadata + * @see 3. OpenID + * Provider Metadata + * @see 4. + * Device Authorization Grant Metadata */ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAccessor { /** - * Returns the {@code URL} the Authorization Server asserts as its Issuer Identifier {@code (issuer)}. - * + * Returns the {@code URL} the Authorization Server asserts as its Issuer Identifier + * {@code (issuer)}. * @return the {@code URL} the Authorization Server asserts as its Issuer Identifier */ default URL getIssuer() { @@ -44,8 +49,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the {@code 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 {@code URL} of the OAuth 2.0 Authorization Endpoint */ default URL getAuthorizationEndpoint() { @@ -53,8 +58,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint {@code (device_authorization_endpoint)}. - * + * Returns the {@code URL} of the OAuth 2.0 Device Authorization Endpoint + * {@code (device_authorization_endpoint)}. * @return the {@code URL} of the OAuth 2.0 Device Authorization Endpoint * @since 1.1 */ @@ -64,7 +69,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc /** * Returns the {@code URL} of the OAuth 2.0 Token Endpoint {@code (token_endpoint)}. - * * @return the {@code URL} of the OAuth 2.0 Token Endpoint */ default URL getTokenEndpoint() { @@ -72,8 +76,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the client authentication methods supported by the OAuth 2.0 Token Endpoint {@code (token_endpoint_auth_methods_supported)}. - * + * Returns the client authentication methods supported by the OAuth 2.0 Token Endpoint + * {@code (token_endpoint_auth_methods_supported)}. * @return the client authentication methods supported by the OAuth 2.0 Token Endpoint */ default List getTokenEndpointAuthenticationMethods() { @@ -82,7 +86,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc /** * Returns the {@code URL} of the JSON Web Key Set {@code (jwks_uri)}. - * * @return the {@code URL} of the JSON Web Key Set */ default URL getJwkSetUrl() { @@ -91,7 +94,6 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc /** * Returns the OAuth 2.0 {@code scope} values supported {@code (scopes_supported)}. - * * @return the OAuth 2.0 {@code scope} values supported */ default List getScopes() { @@ -99,8 +101,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the OAuth 2.0 {@code response_type} values supported {@code (response_types_supported)}. - * + * Returns the OAuth 2.0 {@code response_type} values supported + * {@code (response_types_supported)}. * @return the OAuth 2.0 {@code response_type} values supported */ default List getResponseTypes() { @@ -108,8 +110,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the OAuth 2.0 {@code grant_type} values supported {@code (grant_types_supported)}. - * + * Returns the OAuth 2.0 {@code grant_type} values supported + * {@code (grant_types_supported)}. * @return the OAuth 2.0 {@code grant_type} values supported */ default List getGrantTypes() { @@ -117,8 +119,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the {@code URL} of the OAuth 2.0 Token Revocation Endpoint {@code (revocation_endpoint)}. - * + * Returns the {@code URL} of the OAuth 2.0 Token Revocation Endpoint + * {@code (revocation_endpoint)}. * @return the {@code URL} of the OAuth 2.0 Token Revocation Endpoint */ default URL getTokenRevocationEndpoint() { @@ -126,17 +128,19 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint {@code (revocation_endpoint_auth_methods_supported)}. - * - * @return the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint + * Returns the client authentication methods supported by the OAuth 2.0 Token + * Revocation Endpoint {@code (revocation_endpoint_auth_methods_supported)}. + * @return the client authentication methods supported by the OAuth 2.0 Token + * Revocation Endpoint */ default List getTokenRevocationEndpointAuthenticationMethods() { - return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED); + return getClaimAsStringList( + OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED); } /** - * Returns the {@code URL} of the OAuth 2.0 Token Introspection Endpoint {@code (introspection_endpoint)}. - * + * Returns the {@code URL} of the OAuth 2.0 Token Introspection Endpoint + * {@code (introspection_endpoint)}. * @return the {@code URL} of the OAuth 2.0 Token Introspection Endpoint */ default URL getTokenIntrospectionEndpoint() { @@ -144,17 +148,19 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint {@code (introspection_endpoint_auth_methods_supported)}. - * - * @return the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint + * Returns the client authentication methods supported by the OAuth 2.0 Token + * Introspection Endpoint {@code (introspection_endpoint_auth_methods_supported)}. + * @return the client authentication methods supported by the OAuth 2.0 Token + * Introspection Endpoint */ default List getTokenIntrospectionEndpointAuthenticationMethods() { - return getClaimAsStringList(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED); + return getClaimAsStringList( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED); } /** - * Returns the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint {@code (registration_endpoint)}. - * + * Returns the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint + * {@code (registration_endpoint)}. * @return the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint * @since 0.4.0 */ @@ -163,8 +169,8 @@ public interface OAuth2AuthorizationServerMetadataClaimAccessor extends ClaimAcc } /** - * Returns the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported {@code (code_challenge_methods_supported)}. - * + * Returns the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values + * supported {@code (code_challenge_methods_supported)}. * @return the {@code code_challenge_method} values supported */ default List getCodeChallengeMethods() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java index 831e0ec7..fe7d9107 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataClaimNames.java @@ -21,24 +21,31 @@ package org.springframework.security.oauth2.server.authorization; * * @author Daniel Garnier-Moiroux * @since 0.1.1 - * @see 2. Authorization Server Metadata - * @see 3. OpenID Provider Metadata - * @see 4. Device Authorization Grant Metadata + * @see 2. + * Authorization Server Metadata + * @see 3. OpenID + * Provider Metadata + * @see 4. + * Device Authorization Grant Metadata */ public class OAuth2AuthorizationServerMetadataClaimNames { /** - * {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer Identifier + * {@code issuer} - the {@code URL} the Authorization Server asserts as its Issuer + * Identifier */ public static final String ISSUER = "issuer"; /** - * {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization Endpoint + * {@code authorization_endpoint} - the {@code URL} of the OAuth 2.0 Authorization + * Endpoint */ public static final String AUTHORIZATION_ENDPOINT = "authorization_endpoint"; /** - * {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device Authorization Endpoint + * {@code device_authorization_endpoint} - the {@code URL} of the OAuth 2.0 Device + * Authorization Endpoint * @since 1.1 */ public static final String DEVICE_AUTHORIZATION_ENDPOINT = "device_authorization_endpoint"; @@ -49,7 +56,8 @@ public class OAuth2AuthorizationServerMetadataClaimNames { public static final String TOKEN_ENDPOINT = "token_endpoint"; /** - * {@code token_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Endpoint + * {@code token_endpoint_auth_methods_supported} - the client authentication methods + * supported by the OAuth 2.0 Token Endpoint */ public static final String TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED = "token_endpoint_auth_methods_supported"; @@ -64,7 +72,8 @@ public class OAuth2AuthorizationServerMetadataClaimNames { public static final String SCOPES_SUPPORTED = "scopes_supported"; /** - * {@code response_types_supported} - the OAuth 2.0 {@code response_type} values supported + * {@code response_types_supported} - the OAuth 2.0 {@code response_type} values + * supported */ public static final String RESPONSE_TYPES_SUPPORTED = "response_types_supported"; @@ -74,33 +83,39 @@ public class OAuth2AuthorizationServerMetadataClaimNames { public static final String GRANT_TYPES_SUPPORTED = "grant_types_supported"; /** - * {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation Endpoint + * {@code revocation_endpoint} - the {@code URL} of the OAuth 2.0 Token Revocation + * Endpoint */ public static final String REVOCATION_ENDPOINT = "revocation_endpoint"; /** - * {@code revocation_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Revocation Endpoint + * {@code revocation_endpoint_auth_methods_supported} - the client authentication + * methods supported by the OAuth 2.0 Token Revocation Endpoint */ public static final String REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED = "revocation_endpoint_auth_methods_supported"; /** - * {@code introspection_endpoint} - the {@code URL} of the OAuth 2.0 Token Introspection Endpoint + * {@code introspection_endpoint} - the {@code URL} of the OAuth 2.0 Token + * Introspection Endpoint */ public static final String INTROSPECTION_ENDPOINT = "introspection_endpoint"; /** - * {@code introspection_endpoint_auth_methods_supported} - the client authentication methods supported by the OAuth 2.0 Token Introspection Endpoint + * {@code introspection_endpoint_auth_methods_supported} - the client authentication + * methods supported by the OAuth 2.0 Token Introspection Endpoint */ public static final String INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED = "introspection_endpoint_auth_methods_supported"; /** - * {@code registration_endpoint} - the {@code URL} of the OAuth 2.0 Dynamic Client Registration Endpoint + * {@code registration_endpoint} - the {@code URL} of the OAuth 2.0 Dynamic Client + * Registration Endpoint * @since 0.4.0 */ public static final String REGISTRATION_ENDPOINT = "registration_endpoint"; /** - * {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE) {@code code_challenge_method} values supported + * {@code code_challenge_methods_supported} - the Proof Key for Code Exchange (PKCE) + * {@code code_challenge_method} values supported */ public static final String CODE_CHALLENGE_METHODS_SUPPORTED = "code_challenge_methods_supported"; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java index 9e4e5553..6e5ed530 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationService.java @@ -18,8 +18,8 @@ package org.springframework.security.oauth2.server.authorization; import org.springframework.lang.Nullable; /** - * Implementations of this interface are responsible for the management - * of {@link OAuth2Authorization OAuth 2.0 Authorization(s)}. + * Implementations of this interface are responsible for the management of + * {@link OAuth2Authorization OAuth 2.0 Authorization(s)}. * * @author Joe Grandja * @since 0.0.1 @@ -30,22 +30,19 @@ public interface OAuth2AuthorizationService { /** * Saves the {@link OAuth2Authorization}. - * * @param authorization the {@link OAuth2Authorization} */ void save(OAuth2Authorization authorization); /** * Removes the {@link OAuth2Authorization}. - * * @param authorization the {@link OAuth2Authorization} */ void remove(OAuth2Authorization authorization); /** - * Returns the {@link OAuth2Authorization} identified by the provided {@code id}, - * or {@code null} if not found. - * + * Returns the {@link OAuth2Authorization} identified by the provided {@code id}, or + * {@code null} if not found. * @param id the authorization identifier * @return the {@link OAuth2Authorization} if found, otherwise {@code null} */ @@ -53,9 +50,8 @@ public interface OAuth2AuthorizationService { OAuth2Authorization findById(String id); /** - * Returns the {@link OAuth2Authorization} containing the provided {@code token}, - * or {@code null} if not found. - * + * Returns the {@link OAuth2Authorization} containing the provided {@code token}, or + * {@code null} if not found. * @param token the token credential * @param tokenType the {@link OAuth2TokenType token type} * @return the {@link OAuth2Authorization} if found, otherwise {@code null} diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenIntrospection.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenIntrospection.java index 62b337a3..f2652c3b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenIntrospection.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenIntrospection.java @@ -39,10 +39,13 @@ import org.springframework.util.Assert; * @author Joe Grandja * @since 0.1.1 * @see OAuth2TokenIntrospectionClaimAccessor - * @see Section 2.2 Introspection Response + * @see Section + * 2.2 Introspection Response */ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionClaimAccessor, Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Map claims; private OAuth2TokenIntrospection(Map claims) { @@ -51,7 +54,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Returns the claims in the Token Introspection Response. - * * @return a {@code Map} of the claims */ @Override @@ -60,8 +62,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Constructs a new {@link Builder} initialized with the {@link #isActive() active} claim to {@code false}. - * + * Constructs a new {@link Builder} initialized with the {@link #isActive() active} + * claim to {@code false}. * @return the {@link Builder} */ public static Builder builder() { @@ -69,9 +71,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Constructs a new {@link Builder} initialized with the provided {@link #isActive() active} claim. - * - * @param active {@code true} if the token is currently active, {@code false} otherwise + * Constructs a new {@link Builder} initialized with the provided {@link #isActive() + * active} claim. + * @param active {@code true} if the token is currently active, {@code false} + * otherwise * @return the {@link Builder} */ public static Builder builder(boolean active) { @@ -80,7 +83,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Constructs a new {@link Builder} initialized with the provided claims. - * * @param claims the claims to initialize the builder * @return the {@link Builder} */ @@ -93,6 +95,7 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC * A builder for {@link OAuth2TokenIntrospection}. */ public static class Builder { + private final Map claims = new LinkedHashMap<>(); private Builder(boolean active) { @@ -100,9 +103,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Sets the indicator of whether or not the presented token is currently active, REQUIRED. - * - * @param active {@code true} if the token is currently active, {@code false} otherwise + * Sets the indicator of whether or not the presented token is currently active, + * REQUIRED. + * @param active {@code true} if the token is currently active, {@code false} + * otherwise * @return the {@link Builder} for further configuration */ public Builder active(boolean active) { @@ -111,7 +115,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Add the scope associated with this token, OPTIONAL. - * * @param scope the scope associated with this token * @return the {@link Builder} for further configuration */ @@ -121,10 +124,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * A {@code Consumer} of the scope(s) associated with this token, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param scopesConsumer a {@code Consumer} of the scope(s) associated with this token + * A {@code Consumer} of the scope(s) associated with this token, allowing the + * ability to add, replace, or remove, OPTIONAL. + * @param scopesConsumer a {@code Consumer} of the scope(s) associated with this + * token * @return the {@link Builder} for further configuration */ public Builder scopes(Consumer> scopesConsumer) { @@ -133,9 +136,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Sets the client identifier for the OAuth 2.0 client that requested this token, OPTIONAL. - * - * @param clientId the client identifier for the OAuth 2.0 client that requested this token + * Sets the client identifier for the OAuth 2.0 client that requested this token, + * OPTIONAL. + * @param clientId the client identifier for the OAuth 2.0 client that requested + * this token * @return the {@link Builder} for further configuration */ public Builder clientId(String clientId) { @@ -143,9 +147,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Sets the human-readable identifier for the resource owner who authorized this token, OPTIONAL. - * - * @param username the human-readable identifier for the resource owner who authorized this token + * Sets the human-readable identifier for the resource owner who authorized this + * token, OPTIONAL. + * @param username the human-readable identifier for the resource owner who + * authorized this token * @return the {@link Builder} for further configuration */ public Builder username(String username) { @@ -154,7 +159,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the token type (e.g. bearer), OPTIONAL. - * * @param tokenType the token type * @return the {@link Builder} for further configuration */ @@ -164,7 +168,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the time indicating when this token will expire, OPTIONAL. - * * @param expiresAt the time indicating when this token will expire * @return the {@link Builder} for further configuration */ @@ -174,7 +177,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the time indicating when this token was originally issued, OPTIONAL. - * * @param issuedAt the time indicating when this token was originally issued * @return the {@link Builder} for further configuration */ @@ -184,7 +186,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the time indicating when this token is not to be used before, OPTIONAL. - * * @param notBefore the time indicating when this token is not to be used before * @return the {@link Builder} for further configuration */ @@ -193,9 +194,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * Sets the subject of the token, usually a machine-readable identifier - * of the resource owner who authorized this token, OPTIONAL. - * + * Sets the subject of the token, usually a machine-readable identifier of the + * resource owner who authorized this token, OPTIONAL. * @param subject the subject of the token * @return the {@link Builder} for further configuration */ @@ -205,8 +205,8 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Add the identifier representing the intended audience for this token, OPTIONAL. - * - * @param audience the identifier representing the intended audience for this token + * @param audience the identifier representing the intended audience for this + * token * @return the {@link Builder} for further configuration */ public Builder audience(String audience) { @@ -215,10 +215,10 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC } /** - * A {@code Consumer} of the intended audience(s) for this token, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param audiencesConsumer a {@code Consumer} of the intended audience(s) for this token + * A {@code Consumer} of the intended audience(s) for this token, allowing the + * ability to add, replace, or remove, OPTIONAL. + * @param audiencesConsumer a {@code Consumer} of the intended audience(s) for + * this token * @return the {@link Builder} for further configuration */ public Builder audiences(Consumer> audiencesConsumer) { @@ -228,7 +228,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the issuer of this token, OPTIONAL. - * * @param issuer the issuer of this token * @return the {@link Builder} for further configuration */ @@ -238,7 +237,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the identifier for the token, OPTIONAL. - * * @param jti the identifier for the token * @return the {@link Builder} for further configuration */ @@ -248,7 +246,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Sets the claim. - * * @param name the claim name * @param value the claim value * @return the {@link Builder} for further configuration @@ -263,7 +260,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC /** * Provides access to every {@link #claim(String, Object)} declared so far with * the possibility to add, replace, or remove. - * * @param claimsConsumer a {@code Consumer} of the claims * @return the {@link Builder} for further configurations */ @@ -276,7 +272,6 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC * Validate the claims and build the {@link OAuth2TokenIntrospection}. *

* The following claims are REQUIRED: {@code active} - * * @return the {@link OAuth2TokenIntrospection} */ public OAuth2TokenIntrospection build() { @@ -286,21 +281,27 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC private void validate() { Assert.notNull(this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), "active cannot be null"); - Assert.isInstanceOf(Boolean.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), "active must be of type boolean"); + Assert.isInstanceOf(Boolean.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.ACTIVE), + "active must be of type boolean"); if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.SCOPE)) { - Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE), "scope must be of type List"); + Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE), + "scope must be of type List"); } if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.EXP)) { - Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.EXP), "exp must be of type Instant"); + Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.EXP), + "exp must be of type Instant"); } if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.IAT)) { - Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.IAT), "iat must be of type Instant"); + Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.IAT), + "iat must be of type Instant"); } if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.NBF)) { - Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.NBF), "nbf must be of type Instant"); + Assert.isInstanceOf(Instant.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.NBF), + "nbf must be of type Instant"); } if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.AUD)) { - Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.AUD), "aud must be of type List"); + Assert.isInstanceOf(List.class, this.claims.get(OAuth2TokenIntrospectionClaimNames.AUD), + "aud must be of type List"); } if (this.claims.containsKey(OAuth2TokenIntrospectionClaimNames.ISS)) { validateURL(this.claims.get(OAuth2TokenIntrospectionClaimNames.ISS), "iss must be a valid URL"); @@ -331,9 +332,12 @@ public final class OAuth2TokenIntrospection implements OAuth2TokenIntrospectionC try { new URI(url.toString()).toURL(); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(errorMessage, ex); } } + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java index 8c25c863..63735785 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/OAuth2TokenType.java @@ -25,17 +25,21 @@ import org.springframework.util.Assert; * * @author Joe Grandja * @since 0.0.1 - * @see 4.1.2 OAuth Token Type Hints Registry + * @see 4.1.2 + * OAuth Token Type Hints Registry */ public final class OAuth2TokenType implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + public static final OAuth2TokenType ACCESS_TOKEN = new OAuth2TokenType("access_token"); + public static final OAuth2TokenType REFRESH_TOKEN = new OAuth2TokenType("refresh_token"); + private final String value; /** * Constructs an {@code OAuth2TokenType} using the provided value. - * * @param value the value of the token type */ public OAuth2TokenType(String value) { @@ -45,7 +49,6 @@ public final class OAuth2TokenType implements Serializable { /** * Returns the value of the token type. - * * @return the value of the token type */ public String getValue() { @@ -68,4 +71,5 @@ public final class OAuth2TokenType implements Serializable { public int hashCode() { return getValue().hashCode(); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProvider.java index 50f0afb8..8d985d57 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProvider.java @@ -37,8 +37,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import org.springframework.util.Assert; /** - * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client Authentication, - * which authenticates the {@link OAuth2ParameterNames#CLIENT_SECRET client_secret} parameter. + * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client + * Authentication, which authenticates the {@link OAuth2ParameterNames#CLIENT_SECRET + * client_secret} parameter. * * @author Patryk Kostrzewa * @author Joe Grandja @@ -50,15 +51,20 @@ import org.springframework.util.Assert; * @see PasswordEncoder */ public final class ClientSecretAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1"; + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final CodeVerifierAuthenticator codeVerifierAuthenticator; + private PasswordEncoder passwordEncoder; /** - * Constructs a {@code ClientSecretAuthenticationProvider} using the provided parameters. - * + * Constructs a {@code ClientSecretAuthenticationProvider} using the provided + * parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service */ @@ -72,12 +78,12 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP } /** - * Sets the {@link PasswordEncoder} used to validate - * the {@link RegisteredClient#getClientSecret() client secret}. - * If not set, the client secret will be compared using + * Sets the {@link PasswordEncoder} used to validate the + * {@link RegisteredClient#getClientSecret() client secret}. If not set, the client + * secret will be compared using * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}. - * - * @param passwordEncoder the {@link PasswordEncoder} used to validate the client secret + * @param passwordEncoder the {@link PasswordEncoder} used to validate the client + * secret */ public void setPasswordEncoder(PasswordEncoder passwordEncoder) { Assert.notNull(passwordEncoder, "passwordEncoder cannot be null"); @@ -86,13 +92,14 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2ClientAuthenticationToken clientAuthentication = - (OAuth2ClientAuthenticationToken) authentication; + OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication; + // @formatter:off if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC.equals(clientAuthentication.getClientAuthenticationMethod()) && !ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientAuthentication.getClientAuthenticationMethod())) { return null; } + // @formatter:on String clientId = clientAuthentication.getPrincipal().toString(); RegisteredClient registeredClient = this.registeredClientRepository.findByClientId(clientId); @@ -104,8 +111,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP this.logger.trace("Retrieved registered client"); } - if (!registeredClient.getClientAuthenticationMethods().contains( - clientAuthentication.getClientAuthenticationMethod())) { + if (!registeredClient.getClientAuthenticationMethods() + .contains(clientAuthentication.getClientAuthenticationMethod())) { throwInvalidClient("authentication_method"); } @@ -122,15 +129,15 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP throwInvalidClient(OAuth2ParameterNames.CLIENT_SECRET); } - if (registeredClient.getClientSecretExpiresAt() != null && - Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) { + if (registeredClient.getClientSecretExpiresAt() != null + && Instant.now().isAfter(registeredClient.getClientSecretExpiresAt())) { throwInvalidClient("client_secret_expires_at"); } if (this.passwordEncoder.upgradeEncoding(registeredClient.getClientSecret())) { registeredClient = RegisteredClient.from(registeredClient) - .clientSecret(this.passwordEncoder.encode(clientSecret)) - .build(); + .clientSecret(this.passwordEncoder.encode(clientSecret)) + .build(); this.registeredClientRepository.save(registeredClient); } @@ -138,7 +145,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP this.logger.trace("Validated client authentication parameters"); } - // Validate the "code_verifier" parameter for the confidential client, if available + // Validate the "code_verifier" parameter for the confidential client, if + // available this.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient); if (this.logger.isTraceEnabled()) { @@ -155,11 +163,8 @@ public final class ClientSecretAuthenticationProvider implements AuthenticationP } private static void throwInvalidClient(String parameterName) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_CLIENT, - "Client authentication failed: " + parameterName, - ERROR_URI - ); + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, + "Client authentication failed: " + parameterName, ERROR_URI); throw new OAuth2AuthenticationException(error); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/CodeVerifierAuthenticator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/CodeVerifierAuthenticator.java index d3d9f3db..eceaa302 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/CodeVerifierAuthenticator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/CodeVerifierAuthenticator.java @@ -40,8 +40,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * An authenticator used for OAuth 2.0 Client Authentication, - * which authenticates the {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter. + * An authenticator used for OAuth 2.0 Client Authentication, which authenticates the + * {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter. * * @author Daniel Garnier-Moiroux * @author Joe Grandja @@ -50,8 +50,11 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationService */ final class CodeVerifierAuthenticator { + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; CodeVerifierAuthenticator(OAuth2AuthorizationService authorizationService) { @@ -59,8 +62,7 @@ final class CodeVerifierAuthenticator { this.authorizationService = authorizationService; } - void authenticateRequired(OAuth2ClientAuthenticationToken clientAuthentication, - RegisteredClient registeredClient) { + void authenticateRequired(OAuth2ClientAuthenticationToken clientAuthentication, RegisteredClient registeredClient) { if (!authenticate(clientAuthentication, registeredClient)) { throwInvalidGrant(PkceParameterNames.CODE_VERIFIER); } @@ -79,9 +81,8 @@ final class CodeVerifierAuthenticator { return false; } - OAuth2Authorization authorization = this.authorizationService.findByToken( - (String) parameters.get(OAuth2ParameterNames.CODE), - AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken((String) parameters.get(OAuth2ParameterNames.CODE), AUTHORIZATION_CODE_TOKEN_TYPE); if (authorization == null) { throwInvalidGrant(OAuth2ParameterNames.CODE); } @@ -90,11 +91,11 @@ final class CodeVerifierAuthenticator { this.logger.trace("Retrieved authorization with authorization code"); } - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); String codeChallenge = (String) authorizationRequest.getAdditionalParameters() - .get(PkceParameterNames.CODE_CHALLENGE); + .get(PkceParameterNames.CODE_CHALLENGE); String codeVerifier = (String) parameters.get(PkceParameterNames.CODE_VERIFIER); if (!StringUtils.hasText(codeChallenge)) { if (registeredClient.getClientSettings().isRequireProofKey() || @@ -104,7 +105,8 @@ final class CodeVerifierAuthenticator { " for registered client '%s'", registeredClient.getId())); } throwInvalidGrant(PkceParameterNames.CODE_CHALLENGE); - } else { + } + else { if (this.logger.isTraceEnabled()) { this.logger.trace("Did not authenticate code verifier since requireProofKey=false"); } @@ -117,7 +119,7 @@ final class CodeVerifierAuthenticator { } String codeChallengeMethod = (String) authorizationRequest.getAdditionalParameters() - .get(PkceParameterNames.CODE_CHALLENGE_METHOD); + .get(PkceParameterNames.CODE_CHALLENGE_METHOD); if (!codeVerifierValid(codeVerifier, codeChallenge, codeChallengeMethod)) { if (this.logger.isDebugEnabled()) { this.logger.debug(LogMessage.format("Invalid request: code_verifier is missing or invalid" + @@ -134,22 +136,27 @@ final class CodeVerifierAuthenticator { } private static boolean authorizationCodeGrant(Map parameters) { + // @formatter:off return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals( parameters.get(OAuth2ParameterNames.GRANT_TYPE)) && parameters.get(OAuth2ParameterNames.CODE) != null; + // @formatter:on } private boolean codeVerifierValid(String codeVerifier, String codeChallenge, String codeChallengeMethod) { if (!StringUtils.hasText(codeVerifier)) { return false; - } else if ("S256".equals(codeChallengeMethod)) { + } + else if ("S256".equals(codeChallengeMethod)) { try { MessageDigest md = MessageDigest.getInstance("SHA-256"); byte[] digest = md.digest(codeVerifier.getBytes(StandardCharsets.US_ASCII)); String encodedVerifier = Base64.getUrlEncoder().withoutPadding().encodeToString(digest); return encodedVerifier.equals(codeChallenge); - } catch (NoSuchAlgorithmException ex) { - // It is unlikely that SHA-256 is not available on the server. If it is not available, + } + catch (NoSuchAlgorithmException ex) { + // It is unlikely that SHA-256 is not available on the server. If it is + // not available, // there will likely be bigger issues as well. We default to SERVER_ERROR. throw new OAuth2AuthenticationException(OAuth2ErrorCodes.SERVER_ERROR); } @@ -158,11 +165,8 @@ final class CodeVerifierAuthenticator { } private static void throwInvalidGrant(String parameterName) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_GRANT, - "Client authentication failed: " + parameterName, - null - ); + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_GRANT, + "Client authentication failed: " + parameterName, null); throw new OAuth2AuthenticationException(error); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java index 2adc8c98..9c5c02b2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProvider.java @@ -37,8 +37,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import org.springframework.util.Assert; /** - * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client Authentication, - * which authenticates the {@link Jwt} {@link OAuth2ParameterNames#CLIENT_ASSERTION client_assertion} parameter. + * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Client + * Authentication, which authenticates the {@link Jwt} + * {@link OAuth2ParameterNames#CLIENT_ASSERTION client_assertion} parameter. * * @author Rafal Lewczuk * @author Joe Grandja @@ -50,17 +51,23 @@ import org.springframework.util.Assert; * @see JwtClientAssertionDecoderFactory */ public final class JwtClientAssertionAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1"; - private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = - new ClientAuthenticationMethod("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + + private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod( + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final CodeVerifierAuthenticator codeVerifierAuthenticator; + private JwtDecoderFactory jwtDecoderFactory; /** - * Constructs a {@code JwtClientAssertionAuthenticationProvider} using the provided parameters. - * + * Constructs a {@code JwtClientAssertionAuthenticationProvider} using the provided + * parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service */ @@ -75,8 +82,7 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2ClientAuthenticationToken clientAuthentication = - (OAuth2ClientAuthenticationToken) authentication; + OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication; if (!JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD.equals(clientAuthentication.getClientAuthenticationMethod())) { return null; @@ -92,10 +98,12 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic this.logger.trace("Retrieved registered client"); } + // @formatter:off if (!registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.PRIVATE_KEY_JWT) && !registeredClient.getClientAuthenticationMethods().contains(ClientAuthenticationMethod.CLIENT_SECRET_JWT)) { throwInvalidClient("authentication_method"); } + // @formatter:on if (clientAuthentication.getCredentials() == null) { throwInvalidClient("credentials"); @@ -105,7 +113,8 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic JwtDecoder jwtDecoder = this.jwtDecoderFactory.createDecoder(registeredClient); try { jwtAssertion = jwtDecoder.decode(clientAuthentication.getCredentials().toString()); - } catch (JwtException ex) { + } + catch (JwtException ex) { throwInvalidClient(OAuth2ParameterNames.CLIENT_ASSERTION, ex); } @@ -113,13 +122,16 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic this.logger.trace("Validated client authentication parameters"); } - // Validate the "code_verifier" parameter for the confidential client, if available + // Validate the "code_verifier" parameter for the confidential client, if + // available this.codeVerifierAuthenticator.authenticateIfAvailable(clientAuthentication, registeredClient); + // @formatter:off ClientAuthenticationMethod clientAuthenticationMethod = registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm() instanceof SignatureAlgorithm ? ClientAuthenticationMethod.PRIVATE_KEY_JWT : ClientAuthenticationMethod.CLIENT_SECRET_JWT; + // @formatter:on if (this.logger.isTraceEnabled()) { this.logger.trace("Authenticated client assertion"); @@ -134,11 +146,12 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic } /** - * Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient} - * and is used for authenticating a {@link Jwt} Bearer Token during OAuth 2.0 Client Authentication. - * The default factory is {@link JwtClientAssertionDecoderFactory}. - * - * @param jwtDecoderFactory the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient} + * Sets the {@link JwtDecoderFactory} that provides a {@link JwtDecoder} for the + * specified {@link RegisteredClient} and is used for authenticating a {@link Jwt} + * Bearer Token during OAuth 2.0 Client Authentication. The default factory is + * {@link JwtClientAssertionDecoderFactory}. + * @param jwtDecoderFactory the {@link JwtDecoderFactory} that provides a + * {@link JwtDecoder} for the specified {@link RegisteredClient} * @since 0.4.0 */ public void setJwtDecoderFactory(JwtDecoderFactory jwtDecoderFactory) { @@ -151,11 +164,8 @@ public final class JwtClientAssertionAuthenticationProvider implements Authentic } private static void throwInvalidClient(String parameterName, Throwable cause) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_CLIENT, - "Client authentication failed: " + parameterName, - ERROR_URI - ); + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, + "Client authentication failed: " + parameterName, ERROR_URI); throw new OAuth2AuthenticationException(error, error.toString(), cause); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactory.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactory.java index 3e49e7f4..c2e18ad6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactory.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactory.java @@ -56,8 +56,9 @@ import org.springframework.web.client.RestTemplate; import org.springframework.web.util.UriComponentsBuilder; /** - * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the specified {@link RegisteredClient} - * and is used for authenticating a {@link Jwt} Bearer Token during OAuth 2.0 Client Authentication. + * A {@link JwtDecoderFactory factory} that provides a {@link JwtDecoder} for the + * specified {@link RegisteredClient} and is used for authenticating a {@link Jwt} Bearer + * Token during OAuth 2.0 Client Authentication. * * @author Rafal Lewczuk * @author Joe Grandja @@ -72,13 +73,16 @@ import org.springframework.web.util.UriComponentsBuilder; public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory { /** - * The default {@code OAuth2TokenValidator} factory that validates the {@link JwtClaimNames#ISS iss}, - * {@link JwtClaimNames#SUB sub}, {@link JwtClaimNames#AUD aud}, {@link JwtClaimNames#EXP exp} and - * {@link JwtClaimNames#NBF nbf} claims of the {@link Jwt} for the specified {@link RegisteredClient}. + * The default {@code OAuth2TokenValidator} factory that validates the + * {@link JwtClaimNames#ISS iss}, {@link JwtClaimNames#SUB sub}, + * {@link JwtClaimNames#AUD aud}, {@link JwtClaimNames#EXP exp} and + * {@link JwtClaimNames#NBF nbf} claims of the {@link Jwt} for the specified + * {@link RegisteredClient}. */ public static final Function> DEFAULT_JWT_VALIDATOR_FACTORY = defaultJwtValidatorFactory(); private static final String JWT_CLIENT_AUTHENTICATION_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7523#section-3"; + private static final Map JCA_ALGORITHM_MAPPINGS; static { @@ -99,6 +103,7 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory } private final Map jwtDecoders = new ConcurrentHashMap<>(); + private Function> jwtValidatorFactory = DEFAULT_JWT_VALIDATOR_FACTORY; @Override @@ -112,11 +117,12 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory } /** - * Sets the factory that provides an {@link OAuth2TokenValidator} - * for the specified {@link RegisteredClient} and is used by the {@link JwtDecoder}. - * The default {@code OAuth2TokenValidator} factory is {@link #DEFAULT_JWT_VALIDATOR_FACTORY}. - * - * @param jwtValidatorFactory the factory that provides an {@link OAuth2TokenValidator} for the specified {@link RegisteredClient} + * Sets the factory that provides an {@link OAuth2TokenValidator} for the specified + * {@link RegisteredClient} and is used by the {@link JwtDecoder}. The default + * {@code OAuth2TokenValidator} factory is + * {@link #DEFAULT_JWT_VALIDATOR_FACTORY}. + * @param jwtValidatorFactory the factory that provides an + * {@link OAuth2TokenValidator} for the specified {@link RegisteredClient} */ public void setJwtValidatorFactory(Function> jwtValidatorFactory) { Assert.notNull(jwtValidatorFactory, "jwtValidatorFactory cannot be null"); @@ -124,26 +130,27 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory } private static NimbusJwtDecoder buildDecoder(RegisteredClient registeredClient) { - JwsAlgorithm jwsAlgorithm = registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm(); + JwsAlgorithm jwsAlgorithm = registeredClient.getClientSettings() + .getTokenEndpointAuthenticationSigningAlgorithm(); if (jwsAlgorithm instanceof SignatureAlgorithm) { String jwkSetUrl = registeredClient.getClientSettings().getJwkSetUrl(); if (!StringUtils.hasText(jwkSetUrl)) { OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, - "Failed to find a Signature Verifier for Client: '" - + registeredClient.getId() + "Failed to find a Signature Verifier for Client: '" + registeredClient.getId() + "'. Check to ensure you have configured the JWK Set URL.", JWT_CLIENT_AUTHENTICATION_ERROR_URI); throw new OAuth2AuthenticationException(oauth2Error); } - return NimbusJwtDecoder.withJwkSetUri(jwkSetUrl).jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm) - .restOperations(restTemplate).build(); + return NimbusJwtDecoder.withJwkSetUri(jwkSetUrl) + .jwsAlgorithm((SignatureAlgorithm) jwsAlgorithm) + .restOperations(restTemplate) + .build(); } if (jwsAlgorithm instanceof MacAlgorithm) { String clientSecret = registeredClient.getClientSecret(); if (!StringUtils.hasText(clientSecret)) { OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, - "Failed to find a Signature Verifier for Client: '" - + registeredClient.getId() + "Failed to find a Signature Verifier for Client: '" + registeredClient.getId() + "'. Check to ensure you have configured the client secret.", JWT_CLIENT_AUTHENTICATION_ERROR_URI); throw new OAuth2AuthenticationException(oauth2Error); @@ -153,8 +160,7 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory return NimbusJwtDecoder.withSecretKey(secretKeySpec).macAlgorithm((MacAlgorithm) jwsAlgorithm).build(); } OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, - "Failed to find a Signature Verifier for Client: '" - + registeredClient.getId() + "Failed to find a Signature Verifier for Client: '" + registeredClient.getId() + "'. Check to ensure you have configured a valid JWS Algorithm: '" + jwsAlgorithm + "'.", JWT_CLIENT_AUTHENTICATION_ERROR_URI); throw new OAuth2AuthenticationException(oauth2Error); @@ -163,13 +169,10 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory private static Function> defaultJwtValidatorFactory() { return (registeredClient) -> { String clientId = registeredClient.getClientId(); - return new DelegatingOAuth2TokenValidator<>( - new JwtClaimValidator<>(JwtClaimNames.ISS, clientId::equals), + return new DelegatingOAuth2TokenValidator<>(new JwtClaimValidator<>(JwtClaimNames.ISS, clientId::equals), new JwtClaimValidator<>(JwtClaimNames.SUB, clientId::equals), new JwtClaimValidator<>(JwtClaimNames.AUD, containsAudience()), - new JwtClaimValidator<>(JwtClaimNames.EXP, Objects::nonNull), - new JwtTimestampValidator() - ); + new JwtClaimValidator<>(JwtClaimNames.EXP, Objects::nonNull), new JwtTimestampValidator()); }; } @@ -194,12 +197,15 @@ public final class JwtClientAssertionDecoderFactory implements JwtDecoderFactory return Collections.emptyList(); } - AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings(); + AuthorizationServerSettings authorizationServerSettings = authorizationServerContext + .getAuthorizationServerSettings(); List audience = new ArrayList<>(); audience.add(authorizationServerContext.getIssuer()); audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenEndpoint())); - audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenIntrospectionEndpoint())); - audience.add(asUrl(authorizationServerContext.getIssuer(), authorizationServerSettings.getTokenRevocationEndpoint())); + audience.add(asUrl(authorizationServerContext.getIssuer(), + authorizationServerSettings.getTokenIntrospectionEndpoint())); + audience.add(asUrl(authorizationServerContext.getIssuer(), + authorizationServerSettings.getTokenRevocationEndpoint())); return audience; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java index 6767c828..12797438 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationToken.java @@ -28,8 +28,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation used when issuing an - * OAuth 2.0 Access Token and (optional) Refresh Token. + * An {@link Authentication} implementation used when issuing an OAuth 2.0 Access Token + * and (optional) Refresh Token. * * @author Joe Grandja * @author Madhu Bhat @@ -41,28 +41,34 @@ import org.springframework.util.Assert; * @see OAuth2ClientAuthenticationToken */ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final RegisteredClient registeredClient; + private final Authentication clientPrincipal; + private final OAuth2AccessToken accessToken; + private final OAuth2RefreshToken refreshToken; + private final Map additionalParameters; /** - * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided + * parameters. * @param registeredClient the registered client * @param clientPrincipal the authenticated client principal * @param accessToken the access token */ - public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, - Authentication clientPrincipal, OAuth2AccessToken accessToken) { + public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal, + OAuth2AccessToken accessToken) { this(registeredClient, clientPrincipal, accessToken, null); } /** - * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided + * parameters. * @param registeredClient the registered client * @param clientPrincipal the authenticated client principal * @param accessToken the access token @@ -74,8 +80,8 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication } /** - * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AccessTokenAuthenticationToken} using the provided + * parameters. * @param registeredClient the registered client * @param clientPrincipal the authenticated client principal * @param accessToken the access token @@ -83,7 +89,8 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication * @param additionalParameters the additional parameters */ public OAuth2AccessTokenAuthenticationToken(RegisteredClient registeredClient, Authentication clientPrincipal, - OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken, Map additionalParameters) { + OAuth2AccessToken accessToken, @Nullable OAuth2RefreshToken refreshToken, + Map additionalParameters) { super(Collections.emptyList()); Assert.notNull(registeredClient, "registeredClient cannot be null"); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); @@ -108,7 +115,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication /** * Returns the {@link RegisteredClient registered client}. - * * @return the {@link RegisteredClient} */ public RegisteredClient getRegisteredClient() { @@ -117,7 +123,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication /** * Returns the {@link OAuth2AccessToken access token}. - * * @return the {@link OAuth2AccessToken} */ public OAuth2AccessToken getAccessToken() { @@ -126,7 +131,6 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication /** * Returns the {@link OAuth2RefreshToken refresh token}. - * * @return the {@link OAuth2RefreshToken} or {@code null} if not available */ @Nullable @@ -136,10 +140,10 @@ public class OAuth2AccessTokenAuthenticationToken extends AbstractAuthentication /** * Returns the additional parameters. - * * @return a {@code Map} of the additional parameters, may be empty */ public Map getAdditionalParameters() { return this.additionalParameters; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationContext.java index 58c3cbbe..12bc91d9 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationContext.java @@ -36,7 +36,6 @@ public interface OAuth2AuthenticationContext extends Context { /** * Returns the {@link Authentication} associated to the context. - * * @param the type of the {@code Authentication} * @return the {@link Authentication} */ @@ -53,6 +52,7 @@ public interface OAuth2AuthenticationContext extends Context { * @since 0.2.1 */ abstract class AbstractBuilder> { + private final Map context = new HashMap<>(); protected AbstractBuilder(Authentication authentication) { @@ -62,7 +62,6 @@ public interface OAuth2AuthenticationContext extends Context { /** * Associates an attribute. - * * @param key the key for the attribute * @param value the value of the attribute * @return the {@link AbstractBuilder} for further configuration @@ -75,9 +74,8 @@ public interface OAuth2AuthenticationContext extends Context { } /** - * A {@code Consumer} of the attributes {@code Map} - * allowing the ability to add, replace, or remove. - * + * A {@code Consumer} of the attributes {@code Map} allowing the ability to add, + * replace, or remove. * @param contextConsumer a {@link Consumer} of the attributes {@code Map} * @return the {@link AbstractBuilder} for further configuration */ @@ -102,7 +100,6 @@ public interface OAuth2AuthenticationContext extends Context { /** * Builds a new {@link OAuth2AuthenticationContext}. - * * @return the {@link OAuth2AuthenticationContext} */ public abstract T build(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationProviderUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationProviderUtils.java index 97bc402f..2802e133 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationProviderUtils.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthenticationProviderUtils.java @@ -46,8 +46,7 @@ final class OAuth2AuthenticationProviderUtils { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT); } - static OAuth2Authorization invalidate( - OAuth2Authorization authorization, T token) { + static OAuth2Authorization invalidate(OAuth2Authorization authorization, T token) { // @formatter:off OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization) @@ -74,4 +73,5 @@ final class OAuth2AuthenticationProviderUtils { return authorizationBuilder.build(); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java index 04751dd1..ea744f72 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProvider.java @@ -66,7 +66,8 @@ import org.springframework.util.StringUtils; import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient; /** - * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code Grant. + * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Code + * Grant. * * @author Joe Grandja * @author Daniel Garnier-Moiroux @@ -76,23 +77,32 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2AuthorizationCodeRequestAuthenticationProvider * @see OAuth2AuthorizationService * @see OAuth2TokenGenerator - * @see Section 4.1 Authorization Code Grant - * @see Section 4.1.3 Access Token Request + * @see Section 4.1 Authorization + * Code Grant + * @see Section 4.1.3 Access + * Token Request */ public final class OAuth2AuthorizationCodeAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; - private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = - new OAuth2TokenType(OAuth2ParameterNames.CODE); - private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = - new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; + private SessionRegistry sessionRegistry; /** - * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeAuthenticationProvider} using the + * provided parameters. * @param authorizationService the authorization service * @param tokenGenerator the token generator * @since 0.2.3 @@ -107,19 +117,18 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = - (OAuth2AuthorizationCodeAuthenticationToken) authentication; + OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = (OAuth2AuthorizationCodeAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(authorizationCodeAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + authorizationCodeAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (this.logger.isTraceEnabled()) { this.logger.trace("Retrieved registered client"); } - OAuth2Authorization authorization = this.authorizationService.findByToken( - authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(authorizationCodeAuthentication.getCode(), AUTHORIZATION_CODE_TOKEN_TYPE); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } @@ -128,40 +137,45 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth this.logger.trace("Retrieved authorization with authorization code"); } - OAuth2Authorization.Token authorizationCode = - authorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); if (!registeredClient.getClientId().equals(authorizationRequest.getClientId())) { if (!authorizationCode.isInvalidated()) { - // Invalidate the authorization code given that a different client is attempting to use it - authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, authorizationCode.getToken()); + // Invalidate the authorization code given that a different client is + // attempting to use it + authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, + authorizationCode.getToken()); this.authorizationService.save(authorization); if (this.logger.isWarnEnabled()) { - this.logger.warn(LogMessage.format("Invalidated authorization code used by registered client '%s'", registeredClient.getId())); + this.logger.warn(LogMessage.format("Invalidated authorization code used by registered client '%s'", + registeredClient.getId())); } } throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } - if (StringUtils.hasText(authorizationRequest.getRedirectUri()) && - !authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) { + if (StringUtils.hasText(authorizationRequest.getRedirectUri()) + && !authorizationRequest.getRedirectUri().equals(authorizationCodeAuthentication.getRedirectUri())) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } if (!authorizationCode.isActive()) { if (authorizationCode.isInvalidated()) { - OAuth2Authorization.Token token = authorization.getRefreshToken() != null ? - authorization.getRefreshToken() : - authorization.getAccessToken(); + OAuth2Authorization.Token token = authorization.getRefreshToken() != null + ? authorization.getRefreshToken() : authorization.getAccessToken(); if (token != null) { - // Invalidate the access (and refresh) token as the client is attempting to use the authorization code more than once + // Invalidate the access (and refresh) token as the client is + // attempting to use the authorization code more than once authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, token.getToken()); this.authorizationService.save(authorization); if (this.logger.isWarnEnabled()) { - this.logger.warn(LogMessage.format("Invalidated authorization token(s) previously issued to registered client '%s'", registeredClient.getId())); + this.logger.warn(LogMessage.format( + "Invalidated authorization token(s) previously issued to registered client '%s'", + registeredClient.getId())); } } } @@ -204,14 +218,17 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(), generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); if (generatedAccessToken instanceof ClaimAccessor) { - authorizationBuilder.token(accessToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims())); - } else { + authorizationBuilder.token(accessToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims())); + } + else { authorizationBuilder.accessToken(accessToken); } // ----- Refresh token ----- OAuth2RefreshToken refreshToken = null; + // Do not issue refresh token to public client if (registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.REFRESH_TOKEN)) { tokenContext = tokenContextBuilder.tokenType(OAuth2TokenType.REFRESH_TOKEN).build(); OAuth2Token generatedRefreshToken = this.tokenGenerator.generate(tokenContext); @@ -240,7 +257,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth // Compute (and use) hash for Session ID sessionInformation = new SessionInformation(sessionInformation.getPrincipal(), createHash(sessionInformation.getSessionId()), sessionInformation.getLastRequest()); - } catch (NoSuchAlgorithmException ex) { + } + catch (NoSuchAlgorithmException ex) { OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "Failed to compute hash for Session ID.", ERROR_URI); throw new OAuth2AuthenticationException(error); @@ -266,9 +284,10 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(), generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims()); - authorizationBuilder.token(idToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); - } else { + authorizationBuilder.token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); + } + else { idToken = null; } @@ -293,8 +312,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth this.logger.trace("Authenticated token request"); } - return new OAuth2AccessTokenAuthenticationToken( - registeredClient, clientPrincipal, accessToken, refreshToken, additionalParameters); + return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, refreshToken, + additionalParameters); } @Override @@ -304,8 +323,8 @@ public final class OAuth2AuthorizationCodeAuthenticationProvider implements Auth /** * Sets the {@link SessionRegistry} used to track OpenID Connect sessions. - * - * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect sessions + * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect + * sessions * @since 1.1 */ public void setSessionRegistry(SessionRegistry sessionRegistry) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java index 6f38750e..63e7c09d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationToken.java @@ -23,7 +23,8 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.util.Assert; /** - * An {@link Authentication} implementation used for the OAuth 2.0 Authorization Code Grant. + * An {@link Authentication} implementation used for the OAuth 2.0 Authorization Code + * Grant. * * @author Joe Grandja * @author Madhu Bhat @@ -33,12 +34,14 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationCodeAuthenticationProvider */ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { + private final String code; + private final String redirectUri; /** - * Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeAuthenticationToken} using the provided + * parameters. * @param code the authorization code * @param clientPrincipal the authenticated client principal * @param redirectUri the redirect uri @@ -54,7 +57,6 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat /** * Returns the authorization code. - * * @return the authorization code */ public String getCode() { @@ -63,11 +65,11 @@ public class OAuth2AuthorizationCodeAuthenticationToken extends OAuth2Authorizat /** * Returns the redirect uri. - * * @return the redirect uri */ @Nullable public String getRedirectUri() { return this.redirectUri; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeGenerator.java index 9e5b6e28..aa051704 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeGenerator.java @@ -37,18 +37,19 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke * @see OAuth2AuthorizationConsentAuthenticationProvider */ final class OAuth2AuthorizationCodeGenerator implements OAuth2TokenGenerator { - private final StringKeyGenerator authorizationCodeGenerator = - new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + + private final StringKeyGenerator authorizationCodeGenerator = new Base64StringKeyGenerator( + Base64.getUrlEncoder().withoutPadding(), 96); @Nullable @Override public OAuth2AuthorizationCode generate(OAuth2TokenContext context) { - if (context.getTokenType() == null || - !OAuth2ParameterNames.CODE.equals(context.getTokenType().getValue())) { + if (context.getTokenType() == null || !OAuth2ParameterNames.CODE.equals(context.getTokenType().getValue())) { return null; } Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getAuthorizationCodeTimeToLive()); + Instant expiresAt = issuedAt + .plus(context.getRegisteredClient().getTokenSettings().getAuthorizationCodeTimeToLive()); return new OAuth2AuthorizationCode(this.authorizationCodeGenerator.generateKey(), issuedAt, expiresAt); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationContext.java index c158d940..a60dc938 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationContext.java @@ -25,8 +25,10 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import org.springframework.util.Assert; /** - * An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information - * and is used when validating the OAuth 2.0 Authorization Request used in the Authorization Code Grant. + * An {@link OAuth2AuthenticationContext} that holds an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and additional information + * and is used when validating the OAuth 2.0 Authorization Request used in the + * Authorization Code Grant. * * @author Joe Grandja * @since 0.4.0 @@ -35,6 +37,7 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer) */ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implements OAuth2AuthenticationContext { + private final Map context; private OAuth2AuthorizationCodeRequestAuthenticationContext(Map context) { @@ -56,7 +59,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement /** * Returns the {@link RegisteredClient registered client}. - * * @return the {@link RegisteredClient} */ public RegisteredClient getRegisteredClient() { @@ -64,8 +66,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement } /** - * Constructs a new {@link Builder} with the provided {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. - * + * Constructs a new {@link Builder} with the provided + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. * @param authentication the {@link OAuth2AuthorizationCodeRequestAuthenticationToken} * @return the {@link Builder} */ @@ -76,7 +78,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement /** * A builder for {@link OAuth2AuthorizationCodeRequestAuthenticationContext}. */ - public static final class Builder extends AbstractBuilder { + public static final class Builder + extends AbstractBuilder { private Builder(OAuth2AuthorizationCodeRequestAuthenticationToken authentication) { super(authentication); @@ -84,7 +87,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement /** * Sets the {@link RegisteredClient registered client}. - * * @param registeredClient the {@link RegisteredClient} * @return the {@link Builder} for further configuration */ @@ -94,7 +96,6 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationContext implement /** * Builds a new {@link OAuth2AuthorizationCodeRequestAuthenticationContext}. - * * @return the {@link OAuth2AuthorizationCodeRequestAuthenticationContext} */ public OAuth2AuthorizationCodeRequestAuthenticationContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationException.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationException.java index 8888ba29..36409cdb 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationException.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationException.java @@ -21,8 +21,9 @@ import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2Error; /** - * This exception is thrown by {@link OAuth2AuthorizationCodeRequestAuthenticationProvider} - * when an attempt to authenticate the OAuth 2.0 Authorization Request (or Consent) fails. + * This exception is thrown by + * {@link OAuth2AuthorizationCodeRequestAuthenticationProvider} when an attempt to + * authenticate the OAuth 2.0 Authorization Request (or Consent) fails. * * @author Joe Grandja * @since 0.1.2 @@ -30,13 +31,15 @@ import org.springframework.security.oauth2.core.OAuth2Error; * @see OAuth2AuthorizationCodeRequestAuthenticationProvider */ public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth2AuthenticationException { + private final OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication; /** - * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using + * the provided parameters. * @param error the {@link OAuth2Error OAuth 2.0 Error} - * @param authorizationCodeRequestAuthentication the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent) + * @param authorizationCodeRequestAuthentication the {@link Authentication} instance + * of the OAuth 2.0 Authorization Request (or Consent) */ public OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error, @Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) { @@ -45,11 +48,12 @@ public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth } /** - * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationException} using + * the provided parameters. * @param error the {@link OAuth2Error OAuth 2.0 Error} * @param cause the root cause - * @param authorizationCodeRequestAuthentication the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent) + * @param authorizationCodeRequestAuthentication the {@link Authentication} instance + * of the OAuth 2.0 Authorization Request (or Consent) */ public OAuth2AuthorizationCodeRequestAuthenticationException(OAuth2Error error, Throwable cause, @Nullable OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) { @@ -58,8 +62,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationException extends OAuth } /** - * Returns the {@link Authentication} instance of the OAuth 2.0 Authorization Request (or Consent), or {@code null} if not available. - * + * Returns the {@link Authentication} instance of the OAuth 2.0 Authorization Request + * (or Consent), or {@code null} if not available. * @return the {@link OAuth2AuthorizationCodeRequestAuthenticationToken} */ @Nullable diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java index aecc5b38..04db9883 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProvider.java @@ -52,8 +52,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Request - * used in the Authorization Code Grant. + * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization + * Request used in the Authorization Code Grant. * * @author Joe Grandja * @author Steve Riesenberg @@ -65,30 +65,41 @@ import org.springframework.util.StringUtils; * @see RegisteredClientRepository * @see OAuth2AuthorizationService * @see OAuth2AuthorizationConsentService - * @see Section 4.1.1 Authorization Request + * @see Section 4.1.1 + * Authorization Request */ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; + private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1"; - private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = - new Base64StringKeyGenerator(Base64.getUrlEncoder()); + + private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator( + Base64.getUrlEncoder()); + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2TokenGenerator authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator(); - private Consumer authenticationValidator = - new OAuth2AuthorizationCodeRequestAuthenticationValidator(); + + private Consumer authenticationValidator = new OAuth2AuthorizationCodeRequestAuthenticationValidator(); /** - * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationProvider} using + * the provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service * @param authorizationConsentService the authorization consent service */ public OAuth2AuthorizationCodeRequestAuthenticationProvider(RegisteredClientRepository registeredClientRepository, - OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) { + OAuth2AuthorizationService authorizationService, + OAuth2AuthorizationConsentService authorizationConsentService) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null"); @@ -99,11 +110,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication; + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication; - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId( - authorizationCodeRequestAuthentication.getClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findByClientId(authorizationCodeRequestAuthentication.getClientId()); if (registeredClient == null) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, authorizationCodeRequestAuthentication, null); @@ -113,10 +123,10 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen this.logger.trace("Retrieved registered client"); } - OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = - OAuth2AuthorizationCodeRequestAuthenticationContext.with(authorizationCodeRequestAuthentication) - .registeredClient(registeredClient) - .build(); + OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext = OAuth2AuthorizationCodeRequestAuthenticationContext + .with(authorizationCodeRequestAuthentication) + .registeredClient(registeredClient) + .build(); this.authenticationValidator.accept(authenticationContext); if (!registeredClient.getAuthorizationGrantTypes().contains(AuthorizationGrantType.AUTHORIZATION_CODE)) { @@ -125,14 +135,17 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen } // code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE) - String codeChallenge = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(PkceParameterNames.CODE_CHALLENGE); + String codeChallenge = (String) authorizationCodeRequestAuthentication.getAdditionalParameters() + .get(PkceParameterNames.CODE_CHALLENGE); if (StringUtils.hasText(codeChallenge)) { - String codeChallengeMethod = (String) authorizationCodeRequestAuthentication.getAdditionalParameters().get(PkceParameterNames.CODE_CHALLENGE_METHOD); + String codeChallengeMethod = (String) authorizationCodeRequestAuthentication.getAdditionalParameters() + .get(PkceParameterNames.CODE_CHALLENGE_METHOD); if (!StringUtils.hasText(codeChallengeMethod) || !"S256".equals(codeChallengeMethod)) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, PKCE_ERROR_URI, authorizationCodeRequestAuthentication, registeredClient, null); } - } else if (registeredClient.getClientSettings().isRequireProofKey()) { + } + else if (registeredClient.getClientSettings().isRequireProofKey()) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI, authorizationCodeRequestAuthentication, registeredClient, null); } @@ -155,22 +168,22 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen } OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() - .authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri()) - .clientId(registeredClient.getClientId()) - .redirectUri(authorizationCodeRequestAuthentication.getRedirectUri()) - .scopes(authorizationCodeRequestAuthentication.getScopes()) - .state(authorizationCodeRequestAuthentication.getState()) - .additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters()) - .build(); + .authorizationUri(authorizationCodeRequestAuthentication.getAuthorizationUri()) + .clientId(registeredClient.getClientId()) + .redirectUri(authorizationCodeRequestAuthentication.getRedirectUri()) + .scopes(authorizationCodeRequestAuthentication.getScopes()) + .state(authorizationCodeRequestAuthentication.getState()) + .additionalParameters(authorizationCodeRequestAuthentication.getAdditionalParameters()) + .build(); - OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById( - registeredClient.getId(), principal.getName()); + OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService + .findById(registeredClient.getId(), principal.getName()); if (requireAuthorizationConsent(registeredClient, authorizationRequest, currentAuthorizationConsent)) { String state = DEFAULT_STATE_GENERATOR.generateKey(); OAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest) - .attribute(OAuth2ParameterNames.STATE, state) - .build(); + .attribute(OAuth2ParameterNames.STATE, state) + .build(); if (this.logger.isTraceEnabled()) { logger.trace("Generated authorization consent state"); @@ -178,8 +191,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen this.authorizationService.save(authorization); - Set currentAuthorizedScopes = currentAuthorizationConsent != null ? - currentAuthorizationConsent.getScopes() : null; + Set currentAuthorizedScopes = currentAuthorizationConsent != null + ? currentAuthorizationConsent.getScopes() : null; if (this.logger.isTraceEnabled()) { this.logger.trace("Saved authorization"); @@ -189,8 +202,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen registeredClient.getClientId(), principal, state, currentAuthorizedScopes, null); } - OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext( - authorizationCodeRequestAuthentication, registeredClient, null, authorizationRequest.getScopes()); + OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationCodeRequestAuthentication, + registeredClient, null, authorizationRequest.getScopes()); OAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext); if (authorizationCode == null) { OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, @@ -203,9 +216,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen } OAuth2Authorization authorization = authorizationBuilder(registeredClient, principal, authorizationRequest) - .authorizedScopes(authorizationRequest.getScopes()) - .token(authorizationCode) - .build(); + .authorizedScopes(authorizationRequest.getScopes()) + .token(authorizationCode) + .build(); this.authorizationService.save(authorization); if (this.logger.isTraceEnabled()) { @@ -232,40 +245,47 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen } /** - * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}. - * - * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode} + * Sets the {@link OAuth2TokenGenerator} that generates the + * {@link OAuth2AuthorizationCode}. + * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates + * the {@link OAuth2AuthorizationCode} * @since 0.2.3 */ - public void setAuthorizationCodeGenerator(OAuth2TokenGenerator authorizationCodeGenerator) { + public void setAuthorizationCodeGenerator( + OAuth2TokenGenerator authorizationCodeGenerator) { Assert.notNull(authorizationCodeGenerator, "authorizationCodeGenerator cannot be null"); this.authorizationCodeGenerator = authorizationCodeGenerator; } /** - * Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext} - * and is responsible for validating specific OAuth 2.0 Authorization Request parameters - * associated in the {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. - * The default authentication validator is {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}. + * Sets the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for + * validating specific OAuth 2.0 Authorization Request parameters associated in the + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. The default + * authentication validator is + * {@link OAuth2AuthorizationCodeRequestAuthenticationValidator}. * *

- * NOTE: The authentication validator MUST throw {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails. - * - * @param authenticationValidator the {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for validating specific OAuth 2.0 Authorization Request parameters + * NOTE: The authentication validator MUST throw + * {@link OAuth2AuthorizationCodeRequestAuthenticationException} if validation fails. + * @param authenticationValidator the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} and is responsible for + * validating specific OAuth 2.0 Authorization Request parameters * @since 0.4.0 */ - public void setAuthenticationValidator(Consumer authenticationValidator) { + public void setAuthenticationValidator( + Consumer authenticationValidator) { Assert.notNull(authenticationValidator, "authenticationValidator cannot be null"); this.authenticationValidator = authenticationValidator; } - private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient, Authentication principal, - OAuth2AuthorizationRequest authorizationRequest) { + private static OAuth2Authorization.Builder authorizationBuilder(RegisteredClient registeredClient, + Authentication principal, OAuth2AuthorizationRequest authorizationRequest) { return OAuth2Authorization.withRegisteredClient(registeredClient) - .principalName(principal.getName()) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .attribute(Principal.class.getName(), principal) - .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest); + .principalName(principal.getName()) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .attribute(Principal.class.getName(), principal) + .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest); } private static OAuth2TokenContext createAuthorizationCodeTokenContext( @@ -297,13 +317,13 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen return false; } // 'openid' scope does not require consent - if (authorizationRequest.getScopes().contains(OidcScopes.OPENID) && - authorizationRequest.getScopes().size() == 1) { + if (authorizationRequest.getScopes().contains(OidcScopes.OPENID) + && authorizationRequest.getScopes().size() == 1) { return false; } - if (authorizationConsent != null && - authorizationConsent.getScopes().containsAll(authorizationRequest.getScopes())) { + if (authorizationConsent != null + && authorizationConsent.getScopes().containsAll(authorizationRequest.getScopes())) { return false; } @@ -311,9 +331,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen } private static boolean isPrincipalAuthenticated(Authentication principal) { - return principal != null && - !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) && - principal.isAuthenticated(); + return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) + && principal.isAuthenticated(); } private static void throwError(String errorCode, String parameterName, @@ -326,35 +345,39 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationProvider implemen OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication, RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) { OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, errorUri); - throwError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient, authorizationRequest); + throwError(error, parameterName, authorizationCodeRequestAuthentication, registeredClient, + authorizationRequest); } private static void throwError(OAuth2Error error, String parameterName, OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication, RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) { - String redirectUri = resolveRedirectUri(authorizationCodeRequestAuthentication, authorizationRequest, registeredClient); - if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) && - (parameterName.equals(OAuth2ParameterNames.CLIENT_ID) || - parameterName.equals(OAuth2ParameterNames.STATE))) { - redirectUri = null; // Prevent redirects + String redirectUri = resolveRedirectUri(authorizationCodeRequestAuthentication, authorizationRequest, + registeredClient); + if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) + && (parameterName.equals(OAuth2ParameterNames.CLIENT_ID) + || parameterName.equals(OAuth2ParameterNames.STATE))) { + redirectUri = null; // Prevent redirects } - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationCodeRequestAuthentication.getClientId(), - (Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri, - authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(), - authorizationCodeRequestAuthentication.getAdditionalParameters()); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + authorizationCodeRequestAuthentication.getAuthorizationUri(), + authorizationCodeRequestAuthentication.getClientId(), + (Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri, + authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(), + authorizationCodeRequestAuthentication.getAdditionalParameters()); - throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult); + throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, + authorizationCodeRequestAuthenticationResult); } private static String resolveRedirectUri( OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication, OAuth2AuthorizationRequest authorizationRequest, RegisteredClient registeredClient) { - if (authorizationCodeRequestAuthentication != null && StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) { + if (authorizationCodeRequestAuthentication != null + && StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) { return authorizationCodeRequestAuthentication.getRedirectUri(); } if (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationToken.java index a7fab020..83cf1651 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationToken.java @@ -29,8 +29,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation for the OAuth 2.0 Authorization Request - * used in the Authorization Code Grant. + * An {@link Authentication} implementation for the OAuth 2.0 Authorization Request used + * in the Authorization Code Grant. * * @author Joe Grandja * @since 0.1.2 @@ -38,19 +38,28 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationConsentAuthenticationProvider */ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String authorizationUri; + private final String clientId; + private final Authentication principal; + private final String redirectUri; + private final String state; + private final Set scopes; + private final Map additionalParameters; + private final OAuth2AuthorizationCode authorizationCode; /** - * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the + * provided parameters. * @param authorizationUri the authorization URI * @param clientId the client identifier * @param principal the {@code Principal} (Resource Owner) @@ -60,8 +69,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA * @param additionalParameters the additional parameters * @since 0.4.0 */ - public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, Authentication principal, - @Nullable String redirectUri, @Nullable String state, @Nullable Set scopes, @Nullable Map additionalParameters) { + public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, + Authentication principal, @Nullable String redirectUri, @Nullable String state, + @Nullable Set scopes, @Nullable Map additionalParameters) { super(Collections.emptyList()); Assert.hasText(authorizationUri, "authorizationUri cannot be empty"); Assert.hasText(clientId, "clientId cannot be empty"); @@ -71,20 +81,15 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA this.principal = principal; this.redirectUri = redirectUri; this.state = state; - this.scopes = Collections.unmodifiableSet( - scopes != null ? - new HashSet<>(scopes) : - Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? - new HashMap<>(additionalParameters) : - Collections.emptyMap()); + additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap()); this.authorizationCode = null; } /** - * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationCodeRequestAuthenticationToken} using the + * provided parameters. * @param authorizationUri the authorization URI * @param clientId the client identifier * @param principal the {@code Principal} (Resource Owner) @@ -94,8 +99,9 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA * @param scopes the authorized scope(s) * @since 0.4.0 */ - public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, Authentication principal, - OAuth2AuthorizationCode authorizationCode, @Nullable String redirectUri, @Nullable String state, @Nullable Set scopes) { + public OAuth2AuthorizationCodeRequestAuthenticationToken(String authorizationUri, String clientId, + Authentication principal, OAuth2AuthorizationCode authorizationCode, @Nullable String redirectUri, + @Nullable String state, @Nullable Set scopes) { super(Collections.emptyList()); Assert.hasText(authorizationUri, "authorizationUri cannot be empty"); Assert.hasText(clientId, "clientId cannot be empty"); @@ -107,10 +113,7 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA this.authorizationCode = authorizationCode; this.redirectUri = redirectUri; this.state = state; - this.scopes = Collections.unmodifiableSet( - scopes != null ? - new HashSet<>(scopes) : - Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); this.additionalParameters = Collections.emptyMap(); setAuthenticated(true); } @@ -127,7 +130,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the authorization URI. - * * @return the authorization URI */ public String getAuthorizationUri() { @@ -136,7 +138,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the client identifier. - * * @return the client identifier */ public String getClientId() { @@ -145,7 +146,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the redirect uri. - * * @return the redirect uri */ @Nullable @@ -155,7 +155,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the state. - * * @return the state */ @Nullable @@ -165,8 +164,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the requested (or authorized) scope(s). - * - * @return the requested (or authorized) scope(s), or an empty {@code Set} if not available + * @return the requested (or authorized) scope(s), or an empty {@code Set} if not + * available */ public Set getScopes() { return this.scopes; @@ -174,7 +173,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the additional parameters. - * * @return the additional parameters, or an empty {@code Map} if not available */ public Map getAdditionalParameters() { @@ -183,7 +181,6 @@ public class OAuth2AuthorizationCodeRequestAuthenticationToken extends AbstractA /** * Returns the {@link OAuth2AuthorizationCode}. - * * @return the {@link OAuth2AuthorizationCode} */ @Nullable diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java index 5ad554e3..b574ac83 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationValidator.java @@ -33,15 +33,18 @@ import org.springframework.web.util.UriComponents; import org.springframework.web.util.UriComponentsBuilder; /** - * A {@code Consumer} providing access to the {@link OAuth2AuthorizationCodeRequestAuthenticationContext} - * containing an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and is the default {@link OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer) authentication validator} - * used for validating specific OAuth 2.0 Authorization Request parameters used in the Authorization Code Grant. + * A {@code Consumer} providing access to the + * {@link OAuth2AuthorizationCodeRequestAuthenticationContext} containing an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and is the default + * {@link OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer) + * authentication validator} used for validating specific OAuth 2.0 Authorization Request + * parameters used in the Authorization Code Grant. * *

- * The default implementation first validates {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()} - * and then {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. - * If validation fails, an {@link OAuth2AuthorizationCodeRequestAuthenticationException} is thrown. + * The default implementation first validates + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()} and then + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. If validation + * fails, an {@link OAuth2AuthorizationCodeRequestAuthenticationException} is thrown. * * @author Joe Grandja * @since 0.4.0 @@ -49,24 +52,26 @@ import org.springframework.web.util.UriComponentsBuilder; * @see OAuth2AuthorizationCodeRequestAuthenticationToken * @see OAuth2AuthorizationCodeRequestAuthenticationProvider#setAuthenticationValidator(Consumer) */ -public final class OAuth2AuthorizationCodeRequestAuthenticationValidator implements Consumer { +public final class OAuth2AuthorizationCodeRequestAuthenticationValidator + implements Consumer { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; private static final Log LOGGER = LogFactory.getLog(OAuth2AuthorizationCodeRequestAuthenticationValidator.class); /** - * The default validator for {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. + * The default validator for + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getScopes()}. */ - public static final Consumer DEFAULT_SCOPE_VALIDATOR = - OAuth2AuthorizationCodeRequestAuthenticationValidator::validateScope; + public static final Consumer DEFAULT_SCOPE_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateScope; /** - * The default validator for {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}. + * The default validator for + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken#getRedirectUri()}. */ - public static final Consumer DEFAULT_REDIRECT_URI_VALIDATOR = - OAuth2AuthorizationCodeRequestAuthenticationValidator::validateRedirectUri; + public static final Consumer DEFAULT_REDIRECT_URI_VALIDATOR = OAuth2AuthorizationCodeRequestAuthenticationValidator::validateRedirectUri; - private final Consumer authenticationValidator = - DEFAULT_REDIRECT_URI_VALIDATOR.andThen(DEFAULT_SCOPE_VALIDATOR); + private final Consumer authenticationValidator = DEFAULT_REDIRECT_URI_VALIDATOR + .andThen(DEFAULT_SCOPE_VALIDATOR); @Override public void accept(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) { @@ -74,8 +79,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme } private static void validateScope(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) { - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authenticationContext.getAuthentication(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext + .getAuthentication(); RegisteredClient registeredClient = authenticationContext.getRegisteredClient(); Set requestedScopes = authorizationCodeRequestAuthentication.getScopes(); @@ -91,8 +96,8 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme } private static void validateRedirectUri(OAuth2AuthorizationCodeRequestAuthenticationContext authenticationContext) { - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authenticationContext.getAuthentication(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext + .getAuthentication(); RegisteredClient registeredClient = authenticationContext.getRegisteredClient(); String requestedRedirectUri = authorizationCodeRequestAuthentication.getRedirectUri(); @@ -103,7 +108,9 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme UriComponents requestedRedirect = null; try { requestedRedirect = UriComponentsBuilder.fromUriString(requestedRedirectUri).build(); - } catch (Exception ex) { } + } + catch (Exception ex) { + } if (requestedRedirect == null || requestedRedirect.getFragment() != null) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(LogMessage.format("Invalid request: redirect_uri is missing or contains a fragment" + @@ -114,15 +121,18 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme } if (!isLoopbackAddress(requestedRedirect.getHost())) { - // As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3 + // As per + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-security-topics-22#section-4.1.3 // When comparing client redirect URIs against pre-registered URIs, // authorization servers MUST utilize exact string matching. if (!registeredClient.getRedirectUris().contains(requestedRedirectUri)) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, authorizationCodeRequestAuthentication, registeredClient); } - } else { - // As per https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2 + } + else { + // As per + // https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-08#section-8.4.2 // The authorization server MUST allow any port to be specified at the // time of the request for loopback IP redirect URIs, to accommodate // clients that obtain an available ephemeral port from the operating @@ -146,11 +156,12 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme } } - } else { + } + else { // ***** redirect_uri is NOT available in authorization request - if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID) || - registeredClient.getRedirectUris().size() != 1) { + if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID) + || registeredClient.getRedirectUris().size() != 1) { // redirect_uri is REQUIRED for OpenID Connect throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, authorizationCodeRequestAuthentication, registeredClient); @@ -173,12 +184,13 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme } try { int[] address = new int[ipv4Octets.length]; - for (int i=0; i < ipv4Octets.length; i++) { + for (int i = 0; i < ipv4Octets.length; i++) { address[i] = Integer.parseInt(ipv4Octets[i]); } - return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 && - address[2] <= 255 && address[3] >= 1 && address[3] <= 255; - } catch (NumberFormatException ex) { + return address[0] == 127 && address[1] >= 0 && address[1] <= 255 && address[2] >= 0 && address[2] <= 255 + && address[3] >= 1 && address[3] <= 255; + } + catch (NumberFormatException ex) { return false; } } @@ -194,23 +206,24 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationValidator impleme OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication, RegisteredClient registeredClient) { - String redirectUri = StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri()) ? - authorizationCodeRequestAuthentication.getRedirectUri() : - registeredClient.getRedirectUris().iterator().next(); - if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) && - parameterName.equals(OAuth2ParameterNames.REDIRECT_URI)) { - redirectUri = null; // Prevent redirects + String redirectUri = StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri()) + ? authorizationCodeRequestAuthentication.getRedirectUri() + : registeredClient.getRedirectUris().iterator().next(); + if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) + && parameterName.equals(OAuth2ParameterNames.REDIRECT_URI)) { + redirectUri = null; // Prevent redirects } - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - authorizationCodeRequestAuthentication.getAuthorizationUri(), authorizationCodeRequestAuthentication.getClientId(), - (Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri, - authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(), - authorizationCodeRequestAuthentication.getAdditionalParameters()); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + authorizationCodeRequestAuthentication.getAuthorizationUri(), + authorizationCodeRequestAuthentication.getClientId(), + (Authentication) authorizationCodeRequestAuthentication.getPrincipal(), redirectUri, + authorizationCodeRequestAuthentication.getState(), authorizationCodeRequestAuthentication.getScopes(), + authorizationCodeRequestAuthentication.getAdditionalParameters()); authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult); + throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, + authorizationCodeRequestAuthenticationResult); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java index a60f0398..aff7c9d3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContext.java @@ -29,8 +29,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import org.springframework.util.Assert; /** - * An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2AuthorizationConsent.Builder} and additional information - * and is used when customizing the building of the {@link OAuth2AuthorizationConsent}. + * An {@link OAuth2AuthenticationContext} that holds an + * {@link OAuth2AuthorizationConsent.Builder} and additional information and is used when + * customizing the building of the {@link OAuth2AuthorizationConsent}. * * @author Steve Riesenberg * @author Joe Grandja @@ -40,6 +41,7 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationConsentAuthenticationProvider#setAuthorizationConsentCustomizer(Consumer) */ public final class OAuth2AuthorizationConsentAuthenticationContext implements OAuth2AuthenticationContext { + private final Map context; private OAuth2AuthorizationConsentAuthenticationContext(Map context) { @@ -60,8 +62,8 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA } /** - * Returns the {@link OAuth2AuthorizationConsent.Builder authorization consent builder}. - * + * Returns the {@link OAuth2AuthorizationConsent.Builder authorization consent + * builder}. * @return the {@link OAuth2AuthorizationConsent.Builder} */ public OAuth2AuthorizationConsent.Builder getAuthorizationConsent() { @@ -70,7 +72,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Returns the {@link RegisteredClient registered client}. - * * @return the {@link RegisteredClient} */ public RegisteredClient getRegisteredClient() { @@ -79,7 +80,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Returns the {@link OAuth2Authorization authorization}. - * * @return the {@link OAuth2Authorization} */ public OAuth2Authorization getAuthorization() { @@ -88,7 +88,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Returns the {@link OAuth2AuthorizationRequest authorization request}. - * * @return the {@link OAuth2AuthorizationRequest} */ public OAuth2AuthorizationRequest getAuthorizationRequest() { @@ -96,8 +95,8 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA } /** - * Constructs a new {@link Builder} with the provided {@link OAuth2AuthorizationConsentAuthenticationToken}. - * + * Constructs a new {@link Builder} with the provided + * {@link OAuth2AuthorizationConsentAuthenticationToken}. * @param authentication the {@link OAuth2AuthorizationConsentAuthenticationToken} * @return the {@link Builder} */ @@ -108,15 +107,16 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * A builder for {@link OAuth2AuthorizationConsentAuthenticationContext}. */ - public static final class Builder extends AbstractBuilder { + public static final class Builder + extends AbstractBuilder { private Builder(OAuth2AuthorizationConsentAuthenticationToken authentication) { super(authentication); } /** - * Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent builder}. - * + * Sets the {@link OAuth2AuthorizationConsent.Builder authorization consent + * builder}. * @param authorizationConsent the {@link OAuth2AuthorizationConsent.Builder} * @return the {@link Builder} for further configuration */ @@ -126,7 +126,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Sets the {@link RegisteredClient registered client}. - * * @param registeredClient the {@link RegisteredClient} * @return the {@link Builder} for further configuration */ @@ -136,7 +135,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Sets the {@link OAuth2Authorization authorization}. - * * @param authorization the {@link OAuth2Authorization} * @return the {@link Builder} for further configuration */ @@ -146,7 +144,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Sets the {@link OAuth2AuthorizationRequest authorization request}. - * * @param authorizationRequest the {@link OAuth2AuthorizationRequest} * @return the {@link Builder} for further configuration */ @@ -156,7 +153,6 @@ public final class OAuth2AuthorizationConsentAuthenticationContext implements OA /** * Builds a new {@link OAuth2AuthorizationConsentAuthenticationContext}. - * * @return the {@link OAuth2AuthorizationConsentAuthenticationContext} */ public OAuth2AuthorizationConsentAuthenticationContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java index 8cdd789b..a644b636 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProvider.java @@ -50,8 +50,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization Consent - * used in the Authorization Code Grant. + * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Authorization + * Consent used in the Authorization Code Grant. * * @author Joe Grandja * @since 0.4.0 @@ -63,24 +63,33 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationConsentService */ public final class OAuth2AuthorizationConsentAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; + private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2TokenGenerator authorizationCodeGenerator = new OAuth2AuthorizationCodeGenerator(); + private Consumer authorizationConsentCustomizer; /** - * Constructs an {@code OAuth2AuthorizationConsentAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationConsentAuthenticationProvider} using the + * provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service * @param authorizationConsentService the authorization consent service */ public OAuth2AuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository, - OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) { + OAuth2AuthorizationService authorizationService, + OAuth2AuthorizationConsentService authorizationConsentService) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null"); @@ -92,19 +101,20 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { if (authentication instanceof OAuth2DeviceAuthorizationConsentAuthenticationToken) { - // This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code Grant, - // return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider handle it instead + // This is NOT an OAuth 2.0 Authorization Consent for the Authorization Code + // Grant, + // return null and let OAuth2DeviceAuthorizationConsentAuthenticationProvider + // handle it instead return null; } - OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = - (OAuth2AuthorizationConsentAuthenticationToken) authentication; + OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = (OAuth2AuthorizationConsentAuthenticationToken) authentication; - OAuth2Authorization authorization = this.authorizationService.findByToken( - authorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(authorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE); if (authorization == null) { - throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, - authorizationConsentAuthentication, null, null); + throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication, + null, null); } if (this.logger.isTraceEnabled()) { @@ -114,12 +124,12 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A // The 'in-flight' authorization must be associated to the current principal Authentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal(); if (!isPrincipalAuthenticated(principal) || !principal.getName().equals(authorization.getPrincipalName())) { - throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, - authorizationConsentAuthentication, null, null); + throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, authorizationConsentAuthentication, + null, null); } - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId( - authorizationConsentAuthentication.getClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findByClientId(authorizationConsentAuthentication.getClientId()); if (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, authorizationConsentAuthentication, registeredClient, null); @@ -129,22 +139,23 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A this.logger.trace("Retrieved registered client"); } - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set requestedScopes = authorizationRequest.getScopes(); Set authorizedScopes = new HashSet<>(authorizationConsentAuthentication.getScopes()); if (!requestedScopes.containsAll(authorizedScopes)) { - throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, - authorizationConsentAuthentication, registeredClient, authorizationRequest); + throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationConsentAuthentication, + registeredClient, authorizationRequest); } if (this.logger.isTraceEnabled()) { this.logger.trace("Validated authorization consent request parameters"); } - OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById( - authorization.getRegisteredClientId(), authorization.getPrincipalName()); - Set currentAuthorizedScopes = currentAuthorizationConsent != null ? - currentAuthorizationConsent.getScopes() : Collections.emptySet(); + OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService + .findById(authorization.getRegisteredClientId(), authorization.getPrincipalName()); + Set currentAuthorizedScopes = currentAuthorizationConsent != null + ? currentAuthorizationConsent.getScopes() : Collections.emptySet(); if (!currentAuthorizedScopes.isEmpty()) { for (String requestedScope : requestedScopes) { @@ -165,9 +176,10 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A this.logger.trace("Retrieved existing authorization consent"); } authorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent); - } else { - authorizationConsentBuilder = OAuth2AuthorizationConsent.withId( - authorization.getRegisteredClientId(), authorization.getPrincipalName()); + } + else { + authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), + authorization.getPrincipalName()); } authorizedScopes.forEach(authorizationConsentBuilder::scope); @@ -214,8 +226,8 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A } } - OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext( - authorizationConsentAuthentication, registeredClient, authorization, authorizedScopes); + OAuth2TokenContext tokenContext = createAuthorizationCodeTokenContext(authorizationConsentAuthentication, + registeredClient, authorization, authorizedScopes); OAuth2AuthorizationCode authorizationCode = this.authorizationCodeGenerator.generate(tokenContext); if (authorizationCode == null) { OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, @@ -228,12 +240,12 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A } OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization) - .authorizedScopes(authorizedScopes) - .token(authorizationCode) - .attributes(attrs -> { - attrs.remove(OAuth2ParameterNames.STATE); - }) - .build(); + .authorizedScopes(authorizedScopes) + .token(authorizationCode) + .attributes(attrs -> { + attrs.remove(OAuth2ParameterNames.STATE); + }) + .build(); this.authorizationService.save(updatedAuthorization); if (this.logger.isTraceEnabled()) { @@ -249,9 +261,9 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A this.logger.trace("Authenticated authorization consent request"); } - return new OAuth2AuthorizationCodeRequestAuthenticationToken( - authorizationRequest.getAuthorizationUri(), registeredClient.getClientId(), principal, authorizationCode, - redirectUri, authorizationRequest.getState(), authorizedScopes); + return new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationRequest.getAuthorizationUri(), + registeredClient.getClientId(), principal, authorizationCode, redirectUri, + authorizationRequest.getState(), authorizedScopes); } @Override @@ -260,36 +272,42 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A } /** - * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode}. - * - * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2AuthorizationCode} + * Sets the {@link OAuth2TokenGenerator} that generates the + * {@link OAuth2AuthorizationCode}. + * @param authorizationCodeGenerator the {@link OAuth2TokenGenerator} that generates + * the {@link OAuth2AuthorizationCode} */ - public void setAuthorizationCodeGenerator(OAuth2TokenGenerator authorizationCodeGenerator) { + public void setAuthorizationCodeGenerator( + OAuth2TokenGenerator authorizationCodeGenerator) { Assert.notNull(authorizationCodeGenerator, "authorizationCodeGenerator cannot be null"); this.authorizationCodeGenerator = authorizationCodeGenerator; } /** - * Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationConsentAuthenticationContext} - * containing an {@link OAuth2AuthorizationConsent.Builder} and additional context information. + * Sets the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an + * {@link OAuth2AuthorizationConsent.Builder} and additional context information. * *

* The following context attributes are available: *

    - *
  • The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization consent - * prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.
  • + *
  • The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization + * consent prior to + * {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.
  • *
  • The {@link Authentication} of type * {@link OAuth2AuthorizationConsentAuthenticationToken}.
  • *
  • The {@link RegisteredClient} associated with the authorization request.
  • - *
  • The {@link OAuth2Authorization} associated with the state token presented in the - * authorization consent request.
  • - *
  • The {@link OAuth2AuthorizationRequest} associated with the authorization consent request.
  • + *
  • The {@link OAuth2Authorization} associated with the state token presented in + * the authorization consent request.
  • + *
  • The {@link OAuth2AuthorizationRequest} associated with the authorization + * consent request.
  • *
- * * @param authorizationConsentCustomizer the {@code Consumer} providing access to the - * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an {@link OAuth2AuthorizationConsent.Builder} + * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an + * {@link OAuth2AuthorizationConsent.Builder} */ - public void setAuthorizationConsentCustomizer(Consumer authorizationConsentCustomizer) { + public void setAuthorizationConsentCustomizer( + Consumer authorizationConsentCustomizer) { Assert.notNull(authorizationConsentCustomizer, "authorizationConsentCustomizer cannot be null"); this.authorizationConsentCustomizer = authorizationConsentCustomizer; } @@ -313,9 +331,8 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A } private static boolean isPrincipalAuthenticated(Authentication principal) { - return principal != null && - !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) && - principal.isAuthenticated(); + return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) + && principal.isAuthenticated(); } private static void throwError(String errorCode, String parameterName, @@ -330,29 +347,29 @@ public final class OAuth2AuthorizationConsentAuthenticationProvider implements A RegisteredClient registeredClient, OAuth2AuthorizationRequest authorizationRequest) { String redirectUri = resolveRedirectUri(authorizationRequest, registeredClient); - if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) && - (parameterName.equals(OAuth2ParameterNames.CLIENT_ID) || - parameterName.equals(OAuth2ParameterNames.STATE))) { - redirectUri = null; // Prevent redirects + if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_REQUEST) + && (parameterName.equals(OAuth2ParameterNames.CLIENT_ID) + || parameterName.equals(OAuth2ParameterNames.STATE))) { + redirectUri = null; // Prevent redirects } - String state = authorizationRequest != null ? - authorizationRequest.getState() : - authorizationConsentAuthentication.getState(); - Set requestedScopes = authorizationRequest != null ? - authorizationRequest.getScopes() : - authorizationConsentAuthentication.getScopes(); + String state = authorizationRequest != null ? authorizationRequest.getState() + : authorizationConsentAuthentication.getState(); + Set requestedScopes = authorizationRequest != null ? authorizationRequest.getScopes() + : authorizationConsentAuthentication.getScopes(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - authorizationConsentAuthentication.getAuthorizationUri(), authorizationConsentAuthentication.getClientId(), - (Authentication) authorizationConsentAuthentication.getPrincipal(), redirectUri, - state, requestedScopes, null); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + authorizationConsentAuthentication.getAuthorizationUri(), + authorizationConsentAuthentication.getClientId(), + (Authentication) authorizationConsentAuthentication.getPrincipal(), redirectUri, state, requestedScopes, + null); - throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthenticationResult); + throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, + authorizationCodeRequestAuthenticationResult); } - private static String resolveRedirectUri(OAuth2AuthorizationRequest authorizationRequest, RegisteredClient registeredClient) { + private static String resolveRedirectUri(OAuth2AuthorizationRequest authorizationRequest, + RegisteredClient registeredClient) { if (authorizationRequest != null && StringUtils.hasText(authorizationRequest.getRedirectUri())) { return authorizationRequest.getRedirectUri(); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationToken.java index dd068d63..abe8fbc9 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationToken.java @@ -28,8 +28,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation for the OAuth 2.0 Authorization Consent - * used in the Authorization Code Grant. + * An {@link Authentication} implementation for the OAuth 2.0 Authorization Consent used + * in the Authorization Code Grant. * * @author Joe Grandja * @since 0.4.0 @@ -37,17 +37,24 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationCodeRequestAuthenticationProvider */ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String authorizationUri; + private final String clientId; + private final Authentication principal; + private final String state; + private final Set scopes; + private final Map additionalParameters; /** - * Constructs an {@code OAuth2AuthorizationConsentAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationConsentAuthenticationToken} using the + * provided parameters. * @param authorizationUri the authorization URI * @param clientId the client identifier * @param principal the {@code Principal} (Resource Owner) @@ -55,8 +62,9 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe * @param scopes the requested (or authorized) scope(s) * @param additionalParameters the additional parameters */ - public OAuth2AuthorizationConsentAuthenticationToken(String authorizationUri, String clientId, Authentication principal, - String state, @Nullable Set scopes, @Nullable Map additionalParameters) { + public OAuth2AuthorizationConsentAuthenticationToken(String authorizationUri, String clientId, + Authentication principal, String state, @Nullable Set scopes, + @Nullable Map additionalParameters) { super(Collections.emptyList()); Assert.hasText(authorizationUri, "authorizationUri cannot be empty"); Assert.hasText(clientId, "clientId cannot be empty"); @@ -66,14 +74,9 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe this.clientId = clientId; this.principal = principal; this.state = state; - this.scopes = Collections.unmodifiableSet( - scopes != null ? - new HashSet<>(scopes) : - Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? - new HashMap<>(additionalParameters) : - Collections.emptyMap()); + additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap()); setAuthenticated(true); } @@ -89,7 +92,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe /** * Returns the authorization URI. - * * @return the authorization URI */ public String getAuthorizationUri() { @@ -98,7 +100,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe /** * Returns the client identifier. - * * @return the client identifier */ public String getClientId() { @@ -107,7 +108,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe /** * Returns the state. - * * @return the state */ public String getState() { @@ -116,8 +116,8 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe /** * Returns the requested (or authorized) scope(s). - * - * @return the requested (or authorized) scope(s), or an empty {@code Set} if not available + * @return the requested (or authorized) scope(s), or an empty {@code Set} if not + * available */ public Set getScopes() { return this.scopes; @@ -125,7 +125,6 @@ public class OAuth2AuthorizationConsentAuthenticationToken extends AbstractAuthe /** * Returns the additional parameters. - * * @return the additional parameters, or an empty {@code Map} if not available */ public Map getAdditionalParameters() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java index 659a85b5..59f4e4b5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationGrantAuthenticationToken.java @@ -27,24 +27,29 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * Base implementation of an {@link Authentication} representing an OAuth 2.0 Authorization Grant. + * Base implementation of an {@link Authentication} representing an OAuth 2.0 + * Authorization Grant. * * @author Joe Grandja * @since 0.1.0 * @see AbstractAuthenticationToken * @see AuthorizationGrantType * @see OAuth2ClientAuthenticationToken - * @see Section 1.3 Authorization Grant + * @see Section + * 1.3 Authorization Grant */ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final AuthorizationGrantType authorizationGrantType; + private final Authentication clientPrincipal; + private final Map additionalParameters; /** * Sub-class constructor. - * * @param authorizationGrantType the authorization grant type * @param clientPrincipal the authenticated client principal * @param additionalParameters the additional parameters @@ -57,14 +62,11 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent this.authorizationGrantType = authorizationGrantType; this.clientPrincipal = clientPrincipal; this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? - new HashMap<>(additionalParameters) : - Collections.emptyMap()); + additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap()); } /** * Returns the authorization grant type. - * * @return the authorization grant type */ public AuthorizationGrantType getGrantType() { @@ -83,10 +85,10 @@ public class OAuth2AuthorizationGrantAuthenticationToken extends AbstractAuthent /** * Returns the additional parameters. - * * @return the additional parameters */ public Map getAdditionalParameters() { return this.additionalParameters; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationToken.java index 561d7901..cd898750 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationToken.java @@ -42,16 +42,22 @@ import org.springframework.util.Assert; */ @Transient public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String clientId; + private final RegisteredClient registeredClient; + private final ClientAuthenticationMethod clientAuthenticationMethod; + private final Object credentials; + private final Map additionalParameters; /** - * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided + * parameters. * @param clientId the client identifier * @param clientAuthenticationMethod the authentication method used by the client * @param credentials the client credentials @@ -66,19 +72,19 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken this.registeredClient = null; this.clientAuthenticationMethod = clientAuthenticationMethod; this.credentials = credentials; - this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? additionalParameters : Collections.emptyMap()); + this.additionalParameters = Collections + .unmodifiableMap(additionalParameters != null ? additionalParameters : Collections.emptyMap()); } /** - * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2ClientAuthenticationToken} using the provided + * parameters. * @param registeredClient the authenticated registered client * @param clientAuthenticationMethod the authentication method used by the client * @param credentials the client credentials */ - public OAuth2ClientAuthenticationToken(RegisteredClient registeredClient, ClientAuthenticationMethod clientAuthenticationMethod, - @Nullable Object credentials) { + public OAuth2ClientAuthenticationToken(RegisteredClient registeredClient, + ClientAuthenticationMethod clientAuthenticationMethod, @Nullable Object credentials) { super(Collections.emptyList()); Assert.notNull(registeredClient, "registeredClient cannot be null"); Assert.notNull(clientAuthenticationMethod, "clientAuthenticationMethod cannot be null"); @@ -102,9 +108,10 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken } /** - * Returns the authenticated {@link RegisteredClient registered client}, or {@code null} if not authenticated. - * - * @return the authenticated {@link RegisteredClient}, or {@code null} if not authenticated + * Returns the authenticated {@link RegisteredClient registered client}, or + * {@code null} if not authenticated. + * @return the authenticated {@link RegisteredClient}, or {@code null} if not + * authenticated */ @Nullable public RegisteredClient getRegisteredClient() { @@ -112,8 +119,8 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken } /** - * Returns the {@link ClientAuthenticationMethod authentication method} used by the client. - * + * Returns the {@link ClientAuthenticationMethod authentication method} used by the + * client. * @return the {@link ClientAuthenticationMethod} used by the client */ public ClientAuthenticationMethod getClientAuthenticationMethod() { @@ -122,7 +129,6 @@ public class OAuth2ClientAuthenticationToken extends AbstractAuthenticationToken /** * Returns the additional parameters. - * * @return the additional parameters */ public Map getAdditionalParameters() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java index dc0fcff1..c8fcf167 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java @@ -46,7 +46,8 @@ import org.springframework.util.CollectionUtils; import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient; /** - * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials Grant. + * An {@link AuthenticationProvider} implementation for the OAuth 2.0 Client Credentials + * Grant. * * @author Alexey Nesterov * @author Joe Grandja @@ -55,18 +56,26 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2AccessTokenAuthenticationToken * @see OAuth2AuthorizationService * @see OAuth2TokenGenerator - * @see Section 4.4 Client Credentials Grant - * @see Section 4.4.2 Access Token Request + * @see Section 4.4 Client + * Credentials Grant + * @see Section 4.4.2 Access + * Token Request */ public final class OAuth2ClientCredentialsAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; /** - * Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the + * provided parameters. * @param authorizationService the authorization service * @param tokenGenerator the token generator * @since 0.2.3 @@ -81,11 +90,10 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = - (OAuth2ClientCredentialsAuthenticationToken) authentication; + OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = (OAuth2ClientCredentialsAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(clientCredentialsAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + clientCredentialsAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (this.logger.isTraceEnabled()) { @@ -144,9 +152,11 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth .authorizedScopes(authorizedScopes); // @formatter:on if (generatedAccessToken instanceof ClaimAccessor) { - authorizationBuilder.token(accessToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims())); - } else { + authorizationBuilder.token(accessToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims())); + } + else { authorizationBuilder.accessToken(accessToken); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java index 83849489..59ef7d3a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationToken.java @@ -25,7 +25,8 @@ import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.AuthorizationGrantType; /** - * An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials Grant. + * An {@link Authentication} implementation used for the OAuth 2.0 Client Credentials + * Grant. * * @author Alexey Nesterov * @since 0.0.1 @@ -33,28 +34,28 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType; * @see OAuth2ClientCredentialsAuthenticationProvider */ public class OAuth2ClientCredentialsAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { + private final Set scopes; /** - * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2ClientCredentialsAuthenticationToken} using the provided + * parameters. * @param clientPrincipal the authenticated client principal * @param scopes the requested scope(s) * @param additionalParameters the additional parameters */ - public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, - @Nullable Set scopes, @Nullable Map additionalParameters) { + public OAuth2ClientCredentialsAuthenticationToken(Authentication clientPrincipal, @Nullable Set scopes, + @Nullable Map additionalParameters) { super(AuthorizationGrantType.CLIENT_CREDENTIALS, clientPrincipal, additionalParameters); - this.scopes = Collections.unmodifiableSet( - scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); } /** * Returns the requested scope(s). - * * @return the requested scope(s), or an empty {@code Set} if not available */ public Set getScopes() { return this.scopes; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java index a3cc37aa..38f15c7b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProvider.java @@ -64,20 +64,23 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationConsentService authorizationConsentService; + private Consumer authorizationConsentCustomizer; /** - * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationProvider} using + * the provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service * @param authorizationConsentService the authorization consent service */ - public OAuth2DeviceAuthorizationConsentAuthenticationProvider( - RegisteredClientRepository registeredClientRepository, + public OAuth2DeviceAuthorizationConsentAuthenticationProvider(RegisteredClientRepository registeredClientRepository, OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); @@ -90,11 +93,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication; + OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication; - OAuth2Authorization authorization = this.authorizationService.findByToken( - deviceAuthorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(deviceAuthorizationConsentAuthentication.getState(), STATE_TOKEN_TYPE); if (authorization == null) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE); } @@ -109,8 +111,8 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE); } - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId( - deviceAuthorizationConsentAuthentication.getClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findByClientId(deviceAuthorizationConsentAuthentication.getClientId()); if (registeredClient == null || !registeredClient.getId().equals(authorization.getRegisteredClientId())) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID); } @@ -129,10 +131,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem this.logger.trace("Validated device authorization consent request parameters"); } - OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById( - authorization.getRegisteredClientId(), principal.getName()); - Set currentAuthorizedScopes = currentAuthorizationConsent != null ? - currentAuthorizationConsent.getScopes() : Collections.emptySet(); + OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService + .findById(authorization.getRegisteredClientId(), principal.getName()); + Set currentAuthorizedScopes = currentAuthorizationConsent != null + ? currentAuthorizationConsent.getScopes() : Collections.emptySet(); if (!currentAuthorizedScopes.isEmpty()) { for (String requestedScope : requestedScopes) { @@ -148,9 +150,10 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem this.logger.trace("Retrieved existing authorization consent"); } authorizationConsentBuilder = OAuth2AuthorizationConsent.from(currentAuthorizationConsent); - } else { - authorizationConsentBuilder = OAuth2AuthorizationConsent.withId( - authorization.getRegisteredClientId(), principal.getName()); + } + else { + authorizationConsentBuilder = OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), + principal.getName()); } authorizedScopes.forEach(authorizationConsentBuilder::scope); @@ -184,12 +187,12 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem } } authorization = OAuth2Authorization.from(authorization) - .token(deviceCodeToken.getToken(), metadata -> - metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .token(userCodeToken.getToken(), metadata -> - metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE)) - .build(); + .token(deviceCodeToken.getToken(), + metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .token(userCodeToken.getToken(), + metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE)) + .build(); this.authorizationService.save(authorization); if (this.logger.isTraceEnabled()) { this.logger.trace("Invalidated device code and user code because authorization consent was denied"); @@ -206,12 +209,12 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem } authorization = OAuth2Authorization.from(authorization) - .authorizedScopes(authorizedScopes) - .token(userCodeToken.getToken(), metadata -> - metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE)) - .attributes(attrs -> attrs.remove(OAuth2ParameterNames.SCOPE)) - .build(); + .authorizedScopes(authorizedScopes) + .token(userCodeToken.getToken(), + metadata -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .attributes(attrs -> attrs.remove(OAuth2ParameterNames.STATE)) + .attributes(attrs -> attrs.remove(OAuth2ParameterNames.SCOPE)) + .build(); this.authorizationService.save(authorization); if (this.logger.isTraceEnabled()) { @@ -230,33 +233,36 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationProvider implem } /** - * Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationConsentAuthenticationContext} - * containing an {@link OAuth2AuthorizationConsent.Builder} and additional context information. + * Sets the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an + * {@link OAuth2AuthorizationConsent.Builder} and additional context information. * *

* The following context attributes are available: *

    - *
  • The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization consent - * prior to {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.
  • + *
  • The {@link OAuth2AuthorizationConsent.Builder} used to build the authorization + * consent prior to + * {@link OAuth2AuthorizationConsentService#save(OAuth2AuthorizationConsent)}.
  • *
  • The {@link Authentication} of type * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}.
  • - *
  • The {@link RegisteredClient} associated with the device authorization request.
  • - *
  • The {@link OAuth2Authorization} associated with the state token presented in the - * device authorization consent request.
  • + *
  • The {@link RegisteredClient} associated with the device authorization + * request.
  • + *
  • The {@link OAuth2Authorization} associated with the state token presented in + * the device authorization consent request.
  • *
- * * @param authorizationConsentCustomizer the {@code Consumer} providing access to the - * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an {@link OAuth2AuthorizationConsent.Builder} + * {@link OAuth2AuthorizationConsentAuthenticationContext} containing an + * {@link OAuth2AuthorizationConsent.Builder} */ - public void setAuthorizationConsentCustomizer(Consumer authorizationConsentCustomizer) { + public void setAuthorizationConsentCustomizer( + Consumer authorizationConsentCustomizer) { Assert.notNull(authorizationConsentCustomizer, "authorizationConsentCustomizer cannot be null"); this.authorizationConsentCustomizer = authorizationConsentCustomizer; } private static boolean isPrincipalAuthenticated(Authentication principal) { - return principal != null && - !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) && - principal.isAuthenticated(); + return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) + && principal.isAuthenticated(); } private static void throwError(String errorCode, String parameterName) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java index fbfe3d35..12d34272 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationToken.java @@ -27,8 +27,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation for the Device Authorization Consent used - * in the OAuth 2.0 Device Authorization Grant. + * An {@link Authentication} implementation for the Device Authorization Consent used in + * the OAuth 2.0 Device Authorization Grant. * * @author Steve Riesenberg * @since 1.1 @@ -36,13 +36,16 @@ import org.springframework.util.Assert; * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider */ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2AuthorizationConsentAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String userCode; + private final Set requestedScopes; /** - * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the + * provided parameters. * @param authorizationUri the authorization URI * @param clientId the client identifier * @param principal the {@code Principal} (Resource Owner) @@ -62,8 +65,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A } /** - * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationConsentAuthenticationToken} using the + * provided parameters. * @param authorizationUri the authorization URI * @param clientId the client identifier * @param principal the {@code Principal} (Resource Owner) @@ -78,16 +81,13 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A super(authorizationUri, clientId, principal, state, authorizedScopes, null); Assert.hasText(userCode, "userCode cannot be empty"); this.userCode = userCode; - this.requestedScopes = Collections.unmodifiableSet( - requestedScopes != null ? - new HashSet<>(requestedScopes) : - Collections.emptySet()); + this.requestedScopes = Collections + .unmodifiableSet(requestedScopes != null ? new HashSet<>(requestedScopes) : Collections.emptySet()); setAuthenticated(true); } /** * Returns the user code. - * * @return the user code */ public String getUserCode() { @@ -96,7 +96,6 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationToken extends OAuth2A /** * Returns the requested scopes. - * * @return the requested scopes */ public Set getRequestedScopes() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java index d21ed9c4..610c7c46 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProvider.java @@ -63,8 +63,11 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2DeviceCodeAuthenticationProvider * @see OAuth2AuthorizationService * @see OAuth2TokenGenerator - * @see OAuth 2.0 Device Authorization Grant - * @see Section 3.1 Device Authorization Request + * @see OAuth 2.0 + * Device Authorization Grant + * @see Section 3.1 Device + * Authorization Request */ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implements AuthenticationProvider { @@ -73,13 +76,16 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE); private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private OAuth2TokenGenerator deviceCodeGenerator = new OAuth2DeviceCodeGenerator(); + private OAuth2TokenGenerator userCodeGenerator = new OAuth2UserCodeGenerator(); /** - * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationProvider} using + * the provided parameters. * @param authorizationService the authorization service */ public OAuth2DeviceAuthorizationRequestAuthenticationProvider(OAuth2AuthorizationService authorizationService) { @@ -89,11 +95,10 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication; + OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(deviceAuthorizationRequestAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + deviceAuthorizationRequestAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (this.logger.isTraceEnabled()) { @@ -171,8 +176,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem this.logger.trace("Authenticated device authorization request"); } - return new OAuth2DeviceAuthorizationRequestAuthenticationToken( - clientPrincipal, requestedScopes, deviceCode, userCode); + return new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, requestedScopes, deviceCode, + userCode); } @Override @@ -182,8 +187,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem /** * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2DeviceCode}. - * - * @param deviceCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2DeviceCode} + * @param deviceCodeGenerator the {@link OAuth2TokenGenerator} that generates the + * {@link OAuth2DeviceCode} */ public void setDeviceCodeGenerator(OAuth2TokenGenerator deviceCodeGenerator) { Assert.notNull(deviceCodeGenerator, "deviceCodeGenerator cannot be null"); @@ -192,8 +197,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem /** * Sets the {@link OAuth2TokenGenerator} that generates the {@link OAuth2UserCode}. - * - * @param userCodeGenerator the {@link OAuth2TokenGenerator} that generates the {@link OAuth2UserCode} + * @param userCodeGenerator the {@link OAuth2TokenGenerator} that generates the + * {@link OAuth2UserCode} */ public void setUserCodeGenerator(OAuth2TokenGenerator userCodeGenerator) { Assert.notNull(userCodeGenerator, "userCodeGenerator cannot be null"); @@ -207,18 +212,19 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem private static final class OAuth2DeviceCodeGenerator implements OAuth2TokenGenerator { - private final StringKeyGenerator deviceCodeGenerator = - new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + private final StringKeyGenerator deviceCodeGenerator = new Base64StringKeyGenerator( + Base64.getUrlEncoder().withoutPadding(), 96); @Nullable @Override public OAuth2DeviceCode generate(OAuth2TokenContext context) { - if (context.getTokenType() == null || - !OAuth2ParameterNames.DEVICE_CODE.equals(context.getTokenType().getValue())) { + if (context.getTokenType() == null + || !OAuth2ParameterNames.DEVICE_CODE.equals(context.getTokenType().getValue())) { return null; } Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive()); + Instant expiresAt = issuedAt + .plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive()); return new OAuth2DeviceCode(this.deviceCodeGenerator.generateKey(), issuedAt, expiresAt); } @@ -256,12 +262,13 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationProvider implem @Nullable @Override public OAuth2UserCode generate(OAuth2TokenContext context) { - if (context.getTokenType() == null || - !OAuth2ParameterNames.USER_CODE.equals(context.getTokenType().getValue())) { + if (context.getTokenType() == null + || !OAuth2ParameterNames.USER_CODE.equals(context.getTokenType().getValue())) { return null; } Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive()); + Instant expiresAt = issuedAt + .plus(context.getRegisteredClient().getTokenSettings().getDeviceCodeTimeToLive()); return new OAuth2UserCode(this.userCodeGenerator.generateKey(), issuedAt, expiresAt); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java index c80d9b64..7d4a7e91 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationToken.java @@ -30,8 +30,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation for the Device Authorization Request - * used in the OAuth 2.0 Device Authorization Grant. + * An {@link Authentication} implementation for the Device Authorization Request used in + * the OAuth 2.0 Device Authorization Grant. * * @author Steve Riesenberg * @since 1.1 @@ -40,17 +40,24 @@ import org.springframework.util.Assert; * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider */ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Authentication clientPrincipal; + private final String authorizationUri; + private final Set scopes; + private final OAuth2DeviceCode deviceCode; + private final OAuth2UserCode userCode; + private final Map additionalParameters; /** - * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the + * provided parameters. * @param clientPrincipal the authenticated client principal * @param authorizationUri the authorization {@code URI} * @param scopes the requested scope(s) @@ -63,37 +70,29 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac Assert.hasText(authorizationUri, "authorizationUri cannot be empty"); this.clientPrincipal = clientPrincipal; this.authorizationUri = authorizationUri; - this.scopes = Collections.unmodifiableSet( - scopes != null ? - new HashSet<>(scopes) : - Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? - new HashMap<>(additionalParameters) : - Collections.emptyMap()); + additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap()); this.deviceCode = null; this.userCode = null; } /** - * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationRequestAuthenticationToken} using the + * provided parameters. * @param clientPrincipal the authenticated client principal * @param scopes the requested scope(s) * @param deviceCode the {@link OAuth2DeviceCode} * @param userCode the {@link OAuth2UserCode} */ - public OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal, @Nullable Set scopes, - OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) { + public OAuth2DeviceAuthorizationRequestAuthenticationToken(Authentication clientPrincipal, + @Nullable Set scopes, OAuth2DeviceCode deviceCode, OAuth2UserCode userCode) { super(Collections.emptyList()); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); Assert.notNull(deviceCode, "deviceCode cannot be null"); Assert.notNull(userCode, "userCode cannot be null"); this.clientPrincipal = clientPrincipal; - this.scopes = Collections.unmodifiableSet( - scopes != null ? - new HashSet<>(scopes) : - Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); this.deviceCode = deviceCode; this.userCode = userCode; this.authorizationUri = null; @@ -113,7 +112,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac /** * Returns the authorization {@code URI}. - * * @return the authorization {@code URI} */ public String getAuthorizationUri() { @@ -122,7 +120,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac /** * Returns the requested scope(s). - * * @return the requested scope(s) */ public Set getScopes() { @@ -131,7 +128,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac /** * Returns the device code. - * * @return the device code */ public OAuth2DeviceCode getDeviceCode() { @@ -140,7 +136,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac /** * Returns the user code. - * * @return the user code */ public OAuth2UserCode getUserCode() { @@ -149,7 +144,6 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationToken extends Abstrac /** * Returns the additional parameters. - * * @return the additional parameters */ public Map getAdditionalParameters() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java index 5e08ea54..6e5b51be 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProvider.java @@ -60,30 +60,37 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider * @see OAuth2AuthorizationService * @see OAuth2TokenGenerator - * @see OAuth 2.0 Device Authorization Grant - * @see Section 3.4 Device Access Token Request - * @see Section 3.5 Device Access Token Response + * @see OAuth 2.0 + * Device Authorization Grant + * @see Section 3.4 Device Access + * Token Request + * @see Section 3.5 Device Access + * Token Response */ public final class OAuth2DeviceCodeAuthenticationProvider implements AuthenticationProvider { private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private static final String DEVICE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc8628#section-3.5"; static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE); static final String EXPIRED_TOKEN = "expired_token"; static final String AUTHORIZATION_PENDING = "authorization_pending"; private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; /** - * Constructs an {@code OAuth2DeviceCodeAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceCodeAuthenticationProvider} using the provided + * parameters. * @param authorizationService the authorization service * @param tokenGenerator the token generator */ - public OAuth2DeviceCodeAuthenticationProvider( - OAuth2AuthorizationService authorizationService, + public OAuth2DeviceCodeAuthenticationProvider(OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator tokenGenerator) { Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); @@ -93,19 +100,18 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication = - (OAuth2DeviceCodeAuthenticationToken) authentication; + OAuth2DeviceCodeAuthenticationToken deviceCodeAuthentication = (OAuth2DeviceCodeAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(deviceCodeAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + deviceCodeAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (this.logger.isTraceEnabled()) { this.logger.trace("Retrieved registered client"); } - OAuth2Authorization authorization = this.authorizationService.findByToken( - deviceCodeAuthentication.getDeviceCode(), DEVICE_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(deviceCodeAuthentication.getDeviceCode(), DEVICE_CODE_TOKEN_TYPE); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } @@ -119,12 +125,13 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat if (!registeredClient.getId().equals(authorization.getRegisteredClientId())) { if (!deviceCode.isInvalidated()) { - // Invalidate the device code given that a different client is attempting to use it + // Invalidate the device code given that a different client is attempting + // to use it authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken()); this.authorizationService.save(authorization); if (this.logger.isWarnEnabled()) { - this.logger.warn(LogMessage.format( - "Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId())); + this.logger.warn(LogMessage.format("Invalidated device code used by registered client '%s'", + authorization.getRegisteredClientId())); } } throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); @@ -133,46 +140,46 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat // In https://www.rfc-editor.org/rfc/rfc8628.html#section-3.5, // the following error codes are defined: - // authorization_pending - // The authorization request is still pending as the end user hasn't - // yet completed the user-interaction steps (Section 3.3). The - // client SHOULD repeat the access token request to the token - // endpoint (a process known as polling). Before each new request, - // the client MUST wait at least the number of seconds specified by - // the "interval" parameter of the device authorization response (see - // Section 3.2), or 5 seconds if none was provided, and respect any - // increase in the polling interval required by the "slow_down" - // error. + // authorization_pending + // The authorization request is still pending as the end user hasn't + // yet completed the user-interaction steps (Section 3.3). The + // client SHOULD repeat the access token request to the token + // endpoint (a process known as polling). Before each new request, + // the client MUST wait at least the number of seconds specified by + // the "interval" parameter of the device authorization response (see + // Section 3.2), or 5 seconds if none was provided, and respect any + // increase in the polling interval required by the "slow_down" + // error. if (!userCode.isInvalidated()) { OAuth2Error error = new OAuth2Error(AUTHORIZATION_PENDING, null, DEVICE_ERROR_URI); throw new OAuth2AuthenticationException(error); } - // slow_down - // A variant of "authorization_pending", the authorization request is - // still pending and polling should continue, but the interval MUST - // be increased by 5 seconds for this and all subsequent requests. - // NOTE: This error is not handled in the framework. + // slow_down + // A variant of "authorization_pending", the authorization request is + // still pending and polling should continue, but the interval MUST + // be increased by 5 seconds for this and all subsequent requests. + // NOTE: This error is not handled in the framework. - // access_denied - // The authorization request was denied. + // access_denied + // The authorization request was denied. if (deviceCode.isInvalidated()) { OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.ACCESS_DENIED, null, DEVICE_ERROR_URI); throw new OAuth2AuthenticationException(error); } - // expired_token - // The "device_code" has expired, and the device authorization - // session has concluded. The client MAY commence a new device - // authorization request but SHOULD wait for user interaction before - // restarting to avoid unnecessary polling. + // expired_token + // The "device_code" has expired, and the device authorization + // session has concluded. The client MAY commence a new device + // authorization request but SHOULD wait for user interaction before + // restarting to avoid unnecessary polling. if (deviceCode.isExpired()) { // Invalidate the device code authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, deviceCode.getToken()); this.authorizationService.save(authorization); if (this.logger.isWarnEnabled()) { - this.logger.warn(LogMessage.format( - "Invalidated device code used by registered client '%s'", authorization.getRegisteredClientId())); + this.logger.warn(LogMessage.format("Invalidated device code used by registered client '%s'", + authorization.getRegisteredClientId())); } OAuth2Error error = new OAuth2Error(EXPIRED_TOKEN, null, DEVICE_ERROR_URI); throw new OAuth2AuthenticationException(error); @@ -217,9 +224,11 @@ public final class OAuth2DeviceCodeAuthenticationProvider implements Authenticat generatedAccessToken.getTokenValue(), generatedAccessToken.getIssuedAt(), generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); if (generatedAccessToken instanceof ClaimAccessor) { - authorizationBuilder.token(accessToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims())); - } else { + authorizationBuilder.token(accessToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims())); + } + else { authorizationBuilder.accessToken(accessToken); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java index 29f7cfdf..5ca6196a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationToken.java @@ -23,8 +23,8 @@ import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.util.Assert; /** - * An {@link Authentication} implementation for the Device Access Token Request - * used in the OAuth 2.0 Device Authorization Grant. + * An {@link Authentication} implementation for the Device Access Token Request used in + * the OAuth 2.0 Device Authorization Grant. * * @author Steve Riesenberg * @since 1.1 @@ -36,8 +36,8 @@ public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGran private final String deviceCode; /** - * Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceCodeAuthenticationToken} using the provided + * parameters. * @param deviceCode the device code * @param clientPrincipal the authenticated client principal * @param additionalParameters the additional parameters @@ -51,7 +51,6 @@ public class OAuth2DeviceCodeAuthenticationToken extends OAuth2AuthorizationGran /** * Returns the device code. - * * @return the device code */ public String getDeviceCode() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java index d0b0c2e9..519dbe1a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProvider.java @@ -56,29 +56,35 @@ import org.springframework.util.Assert; * @see RegisteredClientRepository * @see OAuth2AuthorizationService * @see OAuth2AuthorizationConsentService - * @see OAuth 2.0 Device Authorization Grant - * @see Section 3.3 User Interaction + * @see OAuth 2.0 + * Device Authorization Grant + * @see Section 3.3 User + * Interaction */ public final class OAuth2DeviceVerificationAuthenticationProvider implements AuthenticationProvider { static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE); - private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = - new Base64StringKeyGenerator(Base64.getUrlEncoder()); + + private static final StringKeyGenerator DEFAULT_STATE_GENERATOR = new Base64StringKeyGenerator( + Base64.getUrlEncoder()); private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final OAuth2AuthorizationConsentService authorizationConsentService; /** - * Constructs an {@code OAuth2DeviceVerificationAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceVerificationAuthenticationProvider} using the + * provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service * @param authorizationConsentService the authorization consent service */ - public OAuth2DeviceVerificationAuthenticationProvider( - RegisteredClientRepository registeredClientRepository, + public OAuth2DeviceVerificationAuthenticationProvider(RegisteredClientRepository registeredClientRepository, OAuth2AuthorizationService authorizationService, OAuth2AuthorizationConsentService authorizationConsentService) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); @@ -91,11 +97,10 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = - (OAuth2DeviceVerificationAuthenticationToken) authentication; + OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = (OAuth2DeviceVerificationAuthenticationToken) authentication; - OAuth2Authorization authorization = this.authorizationService.findByToken( - deviceVerificationAuthentication.getUserCode(), USER_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(deviceVerificationAuthentication.getUserCode(), USER_CODE_TOKEN_TYPE); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } @@ -109,12 +114,13 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut if (this.logger.isTraceEnabled()) { this.logger.trace("Did not authenticate device verification request since principal not authenticated"); } - // Return the device verification request as-is where isAuthenticated() is false + // Return the device verification request as-is where isAuthenticated() is + // false return deviceVerificationAuthentication; } - RegisteredClient registeredClient = this.registeredClientRepository.findById( - authorization.getRegisteredClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findById(authorization.getRegisteredClientId()); if (this.logger.isTraceEnabled()) { this.logger.trace("Retrieved registered client"); @@ -122,16 +128,16 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut Set requestedScopes = authorization.getAttribute(OAuth2ParameterNames.SCOPE); - OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService.findById( - registeredClient.getId(), principal.getName()); + OAuth2AuthorizationConsent currentAuthorizationConsent = this.authorizationConsentService + .findById(registeredClient.getId(), principal.getName()); if (requiresAuthorizationConsent(requestedScopes, currentAuthorizationConsent)) { String state = DEFAULT_STATE_GENERATOR.generateKey(); authorization = OAuth2Authorization.from(authorization) - .principalName(principal.getName()) - .attribute(Principal.class.getName(), principal) - .attribute(OAuth2ParameterNames.STATE, state) - .build(); + .principalName(principal.getName()) + .attribute(Principal.class.getName(), principal) + .attribute(OAuth2ParameterNames.STATE, state) + .build(); if (this.logger.isTraceEnabled()) { this.logger.trace("Generated device authorization consent state"); @@ -143,11 +149,11 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut this.logger.trace("Saved authorization"); } - Set currentAuthorizedScopes = currentAuthorizationConsent != null ? - currentAuthorizationConsent.getScopes() : null; + Set currentAuthorizedScopes = currentAuthorizationConsent != null + ? currentAuthorizationConsent.getScopes() : null; - AuthorizationServerSettings authorizationServerSettings = - AuthorizationServerContextHolder.getContext().getAuthorizationServerSettings(); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerContextHolder.getContext() + .getAuthorizationServerSettings(); String deviceVerificationUri = authorizationServerSettings.getDeviceVerificationEndpoint(); return new OAuth2DeviceAuthorizationConsentAuthenticationToken(deviceVerificationUri, @@ -183,11 +189,10 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut return OAuth2DeviceVerificationAuthenticationToken.class.isAssignableFrom(authentication); } - private static boolean requiresAuthorizationConsent( - Set requestedScopes, OAuth2AuthorizationConsent authorizationConsent) { + private static boolean requiresAuthorizationConsent(Set requestedScopes, + OAuth2AuthorizationConsent authorizationConsent) { - if (authorizationConsent != null && - authorizationConsent.getScopes().containsAll(requestedScopes)) { + if (authorizationConsent != null && authorizationConsent.getScopes().containsAll(requestedScopes)) { return false; } @@ -195,9 +200,8 @@ public final class OAuth2DeviceVerificationAuthenticationProvider implements Aut } private static boolean isPrincipalAuthenticated(Authentication principal) { - return principal != null && - !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) && - principal.isAuthenticated(); + return principal != null && !AnonymousAuthenticationToken.class.isAssignableFrom(principal.getClass()) + && principal.isAuthenticated(); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java index 40d40de2..28f08f43 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationToken.java @@ -35,15 +35,20 @@ import org.springframework.util.Assert; * @see OAuth2DeviceVerificationAuthenticationProvider */ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Authentication principal; + private final String userCode; + private final Map additionalParameters; + private final String clientId; /** - * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the + * provided parameters. * @param principal the {@code Principal} (Resource Owner) * @param userCode the user code associated with the device authorization response * @param additionalParameters the additional parameters @@ -56,15 +61,13 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent this.principal = principal; this.userCode = userCode; this.additionalParameters = Collections.unmodifiableMap( - additionalParameters != null ? - new HashMap<>(additionalParameters) : - Collections.emptyMap()); + additionalParameters != null ? new HashMap<>(additionalParameters) : Collections.emptyMap()); this.clientId = null; } /** - * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceVerificationAuthenticationToken} using the + * provided parameters. * @param principal the {@code Principal} (Resource Owner) * @param userCode the user code associated with the device authorization response * @param clientId the client identifier @@ -93,7 +96,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent /** * Returns the user code. - * * @return the user code */ public String getUserCode() { @@ -102,7 +104,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent /** * Returns the additional parameters. - * * @return the additional parameters, or an empty {@code Map} if not available */ public Map getAdditionalParameters() { @@ -111,7 +112,6 @@ public class OAuth2DeviceVerificationAuthenticationToken extends AbstractAuthent /** * Returns the client identifier. - * * @return the client identifier */ public String getClientId() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java index 72440957..7f255b93 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProvider.java @@ -62,19 +62,28 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2AccessTokenAuthenticationToken * @see OAuth2AuthorizationService * @see OAuth2TokenGenerator - * @see Section 1.5 Refresh Token Grant - * @see Section 6 Refreshing an Access Token + * @see Section 1.5 Refresh Token + * Grant + * @see Section 6 Refreshing an + * Access Token */ public final class OAuth2RefreshTokenAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; /** - * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2RefreshTokenAuthenticationProvider} using the provided + * parameters. * @param authorizationService the authorization service * @param tokenGenerator the token generator * @since 0.2.3 @@ -89,19 +98,18 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication = - (OAuth2RefreshTokenAuthenticationToken) authentication; + OAuth2RefreshTokenAuthenticationToken refreshTokenAuthentication = (OAuth2RefreshTokenAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(refreshTokenAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + refreshTokenAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); if (this.logger.isTraceEnabled()) { this.logger.trace("Retrieved registered client"); } - OAuth2Authorization authorization = this.authorizationService.findByToken( - refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN); + OAuth2Authorization authorization = this.authorizationService + .findByToken(refreshTokenAuthentication.getRefreshToken(), OAuth2TokenType.REFRESH_TOKEN); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } @@ -122,13 +130,16 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic if (!refreshToken.isActive()) { // As per https://tools.ietf.org/html/rfc6749#section-5.2 // invalid_grant: The provided authorization grant (e.g., authorization code, - // resource owner credentials) or refresh token is invalid, expired, revoked [...]. + // resource owner credentials) or refresh token is invalid, expired, revoked + // [...]. throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_GRANT); } // As per https://tools.ietf.org/html/rfc6749#section-6 - // The requested scope MUST NOT include any scope not originally granted by the resource owner, - // and if omitted is treated as equal to the scope originally granted by the resource owner. + // The requested scope MUST NOT include any scope not originally granted by the + // resource owner, + // and if omitted is treated as equal to the scope originally granted by the + // resource owner. Set scopes = refreshTokenAuthentication.getScopes(); Set authorizedScopes = authorization.getAuthorizedScopes(); if (!authorizedScopes.containsAll(scopes)) { @@ -174,10 +185,12 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic generatedAccessToken.getExpiresAt(), tokenContext.getAuthorizedScopes()); if (generatedAccessToken instanceof ClaimAccessor) { authorizationBuilder.token(accessToken, (metadata) -> { - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) generatedAccessToken).getClaims()); + metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) generatedAccessToken).getClaims()); metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, false); }); - } else { + } + else { authorizationBuilder.accessToken(accessToken); } @@ -222,9 +235,10 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic idToken = new OidcIdToken(generatedIdToken.getTokenValue(), generatedIdToken.getIssuedAt(), generatedIdToken.getExpiresAt(), ((Jwt) generatedIdToken).getClaims()); - authorizationBuilder.token(idToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); - } else { + authorizationBuilder.token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())); + } + else { idToken = null; } @@ -246,8 +260,8 @@ public final class OAuth2RefreshTokenAuthenticationProvider implements Authentic this.logger.trace("Authenticated token request"); } - return new OAuth2AccessTokenAuthenticationToken( - registeredClient, clientPrincipal, accessToken, currentRefreshToken, additionalParameters); + return new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken, + currentRefreshToken, additionalParameters); } @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java index 26ce4ad0..19209c4a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationToken.java @@ -34,12 +34,14 @@ import org.springframework.util.Assert; * @see OAuth2RefreshTokenAuthenticationProvider */ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGrantAuthenticationToken { + private final String refreshToken; + private final Set scopes; /** - * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2RefreshTokenAuthenticationToken} using the provided + * parameters. * @param refreshToken the refresh token * @param clientPrincipal the authenticated client principal * @param scopes the requested scope(s) @@ -50,13 +52,11 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr super(AuthorizationGrantType.REFRESH_TOKEN, clientPrincipal, additionalParameters); Assert.hasText(refreshToken, "refreshToken cannot be empty"); this.refreshToken = refreshToken; - this.scopes = Collections.unmodifiableSet( - scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); + this.scopes = Collections.unmodifiableSet(scopes != null ? new HashSet<>(scopes) : Collections.emptySet()); } /** * Returns the refresh token. - * * @return the refresh token */ public String getRefreshToken() { @@ -65,10 +65,10 @@ public class OAuth2RefreshTokenAuthenticationToken extends OAuth2AuthorizationGr /** * Returns the requested scope(s). - * * @return the requested scope(s), or an empty {@code Set} if not available */ public Set getScopes() { return this.scopes; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProvider.java index d1a4f80b..15ad6f05 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProvider.java @@ -50,19 +50,25 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @see OAuth2TokenIntrospectionAuthenticationToken * @see RegisteredClientRepository * @see OAuth2AuthorizationService - * @see Section 2.1 Introspection Request + * @see Section + * 2.1 Introspection Request */ public final class OAuth2TokenIntrospectionAuthenticationProvider implements AuthenticationProvider { + private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); - private static final TypeDescriptor LIST_STRING_TYPE_DESCRIPTOR = - TypeDescriptor.collection(List.class, TypeDescriptor.valueOf(String.class)); + + private static final TypeDescriptor LIST_STRING_TYPE_DESCRIPTOR = TypeDescriptor.collection(List.class, + TypeDescriptor.valueOf(String.class)); + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; /** - * Constructs an {@code OAuth2TokenIntrospectionAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2TokenIntrospectionAuthenticationProvider} using the + * provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service */ @@ -76,14 +82,13 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = - (OAuth2TokenIntrospectionAuthenticationToken) authentication; + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = (OAuth2TokenIntrospectionAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(tokenIntrospectionAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + tokenIntrospectionAuthentication); - OAuth2Authorization authorization = this.authorizationService.findByToken( - tokenIntrospectionAuthentication.getToken(), null); + OAuth2Authorization authorization = this.authorizationService + .findByToken(tokenIntrospectionAuthentication.getToken(), null); if (authorization == null) { if (this.logger.isTraceEnabled()) { this.logger.trace("Did not authenticate token introspection request since token was not found"); @@ -96,8 +101,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut this.logger.trace("Retrieved authorization with token"); } - OAuth2Authorization.Token authorizedToken = - authorization.getToken(tokenIntrospectionAuthentication.getToken()); + OAuth2Authorization.Token authorizedToken = authorization + .getToken(tokenIntrospectionAuthentication.getToken()); if (!authorizedToken.isActive()) { if (this.logger.isTraceEnabled()) { this.logger.trace("Did not introspect token since not active"); @@ -106,7 +111,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut clientPrincipal, OAuth2TokenIntrospection.builder().build()); } - RegisteredClient authorizedClient = this.registeredClientRepository.findById(authorization.getRegisteredClientId()); + RegisteredClient authorizedClient = this.registeredClientRepository + .findById(authorization.getRegisteredClientId()); OAuth2TokenIntrospection tokenClaims = withActiveTokenClaims(authorizedToken, authorizedClient); if (this.logger.isTraceEnabled()) { @@ -129,7 +135,8 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut if (!CollectionUtils.isEmpty(authorizedToken.getClaims())) { Map claims = convertClaimsIfNecessary(authorizedToken.getClaims()); tokenClaims = OAuth2TokenIntrospection.withClaims(claims).active(true); - } else { + } + else { tokenClaims = OAuth2TokenIntrospection.builder(true); } @@ -158,8 +165,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut Object value = claims.get(OAuth2TokenIntrospectionClaimNames.ISS); if (value != null && !(value instanceof URL)) { - URL convertedValue = ClaimConversionService.getSharedInstance() - .convert(value, URL.class); + URL convertedValue = ClaimConversionService.getSharedInstance().convert(value, URL.class); if (convertedValue != null) { convertedClaims.put(OAuth2TokenIntrospectionClaimNames.ISS, convertedValue); } @@ -168,7 +174,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut value = claims.get(OAuth2TokenIntrospectionClaimNames.SCOPE); if (value != null && !(value instanceof List)) { Object convertedValue = ClaimConversionService.getSharedInstance() - .convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR); + .convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR); if (convertedValue != null) { convertedClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, convertedValue); } @@ -177,7 +183,7 @@ public final class OAuth2TokenIntrospectionAuthenticationProvider implements Aut value = claims.get(OAuth2TokenIntrospectionClaimNames.AUD); if (value != null && !(value instanceof List)) { Object convertedValue = ClaimConversionService.getSharedInstance() - .convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR); + .convert(value, OBJECT_TYPE_DESCRIPTOR, LIST_STRING_TYPE_DESCRIPTOR); if (convertedValue != null) { convertedClaims.put(OAuth2TokenIntrospectionClaimNames.AUD, convertedValue); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationToken.java index c8692ccc..eb97771a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationToken.java @@ -37,16 +37,22 @@ import org.springframework.util.Assert; * @see OAuth2TokenIntrospectionAuthenticationProvider */ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String token; + private final Authentication clientPrincipal; + private final String tokenTypeHint; + private final Map additionalParameters; + private final OAuth2TokenIntrospection tokenClaims; /** - * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the + * provided parameters. * @param token the token * @param clientPrincipal the authenticated client principal * @param tokenTypeHint the token type hint @@ -66,8 +72,8 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent } /** - * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2TokenIntrospectionAuthenticationToken} using the + * provided parameters. * @param token the token * @param clientPrincipal the authenticated client principal * @param tokenClaims the token claims @@ -83,7 +89,8 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent this.tokenTypeHint = null; this.additionalParameters = Collections.emptyMap(); this.tokenClaims = tokenClaims; - // Indicates that the request was authenticated, even though the token might not be active + // Indicates that the request was authenticated, even though the token might not + // be active setAuthenticated(true); } @@ -99,7 +106,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent /** * Returns the token. - * * @return the token */ public String getToken() { @@ -108,7 +114,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent /** * Returns the token type hint. - * * @return the token type hint */ @Nullable @@ -118,7 +123,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent /** * Returns the additional parameters. - * * @return the additional parameters */ public Map getAdditionalParameters() { @@ -127,7 +131,6 @@ public class OAuth2TokenIntrospectionAuthenticationToken extends AbstractAuthent /** * Returns the token claims. - * * @return the {@link OAuth2TokenIntrospection} */ public OAuth2TokenIntrospection getTokenClaims() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java index 4e18aa7c..7f6699b9 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProvider.java @@ -39,15 +39,18 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @since 0.0.3 * @see OAuth2TokenRevocationAuthenticationToken * @see OAuth2AuthorizationService - * @see Section 2.1 Revocation Request + * @see Section + * 2.1 Revocation Request */ public final class OAuth2TokenRevocationAuthenticationProvider implements AuthenticationProvider { + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; /** - * Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OAuth2TokenRevocationAuthenticationProvider} using the + * provided parameters. * @param authorizationService the authorization service */ public OAuth2TokenRevocationAuthenticationProvider(OAuth2AuthorizationService authorizationService) { @@ -57,15 +60,14 @@ public final class OAuth2TokenRevocationAuthenticationProvider implements Authen @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = - (OAuth2TokenRevocationAuthenticationToken) authentication; + OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = (OAuth2TokenRevocationAuthenticationToken) authentication; - OAuth2ClientAuthenticationToken clientPrincipal = - getAuthenticatedClientElseThrowInvalidClient(tokenRevocationAuthentication); + OAuth2ClientAuthenticationToken clientPrincipal = getAuthenticatedClientElseThrowInvalidClient( + tokenRevocationAuthentication); RegisteredClient registeredClient = clientPrincipal.getRegisteredClient(); - OAuth2Authorization authorization = this.authorizationService.findByToken( - tokenRevocationAuthentication.getToken(), null); + OAuth2Authorization authorization = this.authorizationService + .findByToken(tokenRevocationAuthentication.getToken(), null); if (authorization == null) { if (this.logger.isTraceEnabled()) { this.logger.trace("Did not authenticate token revocation request since token was not found"); @@ -95,4 +97,5 @@ public final class OAuth2TokenRevocationAuthenticationProvider implements Authen public boolean supports(Class authentication) { return OAuth2TokenRevocationAuthenticationToken.class.isAssignableFrom(authentication); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java index cfa3e0d1..8f987ea2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationToken.java @@ -34,20 +34,24 @@ import org.springframework.util.Assert; * @see OAuth2TokenRevocationAuthenticationProvider */ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String token; + private final Authentication clientPrincipal; + private final String tokenTypeHint; /** - * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided + * parameters. * @param token the token * @param clientPrincipal the authenticated client principal * @param tokenTypeHint the token type hint */ - public OAuth2TokenRevocationAuthenticationToken(String token, - Authentication clientPrincipal, @Nullable String tokenTypeHint) { + public OAuth2TokenRevocationAuthenticationToken(String token, Authentication clientPrincipal, + @Nullable String tokenTypeHint) { super(Collections.emptyList()); Assert.hasText(token, "token cannot be empty"); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); @@ -57,20 +61,19 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica } /** - * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OAuth2TokenRevocationAuthenticationToken} using the provided + * parameters. * @param revokedToken the revoked token * @param clientPrincipal the authenticated client principal */ - public OAuth2TokenRevocationAuthenticationToken(OAuth2Token revokedToken, - Authentication clientPrincipal) { + public OAuth2TokenRevocationAuthenticationToken(OAuth2Token revokedToken, Authentication clientPrincipal) { super(Collections.emptyList()); Assert.notNull(revokedToken, "revokedToken cannot be null"); Assert.notNull(clientPrincipal, "clientPrincipal cannot be null"); this.token = revokedToken.getTokenValue(); this.clientPrincipal = clientPrincipal; this.tokenTypeHint = null; - setAuthenticated(true); // Indicates that the token was authenticated and revoked + setAuthenticated(true); // Indicates that the token was authenticated and revoked } @Override @@ -85,7 +88,6 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica /** * Returns the token. - * * @return the token */ public String getToken() { @@ -94,11 +96,11 @@ public class OAuth2TokenRevocationAuthenticationToken extends AbstractAuthentica /** * Returns the token type hint. - * * @return the token type hint */ @Nullable public String getTokenTypeHint() { return this.tokenTypeHint; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java index 6e624302..075cf72c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProvider.java @@ -33,8 +33,9 @@ import org.springframework.security.oauth2.server.authorization.client.Registere import org.springframework.util.Assert; /** - * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Public Client Authentication, - * which authenticates the {@link PkceParameterNames#CODE_VERIFIER code_verifier} parameter. + * An {@link AuthenticationProvider} implementation used for OAuth 2.0 Public Client + * Authentication, which authenticates the {@link PkceParameterNames#CODE_VERIFIER + * code_verifier} parameter. * * @author Joe Grandja * @since 0.2.3 @@ -44,14 +45,18 @@ import org.springframework.util.Assert; * @see OAuth2AuthorizationService */ public final class PublicClientAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-3.2.1"; + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final CodeVerifierAuthenticator codeVerifierAuthenticator; /** - * Constructs a {@code PublicClientAuthenticationProvider} using the provided parameters. - * + * Constructs a {@code PublicClientAuthenticationProvider} using the provided + * parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service */ @@ -65,8 +70,7 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OAuth2ClientAuthenticationToken clientAuthentication = - (OAuth2ClientAuthenticationToken) authentication; + OAuth2ClientAuthenticationToken clientAuthentication = (OAuth2ClientAuthenticationToken) authentication; if (!ClientAuthenticationMethod.NONE.equals(clientAuthentication.getClientAuthenticationMethod())) { return null; @@ -82,8 +86,8 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP this.logger.trace("Retrieved registered client"); } - if (!registeredClient.getClientAuthenticationMethods().contains( - clientAuthentication.getClientAuthenticationMethod())) { + if (!registeredClient.getClientAuthenticationMethods() + .contains(clientAuthentication.getClientAuthenticationMethod())) { throwInvalidClient("authentication_method"); } @@ -108,11 +112,8 @@ public final class PublicClientAuthenticationProvider implements AuthenticationP } private static void throwInvalidClient(String parameterName) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_CLIENT, - "Client authentication failed: " + parameterName, - ERROR_URI - ); + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_CLIENT, + "Client authentication failed: " + parameterName, ERROR_URI); throw new OAuth2AuthenticationException(error); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepository.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepository.java index 65492dac..e230b358 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepository.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepository.java @@ -28,7 +28,8 @@ import org.springframework.util.StringUtils; * A {@link RegisteredClientRepository} that stores {@link RegisteredClient}(s) in-memory. * *

- * NOTE: This implementation is recommended ONLY to be used during development/testing. + * NOTE: This implementation is recommended ONLY to be used during + * development/testing. * * @author Anoop Garlapati * @author Ovidiu Popa @@ -38,12 +39,14 @@ import org.springframework.util.StringUtils; * @since 0.0.1 */ public final class InMemoryRegisteredClientRepository implements RegisteredClientRepository { + private final Map idRegistrationMap; + private final Map clientIdRegistrationMap; /** - * Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters. - * + * Constructs an {@code InMemoryRegisteredClientRepository} using the provided + * parameters. * @param registrations the client registration(s) */ public InMemoryRegisteredClientRepository(RegisteredClient... registrations) { @@ -51,8 +54,8 @@ public final class InMemoryRegisteredClientRepository implements RegisteredClien } /** - * Constructs an {@code InMemoryRegisteredClientRepository} using the provided parameters. - * + * Constructs an {@code InMemoryRegisteredClientRepository} using the provided + * parameters. * @param registrations the client registration(s) */ public InMemoryRegisteredClientRepository(List registrations) { @@ -93,20 +96,21 @@ public final class InMemoryRegisteredClientRepository implements RegisteredClien return this.clientIdRegistrationMap.get(clientId); } - private void assertUniqueIdentifiers(RegisteredClient registeredClient, Map registrations) { + private void assertUniqueIdentifiers(RegisteredClient registeredClient, + Map registrations) { registrations.values().forEach(registration -> { if (registeredClient.getId().equals(registration.getId())) { - throw new IllegalArgumentException("Registered client must be unique. " + - "Found duplicate identifier: " + registeredClient.getId()); + throw new IllegalArgumentException("Registered client must be unique. " + "Found duplicate identifier: " + + registeredClient.getId()); } if (registeredClient.getClientId().equals(registration.getClientId())) { - throw new IllegalArgumentException("Registered client must be unique. " + - "Found duplicate client identifier: " + registeredClient.getClientId()); + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client identifier: " + registeredClient.getClientId()); } - if (StringUtils.hasText(registeredClient.getClientSecret()) && - registeredClient.getClientSecret().equals(registration.getClientSecret())) { - throw new IllegalArgumentException("Registered client must be unique. " + - "Found duplicate client secret for identifier: " + registeredClient.getId()); + if (StringUtils.hasText(registeredClient.getClientSecret()) + && registeredClient.getClientSecret().equals(registration.getClientSecret())) { + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client secret for identifier: " + registeredClient.getId()); } }); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java index c5d7416e..9b6d7cf1 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepository.java @@ -56,15 +56,18 @@ import org.springframework.util.StringUtils; * {@link JdbcOperations} for {@link RegisteredClient} persistence. * *

- * IMPORTANT: This {@code RegisteredClientRepository} depends on the table definition described in - * "classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql" and - * therefore MUST be defined in the database schema. + * IMPORTANT: This {@code RegisteredClientRepository} depends on the table + * definition described in + * "classpath:org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql" + * and therefore MUST be defined in the database schema. * *

- * NOTE: This {@code RegisteredClientRepository} is a simplified JDBC implementation that MAY be used in a production environment. - * However, it does have limitations as it likely won't perform well in an environment requiring high throughput. - * The expectation is that the consuming application will provide their own implementation of {@code RegisteredClientRepository} - * that meets the performance requirements for its deployment environment. + * NOTE: This {@code RegisteredClientRepository} is a simplified JDBC + * implementation that MAY be used in a production environment. However, it does have + * limitations as it likely won't perform well in an environment requiring high + * throughput. The expectation is that the consuming application will provide their own + * implementation of {@code RegisteredClientRepository} that meets the performance + * requirements for its deployment environment. * * @author Rafal Lewczuk * @author Joe Grandja @@ -109,7 +112,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor private static final String PK_FILTER = "id = ?"; - private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + " WHERE "; + private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + + " WHERE "; // @formatter:off private static final String INSERT_REGISTERED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME @@ -127,12 +131,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor private static final String COUNT_REGISTERED_CLIENT_SQL = "SELECT COUNT(*) FROM " + TABLE_NAME + " WHERE "; private final JdbcOperations jdbcOperations; + private RowMapper registeredClientRowMapper; + private Function> registeredClientParametersMapper; /** * Constructs a {@code JdbcRegisteredClientRepository} using the provided parameters. - * * @param jdbcOperations the JDBC operations */ public JdbcRegisteredClientRepository(JdbcOperations jdbcOperations) { @@ -145,17 +150,18 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor @Override public void save(RegisteredClient registeredClient) { Assert.notNull(registeredClient, "registeredClient cannot be null"); - RegisteredClient existingRegisteredClient = findBy(PK_FILTER, - registeredClient.getId()); + RegisteredClient existingRegisteredClient = findBy(PK_FILTER, registeredClient.getId()); if (existingRegisteredClient != null) { updateRegisteredClient(registeredClient); - } else { + } + else { insertRegisteredClient(registeredClient); } } private void updateRegisteredClient(RegisteredClient registeredClient) { - List parameters = new ArrayList<>(this.registeredClientParametersMapper.apply(registeredClient)); + List parameters = new ArrayList<>( + this.registeredClientParametersMapper.apply(registeredClient)); SqlParameterValue id = parameters.remove(0); parameters.remove(0); // remove client_id parameters.remove(0); // remove client_id_issued_at @@ -172,21 +178,17 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor } private void assertUniqueIdentifiers(RegisteredClient registeredClient) { - Integer count = this.jdbcOperations.queryForObject( - COUNT_REGISTERED_CLIENT_SQL + "client_id = ?", - Integer.class, + Integer count = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + "client_id = ?", Integer.class, registeredClient.getClientId()); if (count != null && count > 0) { - throw new IllegalArgumentException("Registered client must be unique. " + - "Found duplicate client identifier: " + registeredClient.getClientId()); + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client identifier: " + registeredClient.getClientId()); } - count = this.jdbcOperations.queryForObject( - COUNT_REGISTERED_CLIENT_SQL + "client_secret = ?", - Integer.class, + count = this.jdbcOperations.queryForObject(COUNT_REGISTERED_CLIENT_SQL + "client_secret = ?", Integer.class, registeredClient.getClientSecret()); if (count != null && count > 0) { - throw new IllegalArgumentException("Registered client must be unique. " + - "Found duplicate client secret for identifier: " + registeredClient.getId()); + throw new IllegalArgumentException("Registered client must be unique. " + + "Found duplicate client secret for identifier: " + registeredClient.getId()); } } @@ -203,16 +205,17 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor } private RegisteredClient findBy(String filter, Object... args) { - List result = this.jdbcOperations.query( - LOAD_REGISTERED_CLIENT_SQL + filter, this.registeredClientRowMapper, args); + List result = this.jdbcOperations.query(LOAD_REGISTERED_CLIENT_SQL + filter, + this.registeredClientRowMapper, args); return !result.isEmpty() ? result.get(0) : null; } /** - * Sets the {@link RowMapper} used for mapping the current row in {@code java.sql.ResultSet} to {@link RegisteredClient}. - * The default is {@link RegisteredClientRowMapper}. - * - * @param registeredClientRowMapper the {@link RowMapper} used for mapping the current row in {@code ResultSet} to {@link RegisteredClient} + * Sets the {@link RowMapper} used for mapping the current row in + * {@code java.sql.ResultSet} to {@link RegisteredClient}. The default is + * {@link RegisteredClientRowMapper}. + * @param registeredClientRowMapper the {@link RowMapper} used for mapping the current + * row in {@code ResultSet} to {@link RegisteredClient} */ public final void setRegisteredClientRowMapper(RowMapper registeredClientRowMapper) { Assert.notNull(registeredClientRowMapper, "registeredClientRowMapper cannot be null"); @@ -220,12 +223,14 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor } /** - * Sets the {@code Function} used for mapping {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue}. - * The default is {@link RegisteredClientParametersMapper}. - * - * @param registeredClientParametersMapper the {@code Function} used for mapping {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue} + * Sets the {@code Function} used for mapping {@link RegisteredClient} to a + * {@code List} of {@link SqlParameterValue}. The default is + * {@link RegisteredClientParametersMapper}. + * @param registeredClientParametersMapper the {@code Function} used for mapping + * {@link RegisteredClient} to a {@code List} of {@link SqlParameterValue} */ - public final void setRegisteredClientParametersMapper(Function> registeredClientParametersMapper) { + public final void setRegisteredClientParametersMapper( + Function> registeredClientParametersMapper) { Assert.notNull(registeredClientParametersMapper, "registeredClientParametersMapper cannot be null"); this.registeredClientParametersMapper = registeredClientParametersMapper; } @@ -247,6 +252,7 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor * {@code java.sql.ResultSet} to {@link RegisteredClient}. */ public static class RegisteredClientRowMapper implements RowMapper { + private ObjectMapper objectMapper = new ObjectMapper(); public RegisteredClientRowMapper() { @@ -260,10 +266,13 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException { Timestamp clientIdIssuedAt = rs.getTimestamp("client_id_issued_at"); Timestamp clientSecretExpiresAt = rs.getTimestamp("client_secret_expires_at"); - Set clientAuthenticationMethods = StringUtils.commaDelimitedListToSet(rs.getString("client_authentication_methods")); - Set authorizationGrantTypes = StringUtils.commaDelimitedListToSet(rs.getString("authorization_grant_types")); + Set clientAuthenticationMethods = StringUtils + .commaDelimitedListToSet(rs.getString("client_authentication_methods")); + Set authorizationGrantTypes = StringUtils + .commaDelimitedListToSet(rs.getString("authorization_grant_types")); Set redirectUris = StringUtils.commaDelimitedListToSet(rs.getString("redirect_uris")); - Set postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(rs.getString("post_logout_redirect_uris")); + Set postLogoutRedirectUris = StringUtils + .commaDelimitedListToSet(rs.getString("post_logout_redirect_uris")); Set clientScopes = StringUtils.commaDelimitedListToSet(rs.getString("scopes")); // @formatter:off @@ -308,8 +317,10 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor private Map parseMap(String data) { try { - return this.objectMapper.readValue(data, new TypeReference>() {}); - } catch (Exception ex) { + return this.objectMapper.readValue(data, new TypeReference>() { + }); + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } @@ -317,32 +328,40 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) { if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.AUTHORIZATION_CODE; - } else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) { + } + else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.CLIENT_CREDENTIALS; - } else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) { + } + else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.REFRESH_TOKEN; } - return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type + // Custom authorization grant type + return new AuthorizationGrantType(authorizationGrantType); } private static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) { if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.CLIENT_SECRET_BASIC; - } else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) { + } + else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.CLIENT_SECRET_POST; - } else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) { + } + else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.NONE; } - return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method + // Custom client authentication method + return new ClientAuthenticationMethod(clientAuthenticationMethod); } } /** - * The default {@code Function} that maps {@link RegisteredClient} to a - * {@code List} of {@link SqlParameterValue}. + * The default {@code Function} that maps {@link RegisteredClient} to a {@code List} + * of {@link SqlParameterValue}. */ - public static class RegisteredClientParametersMapper implements Function> { + public static class RegisteredClientParametersMapper + implements Function> { + private ObjectMapper objectMapper = new ObjectMapper(); public RegisteredClientParametersMapper() { @@ -354,32 +373,39 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor @Override public List apply(RegisteredClient registeredClient) { - Timestamp clientIdIssuedAt = registeredClient.getClientIdIssuedAt() != null ? - Timestamp.from(registeredClient.getClientIdIssuedAt()) : Timestamp.from(Instant.now()); + Timestamp clientIdIssuedAt = registeredClient.getClientIdIssuedAt() != null + ? Timestamp.from(registeredClient.getClientIdIssuedAt()) : Timestamp.from(Instant.now()); - Timestamp clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt() != null ? - Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null; + Timestamp clientSecretExpiresAt = registeredClient.getClientSecretExpiresAt() != null + ? Timestamp.from(registeredClient.getClientSecretExpiresAt()) : null; - List clientAuthenticationMethods = new ArrayList<>(registeredClient.getClientAuthenticationMethods().size()); - registeredClient.getClientAuthenticationMethods().forEach(clientAuthenticationMethod -> - clientAuthenticationMethods.add(clientAuthenticationMethod.getValue())); + List clientAuthenticationMethods = new ArrayList<>( + registeredClient.getClientAuthenticationMethods().size()); + registeredClient.getClientAuthenticationMethods() + .forEach(clientAuthenticationMethod -> clientAuthenticationMethods + .add(clientAuthenticationMethod.getValue())); - List authorizationGrantTypes = new ArrayList<>(registeredClient.getAuthorizationGrantTypes().size()); - registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType -> - authorizationGrantTypes.add(authorizationGrantType.getValue())); + List authorizationGrantTypes = new ArrayList<>( + registeredClient.getAuthorizationGrantTypes().size()); + registeredClient.getAuthorizationGrantTypes() + .forEach(authorizationGrantType -> authorizationGrantTypes.add(authorizationGrantType.getValue())); - return Arrays.asList( - new SqlParameterValue(Types.VARCHAR, registeredClient.getId()), + return Arrays.asList(new SqlParameterValue(Types.VARCHAR, registeredClient.getId()), new SqlParameterValue(Types.VARCHAR, registeredClient.getClientId()), new SqlParameterValue(Types.TIMESTAMP, clientIdIssuedAt), new SqlParameterValue(Types.VARCHAR, registeredClient.getClientSecret()), new SqlParameterValue(Types.TIMESTAMP, clientSecretExpiresAt), new SqlParameterValue(Types.VARCHAR, registeredClient.getClientName()), - new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)), - new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)), - new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())), - new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())), - new SqlParameterValue(Types.VARCHAR, StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())), + new SqlParameterValue(Types.VARCHAR, + StringUtils.collectionToCommaDelimitedString(clientAuthenticationMethods)), + new SqlParameterValue(Types.VARCHAR, + StringUtils.collectionToCommaDelimitedString(authorizationGrantTypes)), + new SqlParameterValue(Types.VARCHAR, + StringUtils.collectionToCommaDelimitedString(registeredClient.getRedirectUris())), + new SqlParameterValue(Types.VARCHAR, + StringUtils.collectionToCommaDelimitedString(registeredClient.getPostLogoutRedirectUris())), + new SqlParameterValue(Types.VARCHAR, + StringUtils.collectionToCommaDelimitedString(registeredClient.getScopes())), new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getClientSettings().getSettings())), new SqlParameterValue(Types.VARCHAR, writeMap(registeredClient.getTokenSettings().getSettings()))); } @@ -396,7 +422,8 @@ public class JdbcRegisteredClientRepository implements RegisteredClientRepositor private String writeMap(Map data) { try { return this.objectMapper.writeValueAsString(data); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java index d03afac0..f21e2851 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClient.java @@ -40,23 +40,38 @@ import org.springframework.util.StringUtils; * * @author Joe Grandja * @author Anoop Garlapati - * @see Section 2 Client Registration + * @see Section 2 + * Client Registration * @since 0.0.1 */ public class RegisteredClient implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private String id; + private String clientId; + private Instant clientIdIssuedAt; + private String clientSecret; + private Instant clientSecretExpiresAt; + private String clientName; + private Set clientAuthenticationMethods; + private Set authorizationGrantTypes; + private Set redirectUris; + private Set postLogoutRedirectUris; + private Set scopes; + private ClientSettings clientSettings; + private TokenSettings tokenSettings; protected RegisteredClient() { @@ -64,7 +79,6 @@ public class RegisteredClient implements Serializable { /** * Returns the identifier for the registration. - * * @return the identifier for the registration */ public String getId() { @@ -73,7 +87,6 @@ public class RegisteredClient implements Serializable { /** * Returns the client identifier. - * * @return the client identifier */ public String getClientId() { @@ -82,7 +95,6 @@ public class RegisteredClient implements Serializable { /** * Returns the time at which the client identifier was issued. - * * @return the time at which the client identifier was issued */ @Nullable @@ -92,7 +104,6 @@ public class RegisteredClient implements Serializable { /** * Returns the client secret or {@code null} if not available. - * * @return the client secret or {@code null} if not available */ @Nullable @@ -101,9 +112,10 @@ public class RegisteredClient implements Serializable { } /** - * Returns the time at which the client secret expires or {@code null} if it does not expire. - * - * @return the time at which the client secret expires or {@code null} if it does not expire + * Returns the time at which the client secret expires or {@code null} if it does not + * expire. + * @return the time at which the client secret expires or {@code null} if it does not + * expire */ @Nullable public Instant getClientSecretExpiresAt() { @@ -112,7 +124,6 @@ public class RegisteredClient implements Serializable { /** * Returns the client name. - * * @return the client name */ public String getClientName() { @@ -120,18 +131,20 @@ public class RegisteredClient implements Serializable { } /** - * Returns the {@link ClientAuthenticationMethod authentication method(s)} that the client may use. - * - * @return the {@code Set} of {@link ClientAuthenticationMethod authentication method(s)} + * Returns the {@link ClientAuthenticationMethod authentication method(s)} that the + * client may use. + * @return the {@code Set} of {@link ClientAuthenticationMethod authentication + * method(s)} */ public Set getClientAuthenticationMethods() { return this.clientAuthenticationMethods; } /** - * Returns the {@link AuthorizationGrantType authorization grant type(s)} that the client may use. - * - * @return the {@code Set} of {@link AuthorizationGrantType authorization grant type(s)} + * Returns the {@link AuthorizationGrantType authorization grant type(s)} that the + * client may use. + * @return the {@code Set} of {@link AuthorizationGrantType authorization grant + * type(s)} */ public Set getAuthorizationGrantTypes() { return this.authorizationGrantTypes; @@ -139,7 +152,6 @@ public class RegisteredClient implements Serializable { /** * Returns the redirect URI(s) that the client may use in redirect-based flows. - * * @return the {@code Set} of redirect URI(s) */ public Set getRedirectUris() { @@ -147,10 +159,9 @@ public class RegisteredClient implements Serializable { } /** - * Returns the post logout redirect URI(s) that the client may use for logout. - * The {@code post_logout_redirect_uri} parameter is used by the client when requesting + * Returns the post logout redirect URI(s) that the client may use for logout. The + * {@code post_logout_redirect_uri} parameter is used by the client when requesting * that the End-User's User Agent be redirected to after a logout has been performed. - * * @return the {@code Set} of post logout redirect URI(s) * @since 1.1 */ @@ -160,7 +171,6 @@ public class RegisteredClient implements Serializable { /** * Returns the scope(s) that the client may use. - * * @return the {@code Set} of scope(s) */ public Set getScopes() { @@ -169,7 +179,6 @@ public class RegisteredClient implements Serializable { /** * Returns the {@link ClientSettings client configuration settings}. - * * @return the {@link ClientSettings} */ public ClientSettings getClientSettings() { @@ -178,7 +187,6 @@ public class RegisteredClient implements Serializable { /** * Returns the {@link TokenSettings token configuration settings}. - * * @return the {@link TokenSettings} */ public TokenSettings getTokenSettings() { @@ -194,47 +202,39 @@ public class RegisteredClient implements Serializable { return false; } RegisteredClient that = (RegisteredClient) obj; - return Objects.equals(this.id, that.id) && - Objects.equals(this.clientId, that.clientId) && - Objects.equals(this.clientIdIssuedAt, that.clientIdIssuedAt) && - Objects.equals(this.clientSecret, that.clientSecret) && - Objects.equals(this.clientSecretExpiresAt, that.clientSecretExpiresAt) && - Objects.equals(this.clientName, that.clientName) && - Objects.equals(this.clientAuthenticationMethods, that.clientAuthenticationMethods) && - Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes) && - Objects.equals(this.redirectUris, that.redirectUris) && - Objects.equals(this.postLogoutRedirectUris, that.postLogoutRedirectUris) && - Objects.equals(this.scopes, that.scopes) && - Objects.equals(this.clientSettings, that.clientSettings) && - Objects.equals(this.tokenSettings, that.tokenSettings); + return Objects.equals(this.id, that.id) && Objects.equals(this.clientId, that.clientId) + && Objects.equals(this.clientIdIssuedAt, that.clientIdIssuedAt) + && Objects.equals(this.clientSecret, that.clientSecret) + && Objects.equals(this.clientSecretExpiresAt, that.clientSecretExpiresAt) + && Objects.equals(this.clientName, that.clientName) + && Objects.equals(this.clientAuthenticationMethods, that.clientAuthenticationMethods) + && Objects.equals(this.authorizationGrantTypes, that.authorizationGrantTypes) + && Objects.equals(this.redirectUris, that.redirectUris) + && Objects.equals(this.postLogoutRedirectUris, that.postLogoutRedirectUris) + && Objects.equals(this.scopes, that.scopes) && Objects.equals(this.clientSettings, that.clientSettings) + && Objects.equals(this.tokenSettings, that.tokenSettings); } @Override public int hashCode() { - return Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret, this.clientSecretExpiresAt, - this.clientName, this.clientAuthenticationMethods, this.authorizationGrantTypes, this.redirectUris, - this.postLogoutRedirectUris, this.scopes, this.clientSettings, this.tokenSettings); + return Objects.hash(this.id, this.clientId, this.clientIdIssuedAt, this.clientSecret, + this.clientSecretExpiresAt, this.clientName, this.clientAuthenticationMethods, + this.authorizationGrantTypes, this.redirectUris, this.postLogoutRedirectUris, this.scopes, + this.clientSettings, this.tokenSettings); } @Override public String toString() { - return "RegisteredClient {" + - "id='" + this.id + '\'' + - ", clientId='" + this.clientId + '\'' + - ", clientName='" + this.clientName + '\'' + - ", clientAuthenticationMethods=" + this.clientAuthenticationMethods + - ", authorizationGrantTypes=" + this.authorizationGrantTypes + - ", redirectUris=" + this.redirectUris + - ", postLogoutRedirectUris=" + this.postLogoutRedirectUris + - ", scopes=" + this.scopes + - ", clientSettings=" + this.clientSettings + - ", tokenSettings=" + this.tokenSettings + - '}'; + return "RegisteredClient {" + "id='" + this.id + '\'' + ", clientId='" + this.clientId + '\'' + ", clientName='" + + this.clientName + '\'' + ", clientAuthenticationMethods=" + this.clientAuthenticationMethods + + ", authorizationGrantTypes=" + this.authorizationGrantTypes + ", redirectUris=" + this.redirectUris + + ", postLogoutRedirectUris=" + this.postLogoutRedirectUris + ", scopes=" + this.scopes + + ", clientSettings=" + this.clientSettings + ", tokenSettings=" + this.tokenSettings + '}'; } /** - * Returns a new {@link Builder}, initialized with the provided registration identifier. - * + * Returns a new {@link Builder}, initialized with the provided registration + * identifier. * @param id the identifier for the registration * @return the {@link Builder} */ @@ -244,9 +244,10 @@ public class RegisteredClient implements Serializable { } /** - * Returns a new {@link Builder}, initialized with the values from the provided {@link RegisteredClient}. - * - * @param registeredClient the {@link RegisteredClient} used for initializing the {@link Builder} + * Returns a new {@link Builder}, initialized with the values from the provided + * {@link RegisteredClient}. + * @param registeredClient the {@link RegisteredClient} used for initializing the + * {@link Builder} * @return the {@link Builder} */ public static Builder from(RegisteredClient registeredClient) { @@ -258,19 +259,33 @@ public class RegisteredClient implements Serializable { * A builder for {@link RegisteredClient}. */ public static class Builder implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private String id; + private String clientId; + private Instant clientIdIssuedAt; + private String clientSecret; + private Instant clientSecretExpiresAt; + private String clientName; + private final Set clientAuthenticationMethods = new HashSet<>(); + private final Set authorizationGrantTypes = new HashSet<>(); + private final Set redirectUris = new HashSet<>(); + private final Set postLogoutRedirectUris = new HashSet<>(); + private final Set scopes = new HashSet<>(); + private ClientSettings clientSettings; + private TokenSettings tokenSettings; protected Builder(String id) { @@ -299,13 +314,13 @@ public class RegisteredClient implements Serializable { if (!CollectionUtils.isEmpty(registeredClient.getScopes())) { this.scopes.addAll(registeredClient.getScopes()); } - this.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings()).build(); + this.clientSettings = ClientSettings.withSettings(registeredClient.getClientSettings().getSettings()) + .build(); this.tokenSettings = TokenSettings.withSettings(registeredClient.getTokenSettings().getSettings()).build(); } /** * Sets the identifier for the registration. - * * @param id the identifier for the registration * @return the {@link Builder} */ @@ -316,7 +331,6 @@ public class RegisteredClient implements Serializable { /** * Sets the client identifier. - * * @param clientId the client identifier * @return the {@link Builder} */ @@ -327,7 +341,6 @@ public class RegisteredClient implements Serializable { /** * Sets the time at which the client identifier was issued. - * * @param clientIdIssuedAt the time at which the client identifier was issued * @return the {@link Builder} */ @@ -338,7 +351,6 @@ public class RegisteredClient implements Serializable { /** * Sets the client secret. - * * @param clientSecret the client secret * @return the {@link Builder} */ @@ -348,9 +360,10 @@ public class RegisteredClient implements Serializable { } /** - * Sets the time at which the client secret expires or {@code null} if it does not expire. - * - * @param clientSecretExpiresAt the time at which the client secret expires or {@code null} if it does not expire + * Sets the time at which the client secret expires or {@code null} if it does not + * expire. + * @param clientSecretExpiresAt the time at which the client secret expires or + * {@code null} if it does not expire * @return the {@link Builder} */ public Builder clientSecretExpiresAt(Instant clientSecretExpiresAt) { @@ -360,7 +373,6 @@ public class RegisteredClient implements Serializable { /** * Sets the client name. - * * @param clientName the client name * @return the {@link Builder} */ @@ -370,9 +382,8 @@ public class RegisteredClient implements Serializable { } /** - * Adds an {@link ClientAuthenticationMethod authentication method} - * the client may use when authenticating with the authorization server. - * + * Adds an {@link ClientAuthenticationMethod authentication method} the client may + * use when authenticating with the authorization server. * @param clientAuthenticationMethod the authentication method * @return the {@link Builder} */ @@ -382,10 +393,10 @@ public class RegisteredClient implements Serializable { } /** - * A {@code Consumer} of the {@link ClientAuthenticationMethod authentication method(s)} - * allowing the ability to add, replace, or remove. - * - * @param clientAuthenticationMethodsConsumer a {@code Consumer} of the authentication method(s) + * A {@code Consumer} of the {@link ClientAuthenticationMethod authentication + * method(s)} allowing the ability to add, replace, or remove. + * @param clientAuthenticationMethodsConsumer a {@code Consumer} of the + * authentication method(s) * @return the {@link Builder} */ public Builder clientAuthenticationMethods( @@ -395,8 +406,8 @@ public class RegisteredClient implements Serializable { } /** - * Adds an {@link AuthorizationGrantType authorization grant type} the client may use. - * + * Adds an {@link AuthorizationGrantType authorization grant type} the client may + * use. * @param authorizationGrantType the authorization grant type * @return the {@link Builder} */ @@ -406,10 +417,10 @@ public class RegisteredClient implements Serializable { } /** - * A {@code Consumer} of the {@link AuthorizationGrantType authorization grant type(s)} - * allowing the ability to add, replace, or remove. - * - * @param authorizationGrantTypesConsumer a {@code Consumer} of the authorization grant type(s) + * A {@code Consumer} of the {@link AuthorizationGrantType authorization grant + * type(s)} allowing the ability to add, replace, or remove. + * @param authorizationGrantTypesConsumer a {@code Consumer} of the authorization + * grant type(s) * @return the {@link Builder} */ public Builder authorizationGrantTypes(Consumer> authorizationGrantTypesConsumer) { @@ -419,7 +430,6 @@ public class RegisteredClient implements Serializable { /** * Adds a redirect URI the client may use in a redirect-based flow. - * * @param redirectUri the redirect URI * @return the {@link Builder} */ @@ -429,9 +439,8 @@ public class RegisteredClient implements Serializable { } /** - * A {@code Consumer} of the redirect URI(s) - * allowing the ability to add, replace, or remove. - * + * A {@code Consumer} of the redirect URI(s) allowing the ability to add, replace, + * or remove. * @param redirectUrisConsumer a {@link Consumer} of the redirect URI(s) * @return the {@link Builder} */ @@ -441,10 +450,10 @@ public class RegisteredClient implements Serializable { } /** - * Adds a post logout redirect URI the client may use for logout. - * The {@code post_logout_redirect_uri} parameter is used by the client when requesting - * that the End-User's User Agent be redirected to after a logout has been performed. - * + * Adds a post logout redirect URI the client may use for logout. The + * {@code post_logout_redirect_uri} parameter is used by the client when + * requesting that the End-User's User Agent be redirected to after a logout has + * been performed. * @param postLogoutRedirectUri the post logout redirect URI * @return the {@link Builder} * @since 1.1 @@ -455,10 +464,10 @@ public class RegisteredClient implements Serializable { } /** - * A {@code Consumer} of the post logout redirect URI(s) - * allowing the ability to add, replace, or remove. - * - * @param postLogoutRedirectUrisConsumer a {@link Consumer} of the post logout redirect URI(s) + * A {@code Consumer} of the post logout redirect URI(s) allowing the ability to + * add, replace, or remove. + * @param postLogoutRedirectUrisConsumer a {@link Consumer} of the post logout + * redirect URI(s) * @return the {@link Builder} * @since 1.1 */ @@ -469,7 +478,6 @@ public class RegisteredClient implements Serializable { /** * Adds a scope the client may use. - * * @param scope the scope * @return the {@link Builder} */ @@ -479,9 +487,8 @@ public class RegisteredClient implements Serializable { } /** - * A {@code Consumer} of the scope(s) - * allowing the ability to add, replace, or remove. - * + * A {@code Consumer} of the scope(s) allowing the ability to add, replace, or + * remove. * @param scopesConsumer a {@link Consumer} of the scope(s) * @return the {@link Builder} */ @@ -492,7 +499,6 @@ public class RegisteredClient implements Serializable { /** * Sets the {@link ClientSettings client configuration settings}. - * * @param clientSettings the client configuration settings * @return the {@link Builder} */ @@ -503,7 +509,6 @@ public class RegisteredClient implements Serializable { /** * Sets the {@link TokenSettings token configuration settings}. - * * @param tokenSettings the token configuration settings * @return the {@link Builder} */ @@ -514,7 +519,6 @@ public class RegisteredClient implements Serializable { /** * Builds a new {@link RegisteredClient}. - * * @return a {@link RegisteredClient} */ public RegisteredClient build() { @@ -550,9 +554,9 @@ public class RegisteredClient implements Serializable { } private boolean isPublicClientType() { - return this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE) && - this.clientAuthenticationMethods.size() == 1 && - this.clientAuthenticationMethods.contains(ClientAuthenticationMethod.NONE); + return this.authorizationGrantTypes.contains(AuthorizationGrantType.AUTHORIZATION_CODE) + && this.clientAuthenticationMethods.size() == 1 + && this.clientAuthenticationMethods.contains(ClientAuthenticationMethod.NONE); } private RegisteredClient create() { @@ -564,16 +568,14 @@ public class RegisteredClient implements Serializable { registeredClient.clientSecret = this.clientSecret; registeredClient.clientSecretExpiresAt = this.clientSecretExpiresAt; registeredClient.clientName = this.clientName; - registeredClient.clientAuthenticationMethods = Collections.unmodifiableSet( - new HashSet<>(this.clientAuthenticationMethods)); - registeredClient.authorizationGrantTypes = Collections.unmodifiableSet( - new HashSet<>(this.authorizationGrantTypes)); - registeredClient.redirectUris = Collections.unmodifiableSet( - new HashSet<>(this.redirectUris)); - registeredClient.postLogoutRedirectUris = Collections.unmodifiableSet( - new HashSet<>(this.postLogoutRedirectUris)); - registeredClient.scopes = Collections.unmodifiableSet( - new HashSet<>(this.scopes)); + registeredClient.clientAuthenticationMethods = Collections + .unmodifiableSet(new HashSet<>(this.clientAuthenticationMethods)); + registeredClient.authorizationGrantTypes = Collections + .unmodifiableSet(new HashSet<>(this.authorizationGrantTypes)); + registeredClient.redirectUris = Collections.unmodifiableSet(new HashSet<>(this.redirectUris)); + registeredClient.postLogoutRedirectUris = Collections + .unmodifiableSet(new HashSet<>(this.postLogoutRedirectUris)); + registeredClient.scopes = Collections.unmodifiableSet(new HashSet<>(this.scopes)); registeredClient.clientSettings = this.clientSettings; registeredClient.tokenSettings = this.tokenSettings; @@ -591,10 +593,9 @@ public class RegisteredClient implements Serializable { } private static boolean validateScope(String scope) { - return scope == null || - scope.chars().allMatch(c -> withinTheRangeOf(c, 0x21, 0x21) || - withinTheRangeOf(c, 0x23, 0x5B) || - withinTheRangeOf(c, 0x5D, 0x7E)); + return scope == null || scope.chars() + .allMatch(c -> withinTheRangeOf(c, 0x21, 0x21) || withinTheRangeOf(c, 0x23, 0x5B) + || withinTheRangeOf(c, 0x5D, 0x7E)); } private static boolean withinTheRangeOf(int c, int min, int max) { @@ -618,8 +619,8 @@ public class RegisteredClient implements Serializable { } for (String postLogoutRedirectUri : this.postLogoutRedirectUris) { - Assert.isTrue(validateRedirectUri(postLogoutRedirectUri), - "post_logout_redirect_uri \"" + postLogoutRedirectUri + "\" is not a valid post logout redirect URI or contains fragment"); + Assert.isTrue(validateRedirectUri(postLogoutRedirectUri), "post_logout_redirect_uri \"" + + postLogoutRedirectUri + "\" is not a valid post logout redirect URI or contains fragment"); } } @@ -627,10 +628,12 @@ public class RegisteredClient implements Serializable { try { URI validRedirectUri = new URI(redirectUri); return validRedirectUri.getFragment() == null; - } catch (URISyntaxException ex) { + } + catch (URISyntaxException ex) { return false; } } } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java index 00a00a5f..e9045852 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientRepository.java @@ -32,16 +32,15 @@ public interface RegisteredClientRepository { * Saves the registered client. * *

- * IMPORTANT: Sensitive information should be encoded externally from the implementation, e.g. {@link RegisteredClient#getClientSecret()} - * + * IMPORTANT: Sensitive information should be encoded externally from the + * implementation, e.g. {@link RegisteredClient#getClientSecret()} * @param registeredClient the {@link RegisteredClient} */ void save(RegisteredClient registeredClient); /** - * Returns the registered client identified by the provided {@code id}, - * or {@code null} if not found. - * + * Returns the registered client identified by the provided {@code id}, or + * {@code null} if not found. * @param id the registration identifier * @return the {@link RegisteredClient} if found, otherwise {@code null} */ @@ -49,9 +48,8 @@ public interface RegisteredClientRepository { RegisteredClient findById(String id); /** - * Returns the registered client identified by the provided {@code clientId}, - * or {@code null} if not found. - * + * Returns the registered client identified by the provided {@code clientId}, or + * {@code null} if not found. * @param clientId the client identifier * @return the {@link RegisteredClient} if found, otherwise {@code null} */ diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java index 56cc5939..2dbb7b67 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfiguration.java @@ -78,10 +78,10 @@ public class OAuth2AuthorizationServerConfiguration { jwsAlgs.addAll(JWSAlgorithm.Family.EC); jwsAlgs.addAll(JWSAlgorithm.Family.HMAC_SHA); ConfigurableJWTProcessor jwtProcessor = new DefaultJWTProcessor<>(); - JWSKeySelector jwsKeySelector = - new JWSVerificationKeySelector<>(jwsAlgs, jwkSource); + JWSKeySelector jwsKeySelector = new JWSVerificationKeySelector<>(jwsAlgs, jwkSource); jwtProcessor.setJWSKeySelector(jwsKeySelector); - // Override the default Nimbus claims set verifier as NimbusJwtDecoder handles it instead + // Override the default Nimbus claims set verifier as NimbusJwtDecoder handles it + // instead jwtProcessor.setJWTClaimsSetVerifier((claims, context) -> { }); return new NimbusJwtDecoder(jwtProcessor); @@ -90,7 +90,8 @@ public class OAuth2AuthorizationServerConfiguration { @Bean RegisterMissingBeanPostProcessor registerMissingBeanPostProcessor() { RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor(); - postProcessor.addBeanDefinition(AuthorizationServerSettings.class, () -> AuthorizationServerSettings.builder().build()); + postProcessor.addBeanDefinition(AuthorizationServerSettings.class, + () -> AuthorizationServerSettings.builder().build()); return postProcessor; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java index 202db7e5..8ca044fe 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessor.java @@ -33,14 +33,18 @@ import org.springframework.beans.factory.support.RootBeanDefinition; import org.springframework.context.annotation.AnnotationBeanNameGenerator; /** - * Post processor to register one or more bean definitions on container initialization, if not already present. + * Post processor to register one or more bean definitions on container initialization, if + * not already present. * * @author Steve Riesenberg * @since 0.2.0 */ final class RegisterMissingBeanPostProcessor implements BeanDefinitionRegistryPostProcessor, BeanFactoryAware { + private final AnnotationBeanNameGenerator beanNameGenerator = new AnnotationBeanNameGenerator(); + private final List beanDefinitions = new ArrayList<>(); + private BeanFactory beanFactory; @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AbstractOAuth2Configurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AbstractOAuth2Configurer.java index 56f128e0..ac5ca7ee 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AbstractOAuth2Configurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AbstractOAuth2Configurer.java @@ -26,6 +26,7 @@ import org.springframework.security.web.util.matcher.RequestMatcher; * @since 0.1.2 */ abstract class AbstractOAuth2Configurer { + private final ObjectPostProcessor objectPostProcessor; AbstractOAuth2Configurer(ObjectPostProcessor objectPostProcessor) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java index aa6cee69..3f7a4f3a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/AuthorizationServerContextFilter.java @@ -32,7 +32,8 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; /** - * A {@code Filter} that associates the {@link AuthorizationServerContext} to the {@link AuthorizationServerContextHolder}. + * A {@code Filter} that associates the {@link AuthorizationServerContext} to the + * {@link AuthorizationServerContextHolder}. * * @author Joe Grandja * @since 0.2.2 @@ -41,6 +42,7 @@ import org.springframework.web.util.UriComponentsBuilder; * @see AuthorizationServerSettings */ final class AuthorizationServerContextFilter extends OncePerRequestFilter { + private final AuthorizationServerSettings authorizationServerSettings; AuthorizationServerContextFilter(AuthorizationServerSettings authorizationServerSettings) { @@ -53,21 +55,20 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter { throws ServletException, IOException { try { - AuthorizationServerContext authorizationServerContext = - new DefaultAuthorizationServerContext( - () -> resolveIssuer(this.authorizationServerSettings, request), - this.authorizationServerSettings); + AuthorizationServerContext authorizationServerContext = new DefaultAuthorizationServerContext( + () -> resolveIssuer(this.authorizationServerSettings, request), this.authorizationServerSettings); AuthorizationServerContextHolder.setContext(authorizationServerContext); filterChain.doFilter(request, response); - } finally { + } + finally { AuthorizationServerContextHolder.resetContext(); } } - private static String resolveIssuer(AuthorizationServerSettings authorizationServerSettings, HttpServletRequest request) { - return authorizationServerSettings.getIssuer() != null ? - authorizationServerSettings.getIssuer() : - getContextPath(request); + private static String resolveIssuer(AuthorizationServerSettings authorizationServerSettings, + HttpServletRequest request) { + return authorizationServerSettings.getIssuer() != null ? authorizationServerSettings.getIssuer() + : getContextPath(request); } private static String getContextPath(HttpServletRequest request) { @@ -82,10 +83,13 @@ final class AuthorizationServerContextFilter extends OncePerRequestFilter { } private static final class DefaultAuthorizationServerContext implements AuthorizationServerContext { + private final Supplier issuerSupplier; + private final AuthorizationServerSettings authorizationServerSettings; - private DefaultAuthorizationServerContext(Supplier issuerSupplier, AuthorizationServerSettings authorizationServerSettings) { + private DefaultAuthorizationServerContext(Supplier issuerSupplier, + AuthorizationServerSettings authorizationServerSettings) { this.issuerSupplier = issuerSupplier; this.authorizationServerSettings = authorizationServerSettings; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java index e1ae33e9..d2fc4213 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationEndpointConfigurer.java @@ -60,15 +60,28 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationEndpointFilter */ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List authorizationRequestConverters = new ArrayList<>(); - private Consumer> authorizationRequestConvertersConsumer = (authorizationRequestConverters) -> {}; + + private Consumer> authorizationRequestConvertersConsumer = ( + authorizationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler authorizationResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; + private String consentPage; + private Consumer authorizationCodeRequestAuthenticationValidator; + private SessionAuthenticationStrategy sessionAuthenticationStrategy; /** @@ -79,25 +92,31 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or {@link OAuth2AuthorizationConsentAuthenticationToken} - * used for authenticating the request. - * - * @param authorizationRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} + * Adds an {@link AuthenticationConverter} used when attempting to extract an + * Authorization Request (or Consent) from {@link HttpServletRequest} to an instance + * of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or + * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the + * request. + * @param authorizationRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract an Authorization Request (or Consent) from + * {@link HttpServletRequest} * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration */ - public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter(AuthenticationConverter authorizationRequestConverter) { + public OAuth2AuthorizationEndpointConfigurer authorizationRequestConverter( + AuthenticationConverter authorizationRequestConverter) { Assert.notNull(authorizationRequestConverter, "authorizationRequestConverter cannot be null"); this.authorizationRequestConverters.add(authorizationRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param authorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authorizationRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param authorizationRequestConvertersConsumer the {@code Consumer} providing access + * to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -109,9 +128,10 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration */ public OAuth2AuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { @@ -121,11 +141,12 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -137,56 +158,62 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and returning the {@link OAuth2AuthorizationResponse Authorization Response}. - * - * @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and returning the + * {@link OAuth2AuthorizationResponse Authorization Response}. + * @param authorizationResponseHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration */ - public OAuth2AuthorizationEndpointConfigurer authorizationResponseHandler(AuthenticationSuccessHandler authorizationResponseHandler) { + public OAuth2AuthorizationEndpointConfigurer authorizationResponseHandler( + AuthenticationSuccessHandler authorizationResponseHandler) { this.authorizationResponseHandler = authorizationResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the + * {@link OAuth2Error Error Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration */ - public OAuth2AuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2AuthorizationEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } /** - * Specify the URI to redirect Resource Owners to if consent is required during - * the {@code authorization_code} flow. A default consent page will be generated when - * this attribute is not specified. + * Specify the URI to redirect Resource Owners to if consent is required during the + * {@code authorization_code} flow. A default consent page will be generated when this + * attribute is not specified. * - * If a URI is specified, applications are required to process the specified URI to generate - * a consent page. The query string will contain the following parameters: + * If a URI is specified, applications are required to process the specified URI to + * generate a consent page. The query string will contain the following parameters: * *
    *
  • {@code client_id} - the client identifier
  • - *
  • {@code scope} - a space-delimited list of scopes present in the authorization request
  • + *
  • {@code scope} - a space-delimited list of scopes present in the authorization + * request
  • *
  • {@code state} - a CSRF protection token
  • *
* - * In general, the consent page should create a form that submits - * a request with the following requirements: + * In general, the consent page should create a form that submits a request with the + * following requirements: * *
    *
  • It must be an HTTP POST
  • - *
  • It must be submitted to {@link AuthorizationServerSettings#getAuthorizationEndpoint()}
  • + *
  • It must be submitted to + * {@link AuthorizationServerSettings#getAuthorizationEndpoint()}
  • *
  • It must include the received {@code client_id} as an HTTP parameter
  • *
  • It must include the received {@code state} as an HTTP parameter
  • - *
  • It must include the list of {@code scope}s the {@code Resource Owner} - * consented to as an HTTP parameter
  • + *
  • It must include the list of {@code scope}s the {@code Resource Owner} consented + * to as an HTTP parameter
  • *
- * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") + * @param consentPage the URI of the custom consent page to redirect to if consent is + * required (e.g. "/oauth2/consent") * @return the {@link OAuth2AuthorizationEndpointConfigurer} for further configuration */ public OAuth2AuthorizationEndpointConfigurer consentPage(String consentPage) { @@ -196,10 +223,9 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C void addAuthorizationCodeRequestAuthenticationValidator( Consumer authenticationValidator) { - this.authorizationCodeRequestAuthenticationValidator = - this.authorizationCodeRequestAuthenticationValidator == null ? - authenticationValidator : - this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator); + this.authorizationCodeRequestAuthenticationValidator = this.authorizationCodeRequestAuthenticationValidator == null + ? authenticationValidator + : this.authorizationCodeRequestAuthenticationValidator.andThen(authenticationValidator); } void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) { @@ -208,13 +234,12 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher( - authorizationServerSettings.getAuthorizationEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getAuthorizationEndpoint(), HttpMethod.GET.name()), - new AntPathRequestMatcher( - authorizationServerSettings.getAuthorizationEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getAuthorizationEndpoint(), HttpMethod.POST.name())); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); @@ -222,26 +247,25 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = - new OAuth2AuthorizationEndpointFilter( - authenticationManager, - authorizationServerSettings.getAuthorizationEndpoint()); + OAuth2AuthorizationEndpointFilter authorizationEndpointFilter = new OAuth2AuthorizationEndpointFilter( + authenticationManager, authorizationServerSettings.getAuthorizationEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.authorizationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.authorizationRequestConverters); } this.authorizationRequestConvertersConsumer.accept(authenticationConverters); - authorizationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + authorizationEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.authorizationResponseHandler != null) { authorizationEndpointFilter.setAuthenticationSuccessHandler(this.authorizationResponseHandler); } @@ -254,7 +278,8 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C if (this.sessionAuthenticationStrategy != null) { authorizationEndpointFilter.setSessionAuthenticationStrategy(this.sessionAuthenticationStrategy); } - httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); + httpSecurity.addFilterBefore(postProcess(authorizationEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); } @Override @@ -274,23 +299,21 @@ public final class OAuth2AuthorizationEndpointConfigurer extends AbstractOAuth2C private List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = - new OAuth2AuthorizationCodeRequestAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); + OAuth2AuthorizationCodeRequestAuthenticationProvider authorizationCodeRequestAuthenticationProvider = new OAuth2AuthorizationCodeRequestAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); if (this.authorizationCodeRequestAuthenticationValidator != null) { - authorizationCodeRequestAuthenticationProvider.setAuthenticationValidator( - new OAuth2AuthorizationCodeRequestAuthenticationValidator() - .andThen(this.authorizationCodeRequestAuthenticationValidator)); + authorizationCodeRequestAuthenticationProvider + .setAuthenticationValidator(new OAuth2AuthorizationCodeRequestAuthenticationValidator() + .andThen(this.authorizationCodeRequestAuthenticationValidator)); } authenticationProviders.add(authorizationCodeRequestAuthenticationProvider); - OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = - new OAuth2AuthorizationConsentAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); + OAuth2AuthorizationConsentAuthenticationProvider authorizationConsentAuthenticationProvider = new OAuth2AuthorizationConsentAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationConsentService(httpSecurity)); authenticationProviders.add(authorizationConsentAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java index 1b04ac15..926d5eb5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerConfigurer.java @@ -84,16 +84,16 @@ public final class OAuth2AuthorizationServerConfigurer extends AbstractHttpConfigurer { private final Map, AbstractOAuth2Configurer> configurers = createConfigurers(); - private RequestMatcher endpointsMatcher; + private RequestMatcher endpointsMatcher; /** * Sets the repository of registered clients. - * * @param registeredClientRepository the repository of registered clients * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer registeredClientRepository(RegisteredClientRepository registeredClientRepository) { + public OAuth2AuthorizationServerConfigurer registeredClientRepository( + RegisteredClientRepository registeredClientRepository) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); getBuilder().setSharedObject(RegisteredClientRepository.class, registeredClientRepository); return this; @@ -101,7 +101,6 @@ public final class OAuth2AuthorizationServerConfigurer /** * Sets the authorization service. - * * @param authorizationService the authorization service * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ @@ -113,11 +112,11 @@ public final class OAuth2AuthorizationServerConfigurer /** * Sets the authorization consent service. - * * @param authorizationConsentService the authorization consent service * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer authorizationConsentService(OAuth2AuthorizationConsentService authorizationConsentService) { + public OAuth2AuthorizationServerConfigurer authorizationConsentService( + OAuth2AuthorizationConsentService authorizationConsentService) { Assert.notNull(authorizationConsentService, "authorizationConsentService cannot be null"); getBuilder().setSharedObject(OAuth2AuthorizationConsentService.class, authorizationConsentService); return this; @@ -125,11 +124,11 @@ public final class OAuth2AuthorizationServerConfigurer /** * Sets the authorization server settings. - * * @param authorizationServerSettings the authorization server settings * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer authorizationServerSettings(AuthorizationServerSettings authorizationServerSettings) { + public OAuth2AuthorizationServerConfigurer authorizationServerSettings( + AuthorizationServerSettings authorizationServerSettings) { Assert.notNull(authorizationServerSettings, "authorizationServerSettings cannot be null"); getBuilder().setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings); return this; @@ -137,12 +136,12 @@ public final class OAuth2AuthorizationServerConfigurer /** * Sets the token generator. - * * @param tokenGenerator the token generator * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 0.2.3 */ - public OAuth2AuthorizationServerConfigurer tokenGenerator(OAuth2TokenGenerator tokenGenerator) { + public OAuth2AuthorizationServerConfigurer tokenGenerator( + OAuth2TokenGenerator tokenGenerator) { Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); getBuilder().setSharedObject(OAuth2TokenGenerator.class, tokenGenerator); return this; @@ -150,101 +149,111 @@ public final class OAuth2AuthorizationServerConfigurer /** * Configures OAuth 2.0 Client Authentication. - * - * @param clientAuthenticationCustomizer the {@link Customizer} providing access to the {@link OAuth2ClientAuthenticationConfigurer} + * @param clientAuthenticationCustomizer the {@link Customizer} providing access to + * the {@link OAuth2ClientAuthenticationConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer clientAuthentication(Customizer clientAuthenticationCustomizer) { + public OAuth2AuthorizationServerConfigurer clientAuthentication( + Customizer clientAuthenticationCustomizer) { clientAuthenticationCustomizer.customize(getConfigurer(OAuth2ClientAuthenticationConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Authorization Server Metadata Endpoint. - * - * @param authorizationServerMetadataEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} + * @param authorizationServerMetadataEndpointCustomizer the {@link Customizer} + * providing access to the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 0.4.0 */ - public OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint(Customizer authorizationServerMetadataEndpointCustomizer) { - authorizationServerMetadataEndpointCustomizer.customize(getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class)); + public OAuth2AuthorizationServerConfigurer authorizationServerMetadataEndpoint( + Customizer authorizationServerMetadataEndpointCustomizer) { + authorizationServerMetadataEndpointCustomizer + .customize(getConfigurer(OAuth2AuthorizationServerMetadataEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Authorization Endpoint. - * - * @param authorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2AuthorizationEndpointConfigurer} + * @param authorizationEndpointCustomizer the {@link Customizer} providing access to + * the {@link OAuth2AuthorizationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer authorizationEndpoint(Customizer authorizationEndpointCustomizer) { + public OAuth2AuthorizationServerConfigurer authorizationEndpoint( + Customizer authorizationEndpointCustomizer) { authorizationEndpointCustomizer.customize(getConfigurer(OAuth2AuthorizationEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Token Endpoint. - * - * @param tokenEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenEndpointConfigurer} + * @param tokenEndpointCustomizer the {@link Customizer} providing access to the + * {@link OAuth2TokenEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ - public OAuth2AuthorizationServerConfigurer tokenEndpoint(Customizer tokenEndpointCustomizer) { + public OAuth2AuthorizationServerConfigurer tokenEndpoint( + Customizer tokenEndpointCustomizer) { tokenEndpointCustomizer.customize(getConfigurer(OAuth2TokenEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Token Introspection Endpoint. - * - * @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenIntrospectionEndpointConfigurer} + * @param tokenIntrospectionEndpointCustomizer the {@link Customizer} providing access + * to the {@link OAuth2TokenIntrospectionEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 0.2.3 */ - public OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint(Customizer tokenIntrospectionEndpointCustomizer) { + public OAuth2AuthorizationServerConfigurer tokenIntrospectionEndpoint( + Customizer tokenIntrospectionEndpointCustomizer) { tokenIntrospectionEndpointCustomizer.customize(getConfigurer(OAuth2TokenIntrospectionEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Token Revocation Endpoint. - * - * @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2TokenRevocationEndpointConfigurer} + * @param tokenRevocationEndpointCustomizer the {@link Customizer} providing access to + * the {@link OAuth2TokenRevocationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 0.2.2 */ - public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint(Customizer tokenRevocationEndpointCustomizer) { + public OAuth2AuthorizationServerConfigurer tokenRevocationEndpoint( + Customizer tokenRevocationEndpointCustomizer) { tokenRevocationEndpointCustomizer.customize(getConfigurer(OAuth2TokenRevocationEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Device Authorization Endpoint. - * - * @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer} + * @param deviceAuthorizationEndpointCustomizer the {@link Customizer} providing + * access to the {@link OAuth2DeviceAuthorizationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 1.1 */ - public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint(Customizer deviceAuthorizationEndpointCustomizer) { - deviceAuthorizationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class)); + public OAuth2AuthorizationServerConfigurer deviceAuthorizationEndpoint( + Customizer deviceAuthorizationEndpointCustomizer) { + deviceAuthorizationEndpointCustomizer + .customize(getConfigurer(OAuth2DeviceAuthorizationEndpointConfigurer.class)); return this; } /** * Configures the OAuth 2.0 Device Verification Endpoint. - * - * @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access to the {@link OAuth2DeviceVerificationEndpointConfigurer} + * @param deviceVerificationEndpointCustomizer the {@link Customizer} providing access + * to the {@link OAuth2DeviceVerificationEndpointConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration * @since 1.1 */ - public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint(Customizer deviceVerificationEndpointCustomizer) { + public OAuth2AuthorizationServerConfigurer deviceVerificationEndpoint( + Customizer deviceVerificationEndpointCustomizer) { deviceVerificationEndpointCustomizer.customize(getConfigurer(OAuth2DeviceVerificationEndpointConfigurer.class)); return this; } /** * Configures OpenID Connect 1.0 support (disabled by default). - * - * @param oidcCustomizer the {@link Customizer} providing access to the {@link OidcConfigurer} + * @param oidcCustomizer the {@link Customizer} providing access to the + * {@link OidcConfigurer} * @return the {@link OAuth2AuthorizationServerConfigurer} for further configuration */ public OAuth2AuthorizationServerConfigurer oidc(Customizer oidcCustomizer) { @@ -259,7 +268,6 @@ public final class OAuth2AuthorizationServerConfigurer /** * Returns a {@link RequestMatcher} for the authorization server endpoints. - * * @return a {@link RequestMatcher} for the authorization server endpoints */ public RequestMatcher getEndpointsMatcher() { @@ -270,42 +278,45 @@ public final class OAuth2AuthorizationServerConfigurer @Override public void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); validateAuthorizationServerSettings(authorizationServerSettings); if (isOidcEnabled()) { // Add OpenID Connect session tracking capabilities. initSessionRegistry(httpSecurity); SessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class); - OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = - getConfigurer(OAuth2AuthorizationEndpointConfigurer.class); + OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer( + OAuth2AuthorizationEndpointConfigurer.class); authorizationEndpointConfigurer.setSessionAuthenticationStrategy((authentication, request, response) -> { if (authentication instanceof OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication) { if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) { if (sessionRegistry.getSessionInformation(request.getSession().getId()) == null) { - sessionRegistry.registerNewSession( - request.getSession().getId(), - ((Authentication) authorizationCodeRequestAuthentication.getPrincipal()).getPrincipal()); + sessionRegistry.registerNewSession(request.getSession().getId(), + ((Authentication) authorizationCodeRequestAuthentication.getPrincipal()) + .getPrincipal()); } } } }); - } else { + } + else { // OpenID Connect is disabled. // Add an authentication validator that rejects authentication requests. - OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = - getConfigurer(OAuth2AuthorizationEndpointConfigurer.class); - authorizationEndpointConfigurer.addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> { - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authenticationContext.getAuthentication(); - if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) { - OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE, - "OpenID Connect 1.0 authentication requests are restricted.", - "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"); - throw new OAuth2AuthorizationCodeRequestAuthenticationException( - error, authorizationCodeRequestAuthentication); - } - }); + OAuth2AuthorizationEndpointConfigurer authorizationEndpointConfigurer = getConfigurer( + OAuth2AuthorizationEndpointConfigurer.class); + authorizationEndpointConfigurer + .addAuthorizationCodeRequestAuthenticationValidator((authenticationContext) -> { + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationContext + .getAuthentication(); + if (authorizationCodeRequestAuthentication.getScopes().contains(OidcScopes.OPENID)) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_SCOPE, + "OpenID Connect 1.0 authentication requests are restricted.", + "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"); + throw new OAuth2AuthorizationCodeRequestAuthenticationException(error, + authorizationCodeRequestAuthentication); + } + }); } List requestMatchers = new ArrayList<>(); @@ -313,20 +324,18 @@ public final class OAuth2AuthorizationServerConfigurer configurer.init(httpSecurity); requestMatchers.add(configurer.getRequestMatcher()); }); - requestMatchers.add(new AntPathRequestMatcher( - authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name())); + requestMatchers + .add(new AntPathRequestMatcher(authorizationServerSettings.getJwkSetEndpoint(), HttpMethod.GET.name())); this.endpointsMatcher = new OrRequestMatcher(requestMatchers); - ExceptionHandlingConfigurer exceptionHandling = httpSecurity.getConfigurer(ExceptionHandlingConfigurer.class); + ExceptionHandlingConfigurer exceptionHandling = httpSecurity + .getConfigurer(ExceptionHandlingConfigurer.class); if (exceptionHandling != null) { - exceptionHandling.defaultAuthenticationEntryPointFor( - new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), - new OrRequestMatcher( - getRequestMatcher(OAuth2TokenEndpointConfigurer.class), + exceptionHandling.defaultAuthenticationEntryPointFor(new HttpStatusEntryPoint(HttpStatus.UNAUTHORIZED), + new OrRequestMatcher(getRequestMatcher(OAuth2TokenEndpointConfigurer.class), getRequestMatcher(OAuth2TokenIntrospectionEndpointConfigurer.class), getRequestMatcher(OAuth2TokenRevocationEndpointConfigurer.class), - getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class)) - ); + getRequestMatcher(OAuth2DeviceAuthorizationEndpointConfigurer.class))); } } @@ -334,16 +343,19 @@ public final class OAuth2AuthorizationServerConfigurer public void configure(HttpSecurity httpSecurity) { this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity)); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter(authorizationServerSettings); + AuthorizationServerContextFilter authorizationServerContextFilter = new AuthorizationServerContextFilter( + authorizationServerSettings); httpSecurity.addFilterAfter(postProcess(authorizationServerContextFilter), SecurityContextHolderFilter.class); JWKSource jwkSource = OAuth2ConfigurerUtils.getJwkSource(httpSecurity); if (jwkSource != null) { - NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter( - jwkSource, authorizationServerSettings.getJwkSetEndpoint()); - httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); + NimbusJwkSetEndpointFilter jwkSetEndpointFilter = new NimbusJwkSetEndpointFilter(jwkSource, + authorizationServerSettings.getJwkSetEndpoint()); + httpSecurity.addFilterBefore(postProcess(jwkSetEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); } } @@ -353,14 +365,21 @@ public final class OAuth2AuthorizationServerConfigurer private Map, AbstractOAuth2Configurer> createConfigurers() { Map, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>(); - configurers.put(OAuth2ClientAuthenticationConfigurer.class, new OAuth2ClientAuthenticationConfigurer(this::postProcess)); - configurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class, new OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2AuthorizationEndpointConfigurer.class, new OAuth2AuthorizationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2ClientAuthenticationConfigurer.class, + new OAuth2ClientAuthenticationConfigurer(this::postProcess)); + configurers.put(OAuth2AuthorizationServerMetadataEndpointConfigurer.class, + new OAuth2AuthorizationServerMetadataEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2AuthorizationEndpointConfigurer.class, + new OAuth2AuthorizationEndpointConfigurer(this::postProcess)); configurers.put(OAuth2TokenEndpointConfigurer.class, new OAuth2TokenEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, new OAuth2TokenRevocationEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); - configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2TokenIntrospectionEndpointConfigurer.class, + new OAuth2TokenIntrospectionEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2TokenRevocationEndpointConfigurer.class, + new OAuth2TokenRevocationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2DeviceAuthorizationEndpointConfigurer.class, + new OAuth2DeviceAuthorizationEndpointConfigurer(this::postProcess)); + configurers.put(OAuth2DeviceVerificationEndpointConfigurer.class, + new OAuth2DeviceVerificationEndpointConfigurer(this::postProcess)); return configurers; } @@ -384,7 +403,8 @@ public final class OAuth2AuthorizationServerConfigurer try { issuerUri = new URI(authorizationServerSettings.getIssuer()); issuerUri.toURL(); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException("issuer must be a valid URL", ex); } // rfc8414 https://datatracker.ietf.org/doc/html/rfc8414#section-2 @@ -403,9 +423,10 @@ public final class OAuth2AuthorizationServerConfigurer httpSecurity.setSharedObject(SessionRegistry.class, sessionRegistry); } - private static void registerDelegateApplicationListener(HttpSecurity httpSecurity, ApplicationListener delegate) { - DelegatingApplicationListener delegatingApplicationListener = - OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, DelegatingApplicationListener.class); + private static void registerDelegateApplicationListener(HttpSecurity httpSecurity, + ApplicationListener delegate) { + DelegatingApplicationListener delegatingApplicationListener = OAuth2ConfigurerUtils + .getOptionalBean(httpSecurity, DelegatingApplicationListener.class); if (delegatingApplicationListener == null) { return; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java index 25c4fb39..55d0b284 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataEndpointConfigurer.java @@ -35,8 +35,11 @@ import org.springframework.security.web.util.matcher.RequestMatcher; * @see OAuth2AuthorizationServerMetadataEndpointFilter */ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private Consumer authorizationServerMetadataCustomizer; + private Consumer defaultAuthorizationServerMetadataCustomizer; /** @@ -47,11 +50,13 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A } /** - * Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder} - * allowing the ability to customize the claims of the Authorization Server's configuration. - * - * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder} - * @return the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationServerMetadata.Builder} allowing the ability to customize + * the claims of the Authorization Server's configuration. + * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access + * to the {@link OAuth2AuthorizationServerMetadata.Builder} + * @return the {@link OAuth2AuthorizationServerMetadataEndpointConfigurer} for further + * configuration */ public OAuth2AuthorizationServerMetadataEndpointConfigurer authorizationServerMetadataCustomizer( Consumer authorizationServerMetadataCustomizer) { @@ -61,40 +66,40 @@ public final class OAuth2AuthorizationServerMetadataEndpointConfigurer extends A void addDefaultAuthorizationServerMetadataCustomizer( Consumer defaultAuthorizationServerMetadataCustomizer) { - this.defaultAuthorizationServerMetadataCustomizer = - this.defaultAuthorizationServerMetadataCustomizer == null ? - defaultAuthorizationServerMetadataCustomizer : - this.defaultAuthorizationServerMetadataCustomizer.andThen(defaultAuthorizationServerMetadataCustomizer); + this.defaultAuthorizationServerMetadataCustomizer = this.defaultAuthorizationServerMetadataCustomizer == null + ? defaultAuthorizationServerMetadataCustomizer : this.defaultAuthorizationServerMetadataCustomizer + .andThen(defaultAuthorizationServerMetadataCustomizer); } @Override void init(HttpSecurity httpSecurity) { - this.requestMatcher = new AntPathRequestMatcher( - "/.well-known/oauth-authorization-server", HttpMethod.GET.name()); + this.requestMatcher = new AntPathRequestMatcher("/.well-known/oauth-authorization-server", + HttpMethod.GET.name()); } @Override void configure(HttpSecurity httpSecurity) { - OAuth2AuthorizationServerMetadataEndpointFilter authorizationServerMetadataEndpointFilter = - new OAuth2AuthorizationServerMetadataEndpointFilter(); + OAuth2AuthorizationServerMetadataEndpointFilter authorizationServerMetadataEndpointFilter = new OAuth2AuthorizationServerMetadataEndpointFilter(); Consumer authorizationServerMetadataCustomizer = getAuthorizationServerMetadataCustomizer(); if (authorizationServerMetadataCustomizer != null) { - authorizationServerMetadataEndpointFilter.setAuthorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer); + authorizationServerMetadataEndpointFilter + .setAuthorizationServerMetadataCustomizer(authorizationServerMetadataCustomizer); } - httpSecurity.addFilterBefore(postProcess(authorizationServerMetadataEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); + httpSecurity.addFilterBefore(postProcess(authorizationServerMetadataEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); } private Consumer getAuthorizationServerMetadataCustomizer() { Consumer authorizationServerMetadataCustomizer = null; - if (this.defaultAuthorizationServerMetadataCustomizer != null || this.authorizationServerMetadataCustomizer != null) { + if (this.defaultAuthorizationServerMetadataCustomizer != null + || this.authorizationServerMetadataCustomizer != null) { if (this.defaultAuthorizationServerMetadataCustomizer != null) { authorizationServerMetadataCustomizer = this.defaultAuthorizationServerMetadataCustomizer; } if (this.authorizationServerMetadataCustomizer != null) { - authorizationServerMetadataCustomizer = - authorizationServerMetadataCustomizer == null ? - this.authorizationServerMetadataCustomizer : - authorizationServerMetadataCustomizer.andThen(this.authorizationServerMetadataCustomizer); + authorizationServerMetadataCustomizer = authorizationServerMetadataCustomizer == null + ? this.authorizationServerMetadataCustomizer + : authorizationServerMetadataCustomizer.andThen(this.authorizationServerMetadataCustomizer); } } return authorizationServerMetadataCustomizer; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java index 7e37982f..73f63b66 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientAuthenticationConfigurer.java @@ -60,12 +60,21 @@ import org.springframework.util.Assert; * @see OAuth2ClientAuthenticationFilter */ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List authenticationConverters = new ArrayList<>(); - private Consumer> authenticationConvertersConsumer = (authenticationConverters) -> {}; + + private Consumer> authenticationConvertersConsumer = (authenticationConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler authenticationSuccessHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -76,24 +85,28 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} - * to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client. - * - * @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} + * Adds an {@link AuthenticationConverter} used when attempting to extract client + * credentials from {@link HttpServletRequest} to an instance of + * {@link OAuth2ClientAuthenticationToken} used for authenticating the client. + * @param authenticationConverter an {@link AuthenticationConverter} used when + * attempting to extract client credentials from {@link HttpServletRequest} * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration */ - public OAuth2ClientAuthenticationConfigurer authenticationConverter(AuthenticationConverter authenticationConverter) { + public OAuth2ClientAuthenticationConfigurer authenticationConverter( + AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); this.authenticationConverters.add(authenticationConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param authenticationConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param authenticationConvertersConsumer the {@code Consumer} providing access to + * the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration * @since 0.4.0 */ @@ -105,9 +118,10 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2ClientAuthenticationToken} + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OAuth2ClientAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OAuth2ClientAuthenticationToken} * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration */ public OAuth2ClientAuthenticationConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { @@ -117,11 +131,12 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration * @since 0.4.0 */ @@ -133,44 +148,43 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication - * and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling a successful client authentication + * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client + * authentication and associating the {@link OAuth2ClientAuthenticationToken} to the + * {@link SecurityContext}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling a successful client authentication * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration */ - public OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { + public OAuth2ClientAuthenticationConfigurer authenticationSuccessHandler( + AuthenticationSuccessHandler authenticationSuccessHandler) { this.authenticationSuccessHandler = authenticationSuccessHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling a failed client authentication - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling a failed client authentication + * Sets the {@link AuthenticationFailureHandler} used for handling a failed client + * authentication and returning the {@link OAuth2Error Error Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling a failed client authentication * @return the {@link OAuth2ClientAuthenticationConfigurer} for further configuration */ - public OAuth2ClientAuthenticationConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2ClientAuthenticationConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher( - authorizationServerSettings.getTokenEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name()), + new AntPathRequestMatcher(authorizationServerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name()), - new AntPathRequestMatcher( - authorizationServerSettings.getTokenIntrospectionEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name()), - new AntPathRequestMatcher( - authorizationServerSettings.getTokenRevocationEndpoint(), - HttpMethod.POST.name()), - new AntPathRequestMatcher( - authorizationServerSettings.getDeviceAuthorizationEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getDeviceAuthorizationEndpoint(), HttpMethod.POST.name())); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); @@ -178,8 +192,8 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override @@ -192,15 +206,16 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co authenticationConverters.addAll(0, this.authenticationConverters); } this.authenticationConvertersConsumer.accept(authenticationConverters); - clientAuthenticationFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + clientAuthenticationFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.authenticationSuccessHandler != null) { clientAuthenticationFilter.setAuthenticationSuccessHandler(this.authenticationSuccessHandler); } if (this.errorResponseHandler != null) { clientAuthenticationFilter.setAuthenticationFailureHandler(this.errorResponseHandler); } - httpSecurity.addFilterAfter(postProcess(clientAuthenticationFilter), AbstractPreAuthenticatedProcessingFilter.class); + httpSecurity.addFilterAfter(postProcess(clientAuthenticationFilter), + AbstractPreAuthenticatedProcessingFilter.class); } @Override @@ -222,23 +237,24 @@ public final class OAuth2ClientAuthenticationConfigurer extends AbstractOAuth2Co private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity); + RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils + .getRegisteredClientRepository(httpSecurity); OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity); - JwtClientAssertionAuthenticationProvider jwtClientAssertionAuthenticationProvider = - new JwtClientAssertionAuthenticationProvider(registeredClientRepository, authorizationService); + JwtClientAssertionAuthenticationProvider jwtClientAssertionAuthenticationProvider = new JwtClientAssertionAuthenticationProvider( + registeredClientRepository, authorizationService); authenticationProviders.add(jwtClientAssertionAuthenticationProvider); - ClientSecretAuthenticationProvider clientSecretAuthenticationProvider = - new ClientSecretAuthenticationProvider(registeredClientRepository, authorizationService); + ClientSecretAuthenticationProvider clientSecretAuthenticationProvider = new ClientSecretAuthenticationProvider( + registeredClientRepository, authorizationService); PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class); if (passwordEncoder != null) { clientSecretAuthenticationProvider.setPasswordEncoder(passwordEncoder); } authenticationProviders.add(clientSecretAuthenticationProvider); - PublicClientAuthenticationProvider publicClientAuthenticationProvider = - new PublicClientAuthenticationProvider(registeredClientRepository, authorizationService); + PublicClientAuthenticationProvider publicClientAuthenticationProvider = new PublicClientAuthenticationProvider( + registeredClientRepository, authorizationService); authenticationProviders.add(publicClientAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java index 9d7ab7ad..470e09b8 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ConfigurerUtils.java @@ -57,7 +57,8 @@ final class OAuth2ConfigurerUtils { } static RegisteredClientRepository getRegisteredClientRepository(HttpSecurity httpSecurity) { - RegisteredClientRepository registeredClientRepository = httpSecurity.getSharedObject(RegisteredClientRepository.class); + RegisteredClientRepository registeredClientRepository = httpSecurity + .getSharedObject(RegisteredClientRepository.class); if (registeredClientRepository == null) { registeredClientRepository = getBean(httpSecurity, RegisteredClientRepository.class); httpSecurity.setSharedObject(RegisteredClientRepository.class, registeredClientRepository); @@ -66,7 +67,8 @@ final class OAuth2ConfigurerUtils { } static OAuth2AuthorizationService getAuthorizationService(HttpSecurity httpSecurity) { - OAuth2AuthorizationService authorizationService = httpSecurity.getSharedObject(OAuth2AuthorizationService.class); + OAuth2AuthorizationService authorizationService = httpSecurity + .getSharedObject(OAuth2AuthorizationService.class); if (authorizationService == null) { authorizationService = getOptionalBean(httpSecurity, OAuth2AuthorizationService.class); if (authorizationService == null) { @@ -78,7 +80,8 @@ final class OAuth2ConfigurerUtils { } static OAuth2AuthorizationConsentService getAuthorizationConsentService(HttpSecurity httpSecurity) { - OAuth2AuthorizationConsentService authorizationConsentService = httpSecurity.getSharedObject(OAuth2AuthorizationConsentService.class); + OAuth2AuthorizationConsentService authorizationConsentService = httpSecurity + .getSharedObject(OAuth2AuthorizationConsentService.class); if (authorizationConsentService == null) { authorizationConsentService = getOptionalBean(httpSecurity, OAuth2AuthorizationConsentService.class); if (authorizationConsentService == null) { @@ -91,23 +94,25 @@ final class OAuth2ConfigurerUtils { @SuppressWarnings("unchecked") static OAuth2TokenGenerator getTokenGenerator(HttpSecurity httpSecurity) { - OAuth2TokenGenerator tokenGenerator = httpSecurity.getSharedObject(OAuth2TokenGenerator.class); + OAuth2TokenGenerator tokenGenerator = httpSecurity + .getSharedObject(OAuth2TokenGenerator.class); if (tokenGenerator == null) { tokenGenerator = getOptionalBean(httpSecurity, OAuth2TokenGenerator.class); if (tokenGenerator == null) { JwtGenerator jwtGenerator = getJwtGenerator(httpSecurity); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); - OAuth2TokenCustomizer accessTokenCustomizer = getAccessTokenCustomizer(httpSecurity); + OAuth2TokenCustomizer accessTokenCustomizer = getAccessTokenCustomizer( + httpSecurity); if (accessTokenCustomizer != null) { accessTokenGenerator.setAccessTokenCustomizer(accessTokenCustomizer); } OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); if (jwtGenerator != null) { - tokenGenerator = new DelegatingOAuth2TokenGenerator( - jwtGenerator, accessTokenGenerator, refreshTokenGenerator); - } else { - tokenGenerator = new DelegatingOAuth2TokenGenerator( - accessTokenGenerator, refreshTokenGenerator); + tokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, + refreshTokenGenerator); + } + else { + tokenGenerator = new DelegatingOAuth2TokenGenerator(accessTokenGenerator, refreshTokenGenerator); } } httpSecurity.setSharedObject(OAuth2TokenGenerator.class, tokenGenerator); @@ -162,17 +167,20 @@ final class OAuth2ConfigurerUtils { } private static OAuth2TokenCustomizer getJwtCustomizer(HttpSecurity httpSecurity) { - ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, JwtEncodingContext.class); + ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, + JwtEncodingContext.class); return getOptionalBean(httpSecurity, type); } private static OAuth2TokenCustomizer getAccessTokenCustomizer(HttpSecurity httpSecurity) { - ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, OAuth2TokenClaimsContext.class); + ResolvableType type = ResolvableType.forClassWithGenerics(OAuth2TokenCustomizer.class, + OAuth2TokenClaimsContext.class); return getOptionalBean(httpSecurity, type); } static AuthorizationServerSettings getAuthorizationServerSettings(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = httpSecurity.getSharedObject(AuthorizationServerSettings.class); + AuthorizationServerSettings authorizationServerSettings = httpSecurity + .getSharedObject(AuthorizationServerSettings.class); if (authorizationServerSettings == null) { authorizationServerSettings = getBean(httpSecurity, AuthorizationServerSettings.class); httpSecurity.setSharedObject(AuthorizationServerSettings.class, authorizationServerSettings); @@ -198,12 +206,12 @@ final class OAuth2ConfigurerUtils { } static T getOptionalBean(HttpSecurity httpSecurity, Class type) { - Map beansMap = BeanFactoryUtils.beansOfTypeIncludingAncestors( - httpSecurity.getSharedObject(ApplicationContext.class), type); + Map beansMap = BeanFactoryUtils + .beansOfTypeIncludingAncestors(httpSecurity.getSharedObject(ApplicationContext.class), type); if (beansMap.size() > 1) { throw new NoUniqueBeanDefinitionException(type, beansMap.size(), - "Expected single matching bean of type '" + type.getName() + "' but found " + - beansMap.size() + ": " + StringUtils.collectionToCommaDelimitedString(beansMap.keySet())); + "Expected single matching bean of type '" + type.getName() + "' but found " + beansMap.size() + ": " + + StringUtils.collectionToCommaDelimitedString(beansMap.keySet())); } return (!beansMap.isEmpty() ? beansMap.values().iterator().next() : null); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java index 765548f3..623f6c22 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceAuthorizationEndpointConfigurer.java @@ -56,12 +56,22 @@ import org.springframework.util.StringUtils; public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractOAuth2Configurer { private RequestMatcher requestMatcher; + private final List deviceAuthorizationRequestConverters = new ArrayList<>(); - private Consumer> deviceAuthorizationRequestConvertersConsumer = (deviceAuthorizationRequestConverters) -> {}; + + private Consumer> deviceAuthorizationRequestConvertersConsumer = ( + deviceAuthorizationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler deviceAuthorizationResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; + private String verificationUri; /** @@ -72,52 +82,67 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the request. - * - * @param deviceAuthorizationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest} - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationConverter} used when attempting to extract a Device + * Authorization Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating + * the request. + * @param deviceAuthorizationRequestConverter the {@link AuthenticationConverter} used + * when attempting to extract a Device Authorization Request from + * {@link HttpServletRequest} + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter(AuthenticationConverter deviceAuthorizationRequestConverter) { + public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverter( + AuthenticationConverter deviceAuthorizationRequestConverter) { Assert.notNull(deviceAuthorizationRequestConverter, "deviceAuthorizationRequestConverter cannot be null"); this.deviceAuthorizationRequestConverters.add(deviceAuthorizationRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #deviceAuthorizationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param deviceAuthorizationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added + * {@link #deviceAuthorizationRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param deviceAuthorizationRequestConvertersConsumer the {@code Consumer} providing + * access to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationRequestConverters( Consumer> deviceAuthorizationRequestConvertersConsumer) { - Assert.notNull(deviceAuthorizationRequestConvertersConsumer, "deviceAuthorizationRequestConvertersConsumer cannot be null"); + Assert.notNull(deviceAuthorizationRequestConvertersConsumer, + "deviceAuthorizationRequestConvertersConsumer cannot be null"); this.deviceAuthorizationRequestConvertersConsumer = deviceAuthorizationRequestConvertersConsumer; return this; } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { + public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProvider( + AuthenticationProvider authenticationProvider) { Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); this.authenticationProviders.add(authenticationProvider); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceAuthorizationEndpointConfigurer authenticationProviders( Consumer> authenticationProvidersConsumer) { @@ -127,34 +152,41 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} - * and returning the {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}. - * - * @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} and returning the + * {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}. + * @param deviceAuthorizationResponseHandler the {@link AuthenticationSuccessHandler} + * used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationResponseHandler(AuthenticationSuccessHandler deviceAuthorizationResponseHandler) { + public OAuth2DeviceAuthorizationEndpointConfigurer deviceAuthorizationResponseHandler( + AuthenticationSuccessHandler deviceAuthorizationResponseHandler) { this.deviceAuthorizationResponseHandler = deviceAuthorizationResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2DeviceAuthorizationEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } /** * Sets the end-user verification {@code URI} on the authorization server. - * - * @param verificationUri the end-user verification {@code URI} on the authorization server - * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further configuration + * @param verificationUri the end-user verification {@code URI} on the authorization + * server + * @return the {@link OAuth2DeviceAuthorizationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceAuthorizationEndpointConfigurer verificationUri(String verificationUri) { this.verificationUri = verificationUri; @@ -163,36 +195,36 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO @Override public void init(HttpSecurity builder) { - AuthorizationServerSettings authorizationServerSettings = - OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder); - this.requestMatcher = new AntPathRequestMatcher( - authorizationServerSettings.getDeviceAuthorizationEndpoint(), HttpMethod.POST.name()); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(builder); + this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getDeviceAuthorizationEndpoint(), + HttpMethod.POST.name()); List authenticationProviders = createDefaultAuthenticationProviders(builder); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - builder.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders + .forEach(authenticationProvider -> builder.authenticationProvider(postProcess(authenticationProvider))); } @Override public void configure(HttpSecurity builder) { AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(builder); - OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter = - new OAuth2DeviceAuthorizationEndpointFilter( - authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint()); + OAuth2DeviceAuthorizationEndpointFilter deviceAuthorizationEndpointFilter = new OAuth2DeviceAuthorizationEndpointFilter( + authenticationManager, authorizationServerSettings.getDeviceAuthorizationEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.deviceAuthorizationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.deviceAuthorizationRequestConverters); } this.deviceAuthorizationRequestConvertersConsumer.accept(authenticationConverters); - deviceAuthorizationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + deviceAuthorizationEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.deviceAuthorizationResponseHandler != null) { deviceAuthorizationEndpointFilter.setAuthenticationSuccessHandler(this.deviceAuthorizationResponseHandler); } @@ -222,8 +254,8 @@ public final class OAuth2DeviceAuthorizationEndpointConfigurer extends AbstractO OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder); - OAuth2DeviceAuthorizationRequestAuthenticationProvider deviceAuthorizationRequestAuthenticationProvider = - new OAuth2DeviceAuthorizationRequestAuthenticationProvider(authorizationService); + OAuth2DeviceAuthorizationRequestAuthenticationProvider deviceAuthorizationRequestAuthenticationProvider = new OAuth2DeviceAuthorizationRequestAuthenticationProvider( + authorizationService); authenticationProviders.add(deviceAuthorizationRequestAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java index bc50f7b4..79a4adff 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceVerificationEndpointConfigurer.java @@ -61,12 +61,22 @@ import org.springframework.util.StringUtils; public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOAuth2Configurer { private RequestMatcher requestMatcher; + private final List deviceVerificationRequestConverters = new ArrayList<>(); - private Consumer> deviceVerificationRequestConvertersConsumer = (deviceVerificationRequestConverters) -> {}; + + private Consumer> deviceVerificationRequestConvertersConsumer = ( + deviceVerificationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler deviceVerificationResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; + private String consentPage; /** @@ -77,52 +87,71 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating the request. - * - * @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest} - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationConverter} used when attempting to extract a Device + * Verification Request (or Device Authorization Consent) from + * {@link HttpServletRequest} to an instance of + * {@link OAuth2DeviceVerificationAuthenticationToken} or + * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating + * the request. + * @param deviceVerificationRequestConverter the {@link AuthenticationConverter} used + * when attempting to extract a Device Verification Request (or Device Authorization + * Consent) from {@link HttpServletRequest} + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter(AuthenticationConverter deviceVerificationRequestConverter) { + public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverter( + AuthenticationConverter deviceVerificationRequestConverter) { Assert.notNull(deviceVerificationRequestConverter, "deviceVerificationRequestConverter cannot be null"); this.deviceVerificationRequestConverters.add(deviceVerificationRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #deviceVerificationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param deviceVerificationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added + * {@link #deviceVerificationRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param deviceVerificationRequestConvertersConsumer the {@code Consumer} providing + * access to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationRequestConverters( Consumer> deviceVerificationRequestConvertersConsumer) { - Assert.notNull(deviceVerificationRequestConvertersConsumer, "deviceVerificationRequestConvertersConsumer cannot be null"); + Assert.notNull(deviceVerificationRequestConvertersConsumer, + "deviceVerificationRequestConvertersConsumer cannot be null"); this.deviceVerificationRequestConvertersConsumer = deviceVerificationRequestConvertersConsumer; return this; } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OAuth2DeviceVerificationAuthenticationToken} or + * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OAuth2DeviceVerificationAuthenticationToken} or + * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceVerificationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { + public OAuth2DeviceVerificationEndpointConfigurer authenticationProvider( + AuthenticationProvider authenticationProvider) { Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); this.authenticationProviders.add(authenticationProvider); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceVerificationEndpointConfigurer authenticationProviders( Consumer> authenticationProvidersConsumer) { @@ -132,59 +161,67 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken} - * and returning the response. - * - * @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken} - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2DeviceVerificationAuthenticationToken} and returning the response. + * @param deviceVerificationResponseHandler the {@link AuthenticationSuccessHandler} + * used for handling an {@link OAuth2DeviceVerificationAuthenticationToken} + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler(AuthenticationSuccessHandler deviceVerificationResponseHandler) { + public OAuth2DeviceVerificationEndpointConfigurer deviceVerificationResponseHandler( + AuthenticationSuccessHandler deviceVerificationResponseHandler) { this.deviceVerificationResponseHandler = deviceVerificationResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ - public OAuth2DeviceVerificationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2DeviceVerificationEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } /** - * Specify the URI to redirect Resource Owners to if consent is required during - * the {@code device_code} flow. A default consent page will be generated when - * this attribute is not specified. + * Specify the URI to redirect Resource Owners to if consent is required during the + * {@code device_code} flow. A default consent page will be generated when this + * attribute is not specified. * - * If a URI is specified, applications are required to process the specified URI to generate - * a consent page. The query string will contain the following parameters: + * If a URI is specified, applications are required to process the specified URI to + * generate a consent page. The query string will contain the following parameters: * *
    *
  • {@code client_id} - the client identifier
  • - *
  • {@code scope} - a space-delimited list of scopes present in the device authorization request
  • + *
  • {@code scope} - a space-delimited list of scopes present in the device + * authorization request
  • *
  • {@code state} - a CSRF protection token
  • *
  • {@code user_code} - the user code
  • *
* - * In general, the consent page should create a form that submits - * a request with the following requirements: + * In general, the consent page should create a form that submits a request with the + * following requirements: * *
    *
  • It must be an HTTP POST
  • - *
  • It must be submitted to {@link AuthorizationServerSettings#getDeviceVerificationEndpoint()}
  • + *
  • It must be submitted to + * {@link AuthorizationServerSettings#getDeviceVerificationEndpoint()}
  • *
  • It must include the received {@code client_id} as an HTTP parameter
  • *
  • It must include the received {@code state} as an HTTP parameter
  • - *
  • It must include the list of {@code scope}s the {@code Resource Owner} - * consented to as an HTTP parameter
  • + *
  • It must include the list of {@code scope}s the {@code Resource Owner} consented + * to as an HTTP parameter
  • *
  • It must include the received {@code user_code} as an HTTP parameter
  • *
- * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") - * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further configuration + * @param consentPage the URI of the custom consent page to redirect to if consent is + * required (e.g. "/oauth2/consent") + * @return the {@link OAuth2DeviceVerificationEndpointConfigurer} for further + * configuration */ public OAuth2DeviceVerificationEndpointConfigurer consentPage(String consentPage) { this.consentPage = consentPage; @@ -193,14 +230,12 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA @Override public void init(HttpSecurity builder) { - AuthorizationServerSettings authorizationServerSettings = - OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(builder); this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher( - authorizationServerSettings.getDeviceVerificationEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getDeviceVerificationEndpoint(), HttpMethod.GET.name()), - new AntPathRequestMatcher( - authorizationServerSettings.getDeviceVerificationEndpoint(), + new AntPathRequestMatcher(authorizationServerSettings.getDeviceVerificationEndpoint(), HttpMethod.POST.name())); List authenticationProviders = createDefaultAuthenticationProviders(builder); @@ -208,27 +243,25 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - builder.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders + .forEach(authenticationProvider -> builder.authenticationProvider(postProcess(authenticationProvider))); } @Override public void configure(HttpSecurity builder) { AuthenticationManager authenticationManager = builder.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = - OAuth2ConfigurerUtils.getAuthorizationServerSettings(builder); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(builder); - OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter = - new OAuth2DeviceVerificationEndpointFilter( - authenticationManager, - authorizationServerSettings.getDeviceVerificationEndpoint()); + OAuth2DeviceVerificationEndpointFilter deviceVerificationEndpointFilter = new OAuth2DeviceVerificationEndpointFilter( + authenticationManager, authorizationServerSettings.getDeviceVerificationEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.deviceVerificationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.deviceVerificationRequestConverters); } this.deviceVerificationRequestConvertersConsumer.accept(authenticationConverters); - deviceVerificationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + deviceVerificationEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.deviceVerificationResponseHandler != null) { deviceVerificationEndpointFilter.setAuthenticationSuccessHandler(this.deviceVerificationResponseHandler); } @@ -238,7 +271,8 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA if (StringUtils.hasText(this.consentPage)) { deviceVerificationEndpointFilter.setConsentPage(this.consentPage); } - builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); + builder.addFilterBefore(postProcess(deviceVerificationEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); } @Override @@ -256,12 +290,11 @@ public final class OAuth2DeviceVerificationEndpointConfigurer extends AbstractOA } private static List createDefaultAuthenticationProviders(HttpSecurity builder) { - RegisteredClientRepository registeredClientRepository = - OAuth2ConfigurerUtils.getRegisteredClientRepository(builder); - OAuth2AuthorizationService authorizationService = - OAuth2ConfigurerUtils.getAuthorizationService(builder); - OAuth2AuthorizationConsentService authorizationConsentService = - OAuth2ConfigurerUtils.getAuthorizationConsentService(builder); + RegisteredClientRepository registeredClientRepository = OAuth2ConfigurerUtils + .getRegisteredClientRepository(builder); + OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(builder); + OAuth2AuthorizationConsentService authorizationConsentService = OAuth2ConfigurerUtils + .getAuthorizationConsentService(builder); List authenticationProviders = new ArrayList<>(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java index 61e089f2..863150c6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java @@ -63,12 +63,22 @@ import org.springframework.util.Assert; * @see OAuth2TokenEndpointFilter */ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List accessTokenRequestConverters = new ArrayList<>(); - private Consumer> accessTokenRequestConvertersConsumer = (accessTokenRequestConverters) -> {}; + + private Consumer> accessTokenRequestConvertersConsumer = ( + accessTokenRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler accessTokenResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -79,24 +89,29 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the authorization grant. - * - * @param accessTokenRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} + * Adds an {@link AuthenticationConverter} used when attempting to extract an Access + * Token Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the + * authorization grant. + * @param accessTokenRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract an Access Token Request from {@link HttpServletRequest} * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration */ - public OAuth2TokenEndpointConfigurer accessTokenRequestConverter(AuthenticationConverter accessTokenRequestConverter) { + public OAuth2TokenEndpointConfigurer accessTokenRequestConverter( + AuthenticationConverter accessTokenRequestConverter) { Assert.notNull(accessTokenRequestConverter, "accessTokenRequestConverter cannot be null"); this.accessTokenRequestConverters.add(accessTokenRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #accessTokenRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param accessTokenRequestConvertersConsumer the {@code Consumer} providing access + * to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -108,9 +123,10 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure } /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken} + * Adds an {@link AuthenticationProvider} used for authenticating a type of + * {@link OAuth2AuthorizationGrantAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating a type of {@link OAuth2AuthorizationGrantAuthenticationToken} * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration */ public OAuth2TokenEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { @@ -120,11 +136,12 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -136,22 +153,25 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} - * and returning the {@link OAuth2AccessTokenResponse Access Token Response}. - * - * @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2AccessTokenAuthenticationToken} and returning the + * {@link OAuth2AccessTokenResponse Access Token Response}. + * @param accessTokenResponseHandler the {@link AuthenticationSuccessHandler} used for + * handling an {@link OAuth2AccessTokenAuthenticationToken} * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration */ - public OAuth2TokenEndpointConfigurer accessTokenResponseHandler(AuthenticationSuccessHandler accessTokenResponseHandler) { + public OAuth2TokenEndpointConfigurer accessTokenResponseHandler( + AuthenticationSuccessHandler accessTokenResponseHandler) { this.accessTokenResponseHandler = accessTokenResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} * @return the {@link OAuth2TokenEndpointConfigurer} for further configuration */ public OAuth2TokenEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { @@ -161,35 +181,34 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); - this.requestMatcher = new AntPathRequestMatcher( - authorizationServerSettings.getTokenEndpoint(), HttpMethod.POST.name()); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); + this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenEndpoint(), + HttpMethod.POST.name()); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OAuth2TokenEndpointFilter tokenEndpointFilter = - new OAuth2TokenEndpointFilter( - authenticationManager, - authorizationServerSettings.getTokenEndpoint()); + OAuth2TokenEndpointFilter tokenEndpointFilter = new OAuth2TokenEndpointFilter(authenticationManager, + authorizationServerSettings.getTokenEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.accessTokenRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.accessTokenRequestConverters); } this.accessTokenRequestConvertersConsumer.accept(authenticationConverters); - tokenEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + tokenEndpointFilter.setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.accessTokenResponseHandler != null) { tokenEndpointFilter.setAuthenticationSuccessHandler(this.accessTokenResponseHandler); } @@ -219,26 +238,27 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure List authenticationProviders = new ArrayList<>(); OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity); - OAuth2TokenGenerator tokenGenerator = OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity); + OAuth2TokenGenerator tokenGenerator = OAuth2ConfigurerUtils + .getTokenGenerator(httpSecurity); - OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = - new OAuth2AuthorizationCodeAuthenticationProvider(authorizationService, tokenGenerator); + OAuth2AuthorizationCodeAuthenticationProvider authorizationCodeAuthenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider( + authorizationService, tokenGenerator); SessionRegistry sessionRegistry = httpSecurity.getSharedObject(SessionRegistry.class); if (sessionRegistry != null) { authorizationCodeAuthenticationProvider.setSessionRegistry(sessionRegistry); } authenticationProviders.add(authorizationCodeAuthenticationProvider); - OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = - new OAuth2RefreshTokenAuthenticationProvider(authorizationService, tokenGenerator); + OAuth2RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider = new OAuth2RefreshTokenAuthenticationProvider( + authorizationService, tokenGenerator); authenticationProviders.add(refreshTokenAuthenticationProvider); - OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider = - new OAuth2ClientCredentialsAuthenticationProvider(authorizationService, tokenGenerator); + OAuth2ClientCredentialsAuthenticationProvider clientCredentialsAuthenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider( + authorizationService, tokenGenerator); authenticationProviders.add(clientCredentialsAuthenticationProvider); - OAuth2DeviceCodeAuthenticationProvider deviceCodeAuthenticationProvider = - new OAuth2DeviceCodeAuthenticationProvider(authorizationService, tokenGenerator); + OAuth2DeviceCodeAuthenticationProvider deviceCodeAuthenticationProvider = new OAuth2DeviceCodeAuthenticationProvider( + authorizationService, tokenGenerator); authenticationProviders.add(deviceCodeAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java index 08f9c0b3..fe940056 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionEndpointConfigurer.java @@ -53,12 +53,22 @@ import org.springframework.util.Assert; * @see OAuth2TokenIntrospectionEndpointFilter */ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List introspectionRequestConverters = new ArrayList<>(); - private Consumer> introspectionRequestConvertersConsumer = (introspectionRequestConverters) -> {}; + + private Consumer> introspectionRequestConvertersConsumer = ( + introspectionRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler introspectionResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -69,25 +79,32 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request. - * - * @param introspectionRequestConverter an {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationConverter} used when attempting to extract an + * Introspection Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the + * request. + * @param introspectionRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract an Introspection Request from {@link HttpServletRequest} + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration */ - public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter(AuthenticationConverter introspectionRequestConverter) { + public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverter( + AuthenticationConverter introspectionRequestConverter) { Assert.notNull(introspectionRequestConverter, "introspectionRequestConverter cannot be null"); this.introspectionRequestConverters.add(introspectionRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param introspectionRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #introspectionRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param introspectionRequestConvertersConsumer the {@code Consumer} providing access + * to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OAuth2TokenIntrospectionEndpointConfigurer introspectionRequestConverters( @@ -98,24 +115,29 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA } /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken} - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationProvider} used for authenticating a type of + * {@link OAuth2TokenIntrospectionAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating a type of {@link OAuth2TokenIntrospectionAuthenticationToken} + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration */ - public OAuth2TokenIntrospectionEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { + public OAuth2TokenIntrospectionEndpointConfigurer authenticationProvider( + AuthenticationProvider authenticationProvider) { Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); this.authenticationProviders.add(authenticationProvider); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OAuth2TokenIntrospectionEndpointConfigurer authenticationProviders( @@ -126,58 +148,65 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}. - * - * @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken} - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2TokenIntrospectionAuthenticationToken}. + * @param introspectionResponseHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2TokenIntrospectionAuthenticationToken} + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration */ - public OAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler(AuthenticationSuccessHandler introspectionResponseHandler) { + public OAuth2TokenIntrospectionEndpointConfigurer introspectionResponseHandler( + AuthenticationSuccessHandler introspectionResponseHandler) { this.introspectionResponseHandler = introspectionResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} + * @return the {@link OAuth2TokenIntrospectionEndpointConfigurer} for further + * configuration */ - public OAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2TokenIntrospectionEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); - this.requestMatcher = new AntPathRequestMatcher( - authorizationServerSettings.getTokenIntrospectionEndpoint(), HttpMethod.POST.name()); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); + this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenIntrospectionEndpoint(), + HttpMethod.POST.name()); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter = - new OAuth2TokenIntrospectionEndpointFilter( - authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint()); + OAuth2TokenIntrospectionEndpointFilter introspectionEndpointFilter = new OAuth2TokenIntrospectionEndpointFilter( + authenticationManager, authorizationServerSettings.getTokenIntrospectionEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.introspectionRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.introspectionRequestConverters); } this.introspectionRequestConvertersConsumer.accept(authenticationConverters); - introspectionEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + introspectionEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.introspectionResponseHandler != null) { introspectionEndpointFilter.setAuthenticationSuccessHandler(this.introspectionResponseHandler); } @@ -203,10 +232,9 @@ public final class OAuth2TokenIntrospectionEndpointConfigurer extends AbstractOA private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider = - new OAuth2TokenIntrospectionAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); + OAuth2TokenIntrospectionAuthenticationProvider tokenIntrospectionAuthenticationProvider = new OAuth2TokenIntrospectionAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); authenticationProviders.add(tokenIntrospectionAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java index e6570f42..3e0f20e2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationEndpointConfigurer.java @@ -52,12 +52,22 @@ import org.springframework.util.Assert; * @see OAuth2TokenRevocationEndpointFilter */ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List revocationRequestConverters = new ArrayList<>(); - private Consumer> revocationRequestConvertersConsumer = (revocationRequestConverters) -> {}; + + private Consumer> revocationRequestConvertersConsumer = ( + revocationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler revocationResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -68,25 +78,32 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request. - * - * @param revocationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationConverter} used when attempting to extract a Revoke + * Token Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the + * request. + * @param revocationRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract a Revoke Token Request from {@link HttpServletRequest} + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration */ - public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter(AuthenticationConverter revocationRequestConverter) { + public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverter( + AuthenticationConverter revocationRequestConverter) { Assert.notNull(revocationRequestConverter, "revocationRequestConverter cannot be null"); this.revocationRequestConverters.add(revocationRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #revocationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param revocationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #revocationRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param revocationRequestConvertersConsumer the {@code Consumer} providing access to + * the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OAuth2TokenRevocationEndpointConfigurer revocationRequestConverters( @@ -97,24 +114,29 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth } /** - * Adds an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken} - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationProvider} used for authenticating a type of + * {@link OAuth2TokenRevocationAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating a type of {@link OAuth2TokenRevocationAuthenticationToken} + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration */ - public OAuth2TokenRevocationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { + public OAuth2TokenRevocationEndpointConfigurer authenticationProvider( + AuthenticationProvider authenticationProvider) { Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); this.authenticationProviders.add(authenticationProvider); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OAuth2TokenRevocationEndpointConfigurer authenticationProviders( @@ -125,58 +147,65 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}. - * - * @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken} - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2TokenRevocationAuthenticationToken}. + * @param revocationResponseHandler the {@link AuthenticationSuccessHandler} used for + * handling an {@link OAuth2TokenRevocationAuthenticationToken} + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration */ - public OAuth2TokenRevocationEndpointConfigurer revocationResponseHandler(AuthenticationSuccessHandler revocationResponseHandler) { + public OAuth2TokenRevocationEndpointConfigurer revocationResponseHandler( + AuthenticationSuccessHandler revocationResponseHandler) { this.revocationResponseHandler = revocationResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} + * @return the {@link OAuth2TokenRevocationEndpointConfigurer} for further + * configuration */ - public OAuth2TokenRevocationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OAuth2TokenRevocationEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); - this.requestMatcher = new AntPathRequestMatcher( - authorizationServerSettings.getTokenRevocationEndpoint(), HttpMethod.POST.name()); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); + this.requestMatcher = new AntPathRequestMatcher(authorizationServerSettings.getTokenRevocationEndpoint(), + HttpMethod.POST.name()); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OAuth2TokenRevocationEndpointFilter revocationEndpointFilter = - new OAuth2TokenRevocationEndpointFilter( - authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint()); + OAuth2TokenRevocationEndpointFilter revocationEndpointFilter = new OAuth2TokenRevocationEndpointFilter( + authenticationManager, authorizationServerSettings.getTokenRevocationEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.revocationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.revocationRequestConverters); } this.revocationRequestConvertersConsumer.accept(authenticationConverters); - revocationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + revocationEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.revocationResponseHandler != null) { revocationEndpointFilter.setAuthenticationSuccessHandler(this.revocationResponseHandler); } @@ -202,8 +231,8 @@ public final class OAuth2TokenRevocationEndpointConfigurer extends AbstractOAuth private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider = - new OAuth2TokenRevocationAuthenticationProvider(OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); + OAuth2TokenRevocationAuthenticationProvider tokenRevocationAuthenticationProvider = new OAuth2TokenRevocationAuthenticationProvider( + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); authenticationProviders.add(tokenRevocationAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java index 082b2344..798428f2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationEndpointConfigurer.java @@ -56,12 +56,22 @@ import org.springframework.util.Assert; * @see OidcClientRegistrationEndpointFilter */ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List clientRegistrationRequestConverters = new ArrayList<>(); - private Consumer> clientRegistrationRequestConvertersConsumer = (clientRegistrationRequestConverters) -> {}; + + private Consumer> clientRegistrationRequestConvertersConsumer = ( + clientRegistrationRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler clientRegistrationResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -72,11 +82,15 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} - * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. - * - * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} - * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationConverter} used when attempting to extract a Client + * Registration Request from {@link HttpServletRequest} to an instance of + * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the + * request. + * @param clientRegistrationRequestConverter an {@link AuthenticationConverter} used + * when attempting to extract a Client Registration Request from + * {@link HttpServletRequest} + * @return the {@link OidcClientRegistrationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverter( @@ -87,41 +101,50 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #clientRegistrationRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added + * {@link #clientRegistrationRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param clientRegistrationRequestConvertersConsumer the {@code Consumer} providing + * access to the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ public OidcClientRegistrationEndpointConfigurer clientRegistrationRequestConverters( Consumer> clientRegistrationRequestConvertersConsumer) { - Assert.notNull(clientRegistrationRequestConvertersConsumer, "clientRegistrationRequestConvertersConsumer cannot be null"); + Assert.notNull(clientRegistrationRequestConvertersConsumer, + "clientRegistrationRequestConvertersConsumer cannot be null"); this.clientRegistrationRequestConvertersConsumer = clientRegistrationRequestConvertersConsumer; return this; } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcClientRegistrationAuthenticationToken} - * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OidcClientRegistrationAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OidcClientRegistrationAuthenticationToken} + * @return the {@link OidcClientRegistrationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ - public OidcClientRegistrationEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { + public OidcClientRegistrationEndpointConfigurer authenticationProvider( + AuthenticationProvider authenticationProvider) { Assert.notNull(authenticationProvider, "authenticationProvider cannot be null"); this.authenticationProviders.add(authenticationProvider); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s - * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * @return the {@link OidcClientRegistrationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ public OidcClientRegistrationEndpointConfigurer authenticationProviders( @@ -132,68 +155,73 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} - * and returning the {@link OidcClientRegistration Client Registration Response}. - * - * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} - * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcClientRegistrationAuthenticationToken} and returning the + * {@link OidcClientRegistration Client Registration Response}. + * @param clientRegistrationResponseHandler the {@link AuthenticationSuccessHandler} + * used for handling an {@link OidcClientRegistrationAuthenticationToken} + * @return the {@link OidcClientRegistrationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ - public OidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler(AuthenticationSuccessHandler clientRegistrationResponseHandler) { + public OidcClientRegistrationEndpointConfigurer clientRegistrationResponseHandler( + AuthenticationSuccessHandler clientRegistrationResponseHandler) { this.clientRegistrationResponseHandler = clientRegistrationResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * @return the {@link OidcClientRegistrationEndpointConfigurer} for further configuration + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} + * @return the {@link OidcClientRegistrationEndpointConfigurer} for further + * configuration * @since 0.4.0 */ - public OidcClientRegistrationEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { + public OidcClientRegistrationEndpointConfigurer errorResponseHandler( + AuthenticationFailureHandler errorResponseHandler) { this.errorResponseHandler = errorResponseHandler; return this; } @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); String clientRegistrationEndpointUri = authorizationServerSettings.getOidcClientRegistrationEndpoint(); this.requestMatcher = new OrRequestMatcher( new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()), - new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name()) - ); + new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.GET.name())); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = - new OidcClientRegistrationEndpointFilter( - authenticationManager, - authorizationServerSettings.getOidcClientRegistrationEndpoint()); + OidcClientRegistrationEndpointFilter oidcClientRegistrationEndpointFilter = new OidcClientRegistrationEndpointFilter( + authenticationManager, authorizationServerSettings.getOidcClientRegistrationEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.clientRegistrationRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.clientRegistrationRequestConverters); } this.clientRegistrationRequestConvertersConsumer.accept(authenticationConverters); - oidcClientRegistrationEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + oidcClientRegistrationEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.clientRegistrationResponseHandler != null) { oidcClientRegistrationEndpointFilter - .setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler); + .setAuthenticationSuccessHandler(this.clientRegistrationResponseHandler); } if (this.errorResponseHandler != null) { oidcClientRegistrationEndpointFilter.setAuthenticationFailureHandler(this.errorResponseHandler); @@ -217,21 +245,19 @@ public final class OidcClientRegistrationEndpointConfigurer extends AbstractOAut private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider = - new OidcClientRegistrationAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity)); + OidcClientRegistrationAuthenticationProvider oidcClientRegistrationAuthenticationProvider = new OidcClientRegistrationAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + OAuth2ConfigurerUtils.getTokenGenerator(httpSecurity)); PasswordEncoder passwordEncoder = OAuth2ConfigurerUtils.getOptionalBean(httpSecurity, PasswordEncoder.class); if (passwordEncoder != null) { oidcClientRegistrationAuthenticationProvider.setPasswordEncoder(passwordEncoder); } authenticationProviders.add(oidcClientRegistrationAuthenticationProvider); - OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider = - new OidcClientConfigurationAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); + OidcClientConfigurationAuthenticationProvider oidcClientConfigurationAuthenticationProvider = new OidcClientConfigurationAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); authenticationProviders.add(oidcClientConfigurationAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcConfigurer.java index d1abe967..6464c171 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcConfigurer.java @@ -42,7 +42,9 @@ import org.springframework.web.util.UriComponentsBuilder; * @see OidcUserInfoEndpointConfigurer */ public final class OidcConfigurer extends AbstractOAuth2Configurer { + private final Map, AbstractOAuth2Configurer> configurers = new LinkedHashMap<>(); + private RequestMatcher requestMatcher; /** @@ -50,27 +52,30 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { */ OidcConfigurer(ObjectPostProcessor objectPostProcessor) { super(objectPostProcessor); - addConfigurer(OidcProviderConfigurationEndpointConfigurer.class, new OidcProviderConfigurationEndpointConfigurer(objectPostProcessor)); + addConfigurer(OidcProviderConfigurationEndpointConfigurer.class, + new OidcProviderConfigurationEndpointConfigurer(objectPostProcessor)); addConfigurer(OidcLogoutEndpointConfigurer.class, new OidcLogoutEndpointConfigurer(objectPostProcessor)); addConfigurer(OidcUserInfoEndpointConfigurer.class, new OidcUserInfoEndpointConfigurer(objectPostProcessor)); } /** * Configures the OpenID Connect 1.0 Provider Configuration Endpoint. - * - * @param providerConfigurationEndpointCustomizer the {@link Customizer} providing access to the {@link OidcProviderConfigurationEndpointConfigurer} + * @param providerConfigurationEndpointCustomizer the {@link Customizer} providing + * access to the {@link OidcProviderConfigurationEndpointConfigurer} * @return the {@link OidcConfigurer} for further configuration * @since 0.4.0 */ - public OidcConfigurer providerConfigurationEndpoint(Customizer providerConfigurationEndpointCustomizer) { - providerConfigurationEndpointCustomizer.customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class)); + public OidcConfigurer providerConfigurationEndpoint( + Customizer providerConfigurationEndpointCustomizer) { + providerConfigurationEndpointCustomizer + .customize(getConfigurer(OidcProviderConfigurationEndpointConfigurer.class)); return this; } /** * Configures the OpenID Connect 1.0 RP-Initiated Logout Endpoint. - * - * @param logoutEndpointCustomizer the {@link Customizer} providing access to the {@link OidcLogoutEndpointConfigurer} + * @param logoutEndpointCustomizer the {@link Customizer} providing access to the + * {@link OidcLogoutEndpointConfigurer} * @return the {@link OidcConfigurer} for further configuration * @since 1.1 */ @@ -81,13 +86,14 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { /** * Configures the OpenID Connect Dynamic Client Registration 1.0 Endpoint. - * - * @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access to the {@link OidcClientRegistrationEndpointConfigurer} + * @param clientRegistrationEndpointCustomizer the {@link Customizer} providing access + * to the {@link OidcClientRegistrationEndpointConfigurer} * @return the {@link OidcConfigurer} for further configuration */ - public OidcConfigurer clientRegistrationEndpoint(Customizer clientRegistrationEndpointCustomizer) { - OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = - getConfigurer(OidcClientRegistrationEndpointConfigurer.class); + public OidcConfigurer clientRegistrationEndpoint( + Customizer clientRegistrationEndpointCustomizer) { + OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer( + OidcClientRegistrationEndpointConfigurer.class); if (clientRegistrationEndpointConfigurer == null) { addConfigurer(OidcClientRegistrationEndpointConfigurer.class, new OidcClientRegistrationEndpointConfigurer(getObjectPostProcessor())); @@ -99,8 +105,8 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { /** * Configures the OpenID Connect 1.0 UserInfo Endpoint. - * - * @param userInfoEndpointCustomizer the {@link Customizer} providing access to the {@link OidcUserInfoEndpointConfigurer} + * @param userInfoEndpointCustomizer the {@link Customizer} providing access to the + * {@link OidcUserInfoEndpointConfigurer} * @return the {@link OidcConfigurer} for further configuration */ public OidcConfigurer userInfoEndpoint(Customizer userInfoEndpointCustomizer) { @@ -120,23 +126,25 @@ public final class OidcConfigurer extends AbstractOAuth2Configurer { @Override void configure(HttpSecurity httpSecurity) { - OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = - getConfigurer(OidcClientRegistrationEndpointConfigurer.class); + OidcClientRegistrationEndpointConfigurer clientRegistrationEndpointConfigurer = getConfigurer( + OidcClientRegistrationEndpointConfigurer.class); if (clientRegistrationEndpointConfigurer != null) { - OidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = - getConfigurer(OidcProviderConfigurationEndpointConfigurer.class); + OidcProviderConfigurationEndpointConfigurer providerConfigurationEndpointConfigurer = getConfigurer( + OidcProviderConfigurationEndpointConfigurer.class); - providerConfigurationEndpointConfigurer - .addDefaultProviderConfigurationCustomizer((builder) -> { - AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); - String issuer = authorizationServerContext.getIssuer(); - AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings(); + providerConfigurationEndpointConfigurer.addDefaultProviderConfigurationCustomizer((builder) -> { + AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); + String issuer = authorizationServerContext.getIssuer(); + AuthorizationServerSettings authorizationServerSettings = authorizationServerContext + .getAuthorizationServerSettings(); - String clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer) - .path(authorizationServerSettings.getOidcClientRegistrationEndpoint()).build().toUriString(); + String clientRegistrationEndpoint = UriComponentsBuilder.fromUriString(issuer) + .path(authorizationServerSettings.getOidcClientRegistrationEndpoint()) + .build() + .toUriString(); - builder.clientRegistrationEndpoint(clientRegistrationEndpoint); - }); + builder.clientRegistrationEndpoint(clientRegistrationEndpoint); + }); } this.configurers.values().forEach(configurer -> configurer.configure(httpSecurity)); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java index 04b356ff..841491d4 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcLogoutEndpointConfigurer.java @@ -53,12 +53,21 @@ import org.springframework.util.Assert; * @see OidcLogoutEndpointFilter */ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List logoutRequestConverters = new ArrayList<>(); - private Consumer> logoutRequestConvertersConsumer = (logoutRequestConverters) -> {}; + + private Consumer> logoutRequestConvertersConsumer = (logoutRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler logoutResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; /** @@ -69,25 +78,26 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest} - * to an instance of {@link OidcLogoutAuthenticationToken} used for authenticating the request. - * - * @param logoutRequestConverter an {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest} + * Adds an {@link AuthenticationConverter} used when attempting to extract a Logout + * Request from {@link HttpServletRequest} to an instance of + * {@link OidcLogoutAuthenticationToken} used for authenticating the request. + * @param logoutRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract a Logout Request from {@link HttpServletRequest} * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ - public OidcLogoutEndpointConfigurer logoutRequestConverter( - AuthenticationConverter logoutRequestConverter) { + public OidcLogoutEndpointConfigurer logoutRequestConverter(AuthenticationConverter logoutRequestConverter) { Assert.notNull(logoutRequestConverter, "logoutRequestConverter cannot be null"); this.logoutRequestConverters.add(logoutRequestConverter); return this; } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #logoutRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param logoutRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #logoutRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param logoutRequestConvertersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationConverter}'s * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ public OidcLogoutEndpointConfigurer logoutRequestConverters( @@ -98,9 +108,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcLogoutAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcLogoutAuthenticationToken} + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OidcLogoutAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OidcLogoutAuthenticationToken} * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ public OidcLogoutEndpointConfigurer authenticationProvider(AuthenticationProvider authenticationProvider) { @@ -110,11 +121,12 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ public OidcLogoutEndpointConfigurer authenticationProviders( @@ -125,10 +137,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken} - * and performing the logout. - * - * @param logoutResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcLogoutAuthenticationToken} and performing the logout. + * @param logoutResponseHandler the {@link AuthenticationSuccessHandler} used for + * handling an {@link OidcLogoutAuthenticationToken} * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ public OidcLogoutEndpointConfigurer logoutResponseHandler(AuthenticationSuccessHandler logoutResponseHandler) { @@ -137,10 +149,11 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} * @return the {@link OidcLogoutEndpointConfigurer} for further configuration */ public OidcLogoutEndpointConfigurer errorResponseHandler(AuthenticationFailureHandler errorResponseHandler) { @@ -150,38 +163,36 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); String logoutEndpointUri = authorizationServerSettings.getOidcLogoutEndpoint(); - this.requestMatcher = new OrRequestMatcher( - new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()), - new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name()) - ); + this.requestMatcher = new OrRequestMatcher(new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.GET.name()), + new AntPathRequestMatcher(logoutEndpointUri, HttpMethod.POST.name())); List authenticationProviders = createDefaultAuthenticationProviders(httpSecurity); if (!this.authenticationProviders.isEmpty()) { authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OidcLogoutEndpointFilter oidcLogoutEndpointFilter = - new OidcLogoutEndpointFilter( - authenticationManager, - authorizationServerSettings.getOidcLogoutEndpoint()); + OidcLogoutEndpointFilter oidcLogoutEndpointFilter = new OidcLogoutEndpointFilter(authenticationManager, + authorizationServerSettings.getOidcLogoutEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.logoutRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.logoutRequestConverters); } this.logoutRequestConvertersConsumer.accept(authenticationConverters); - oidcLogoutEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + oidcLogoutEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.logoutResponseHandler != null) { oidcLogoutEndpointFilter.setAuthenticationSuccessHandler(this.logoutResponseHandler); } @@ -207,11 +218,10 @@ public final class OidcLogoutEndpointConfigurer extends AbstractOAuth2Configurer private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider = - new OidcLogoutAuthenticationProvider( - OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), - httpSecurity.getSharedObject(SessionRegistry.class)); + OidcLogoutAuthenticationProvider oidcLogoutAuthenticationProvider = new OidcLogoutAuthenticationProvider( + OAuth2ConfigurerUtils.getRegisteredClientRepository(httpSecurity), + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity), + httpSecurity.getSharedObject(SessionRegistry.class)); authenticationProviders.add(oidcLogoutAuthenticationProvider); return authenticationProviders; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java index 1141f330..1ddfd8d7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationEndpointConfigurer.java @@ -35,8 +35,11 @@ import org.springframework.security.web.util.matcher.RequestMatcher; * @see OidcProviderConfigurationEndpointFilter */ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private Consumer providerConfigurationCustomizer; + private Consumer defaultProviderConfigurationCustomizer; /** @@ -47,11 +50,13 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO } /** - * Sets the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder} - * allowing the ability to customize the claims of the OpenID Provider's configuration. - * - * @param providerConfigurationCustomizer the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder} - * @return the {@link OidcProviderConfigurationEndpointConfigurer} for further configuration + * Sets the {@code Consumer} providing access to the + * {@link OidcProviderConfiguration.Builder} allowing the ability to customize the + * claims of the OpenID Provider's configuration. + * @param providerConfigurationCustomizer the {@code Consumer} providing access to the + * {@link OidcProviderConfiguration.Builder} + * @return the {@link OidcProviderConfigurationEndpointConfigurer} for further + * configuration */ public OidcProviderConfigurationEndpointConfigurer providerConfigurationCustomizer( Consumer providerConfigurationCustomizer) { @@ -61,27 +66,25 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO void addDefaultProviderConfigurationCustomizer( Consumer defaultProviderConfigurationCustomizer) { - this.defaultProviderConfigurationCustomizer = - this.defaultProviderConfigurationCustomizer == null ? - defaultProviderConfigurationCustomizer : - this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer); + this.defaultProviderConfigurationCustomizer = this.defaultProviderConfigurationCustomizer == null + ? defaultProviderConfigurationCustomizer + : this.defaultProviderConfigurationCustomizer.andThen(defaultProviderConfigurationCustomizer); } @Override void init(HttpSecurity httpSecurity) { - this.requestMatcher = new AntPathRequestMatcher( - "/.well-known/openid-configuration", HttpMethod.GET.name()); + this.requestMatcher = new AntPathRequestMatcher("/.well-known/openid-configuration", HttpMethod.GET.name()); } @Override void configure(HttpSecurity httpSecurity) { - OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = - new OidcProviderConfigurationEndpointFilter(); + OidcProviderConfigurationEndpointFilter oidcProviderConfigurationEndpointFilter = new OidcProviderConfigurationEndpointFilter(); Consumer providerConfigurationCustomizer = getProviderConfigurationCustomizer(); if (providerConfigurationCustomizer != null) { oidcProviderConfigurationEndpointFilter.setProviderConfigurationCustomizer(providerConfigurationCustomizer); } - httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), AbstractPreAuthenticatedProcessingFilter.class); + httpSecurity.addFilterBefore(postProcess(oidcProviderConfigurationEndpointFilter), + AbstractPreAuthenticatedProcessingFilter.class); } private Consumer getProviderConfigurationCustomizer() { @@ -91,10 +94,9 @@ public final class OidcProviderConfigurationEndpointConfigurer extends AbstractO providerConfigurationCustomizer = this.defaultProviderConfigurationCustomizer; } if (this.providerConfigurationCustomizer != null) { - providerConfigurationCustomizer = - providerConfigurationCustomizer == null ? - this.providerConfigurationCustomizer : - providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer); + providerConfigurationCustomizer = providerConfigurationCustomizer == null + ? this.providerConfigurationCustomizer + : providerConfigurationCustomizer.andThen(this.providerConfigurationCustomizer); } } return providerConfigurationCustomizer; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java index 0bd08f53..425d5ea5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoEndpointConfigurer.java @@ -59,13 +59,23 @@ import org.springframework.util.Assert; * @see OidcUserInfoEndpointFilter */ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configurer { + private RequestMatcher requestMatcher; + private final List userInfoRequestConverters = new ArrayList<>(); - private Consumer> userInfoRequestConvertersConsumer = (userInfoRequestConverters) -> {}; + + private Consumer> userInfoRequestConvertersConsumer = (userInfoRequestConverters) -> { + }; + private final List authenticationProviders = new ArrayList<>(); - private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> {}; + + private Consumer> authenticationProvidersConsumer = (authenticationProviders) -> { + }; + private AuthenticationSuccessHandler userInfoResponseHandler; + private AuthenticationFailureHandler errorResponseHandler; + private Function userInfoMapper; /** @@ -76,10 +86,11 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Adds an {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest} - * to an instance of {@link OidcUserInfoAuthenticationToken} used for authenticating the request. - * - * @param userInfoRequestConverter an {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest} + * Adds an {@link AuthenticationConverter} used when attempting to extract an UserInfo + * Request from {@link HttpServletRequest} to an instance of + * {@link OidcUserInfoAuthenticationToken} used for authenticating the request. + * @param userInfoRequestConverter an {@link AuthenticationConverter} used when + * attempting to extract an UserInfo Request from {@link HttpServletRequest} * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -90,11 +101,13 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #userInfoRequestConverter(AuthenticationConverter) AuthenticationConverter}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationConverter}. - * - * @param userInfoRequestConvertersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationConverter}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #userInfoRequestConverter(AuthenticationConverter) + * AuthenticationConverter}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationConverter}. + * @param userInfoRequestConvertersConsumer the {@code Consumer} providing access to + * the {@code List} of default and (optionally) added + * {@link AuthenticationConverter}'s * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -106,9 +119,10 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Adds an {@link AuthenticationProvider} used for authenticating an {@link OidcUserInfoAuthenticationToken}. - * - * @param authenticationProvider an {@link AuthenticationProvider} used for authenticating an {@link OidcUserInfoAuthenticationToken} + * Adds an {@link AuthenticationProvider} used for authenticating an + * {@link OidcUserInfoAuthenticationToken}. + * @param authenticationProvider an {@link AuthenticationProvider} used for + * authenticating an {@link OidcUserInfoAuthenticationToken} * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -119,11 +133,12 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Sets the {@code Consumer} providing access to the {@code List} of default - * and (optionally) added {@link #authenticationProvider(AuthenticationProvider) AuthenticationProvider}'s - * allowing the ability to add, remove, or customize a specific {@link AuthenticationProvider}. - * - * @param authenticationProvidersConsumer the {@code Consumer} providing access to the {@code List} of default and (optionally) added {@link AuthenticationProvider}'s + * Sets the {@code Consumer} providing access to the {@code List} of default and + * (optionally) added {@link #authenticationProvider(AuthenticationProvider) + * AuthenticationProvider}'s allowing the ability to add, remove, or customize a + * specific {@link AuthenticationProvider}. + * @param authenticationProvidersConsumer the {@code Consumer} providing access to the + * {@code List} of default and (optionally) added {@link AuthenticationProvider}'s * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -135,23 +150,26 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken} - * and returning the {@link OidcUserInfo UserInfo Response}. - * - * @param userInfoResponseHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcUserInfoAuthenticationToken} and returning the {@link OidcUserInfo + * UserInfo Response}. + * @param userInfoResponseHandler the {@link AuthenticationSuccessHandler} used for + * handling an {@link OidcUserInfoAuthenticationToken} * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ - public OidcUserInfoEndpointConfigurer userInfoResponseHandler(AuthenticationSuccessHandler userInfoResponseHandler) { + public OidcUserInfoEndpointConfigurer userInfoResponseHandler( + AuthenticationSuccessHandler userInfoResponseHandler) { this.userInfoResponseHandler = userInfoResponseHandler; return this; } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param errorResponseHandler the {@link AuthenticationFailureHandler} used for + * handling an {@link OAuth2AuthenticationException} * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration * @since 0.4.0 */ @@ -161,19 +179,23 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur } /** - * Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} - * to an instance of {@link OidcUserInfo} for the UserInfo response. + * Sets the {@link Function} used to extract claims from + * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} + * for the UserInfo response. * *

- * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}, - * as well as, the following context attributes: + * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the + * {@link OidcUserInfoAuthenticationToken}, as well as, the following context + * attributes: *

    - *
  • {@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.
  • - *
  • {@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and - * {@link OAuth2AccessToken} associated with the bearer token used to make the request.
  • + *
  • {@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the + * bearer token used to make the request.
  • + *
  • {@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the + * {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token + * used to make the request.
  • *
- * - * @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} + * @param userInfoMapper the {@link Function} used to extract claims from + * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} * @return the {@link OidcUserInfoEndpointConfigurer} for further configuration */ public OidcUserInfoEndpointConfigurer userInfoMapper( @@ -184,7 +206,8 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur @Override void init(HttpSecurity httpSecurity) { - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); String userInfoEndpointUri = authorizationServerSettings.getOidcUserInfoEndpoint(); this.requestMatcher = new OrRequestMatcher( new AntPathRequestMatcher(userInfoEndpointUri, HttpMethod.GET.name()), @@ -195,26 +218,25 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur authenticationProviders.addAll(0, this.authenticationProviders); } this.authenticationProvidersConsumer.accept(authenticationProviders); - authenticationProviders.forEach(authenticationProvider -> - httpSecurity.authenticationProvider(postProcess(authenticationProvider))); + authenticationProviders.forEach( + authenticationProvider -> httpSecurity.authenticationProvider(postProcess(authenticationProvider))); } @Override void configure(HttpSecurity httpSecurity) { AuthenticationManager authenticationManager = httpSecurity.getSharedObject(AuthenticationManager.class); - AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils.getAuthorizationServerSettings(httpSecurity); + AuthorizationServerSettings authorizationServerSettings = OAuth2ConfigurerUtils + .getAuthorizationServerSettings(httpSecurity); - OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter = - new OidcUserInfoEndpointFilter( - authenticationManager, - authorizationServerSettings.getOidcUserInfoEndpoint()); + OidcUserInfoEndpointFilter oidcUserInfoEndpointFilter = new OidcUserInfoEndpointFilter(authenticationManager, + authorizationServerSettings.getOidcUserInfoEndpoint()); List authenticationConverters = createDefaultAuthenticationConverters(); if (!this.userInfoRequestConverters.isEmpty()) { authenticationConverters.addAll(0, this.userInfoRequestConverters); } this.userInfoRequestConvertersConsumer.accept(authenticationConverters); - oidcUserInfoEndpointFilter.setAuthenticationConverter( - new DelegatingAuthenticationConverter(authenticationConverters)); + oidcUserInfoEndpointFilter + .setAuthenticationConverter(new DelegatingAuthenticationConverter(authenticationConverters)); if (this.userInfoResponseHandler != null) { oidcUserInfoEndpointFilter.setAuthenticationSuccessHandler(this.userInfoResponseHandler); } @@ -232,12 +254,10 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur private static List createDefaultAuthenticationConverters() { List authenticationConverters = new ArrayList<>(); - authenticationConverters.add( - (request) -> { - Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); - return new OidcUserInfoAuthenticationToken(authentication); - } - ); + authenticationConverters.add((request) -> { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + return new OidcUserInfoAuthenticationToken(authentication); + }); return authenticationConverters; } @@ -245,9 +265,8 @@ public final class OidcUserInfoEndpointConfigurer extends AbstractOAuth2Configur private List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); - OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider = - new OidcUserInfoAuthenticationProvider( - OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); + OidcUserInfoAuthenticationProvider oidcUserInfoAuthenticationProvider = new OidcUserInfoAuthenticationProvider( + OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity)); if (this.userInfoMapper != null) { oidcUserInfoAuthenticationProvider.setUserInfoMapper(this.userInfoMapper); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java index a12ef305..17cc3dce 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContext.java @@ -29,14 +29,12 @@ public interface AuthorizationServerContext { /** * Returns the {@code URL} of the Authorization Server's issuer identifier. - * * @return the {@code URL} of the Authorization Server's issuer identifier */ String getIssuer(); /** * Returns the {@link AuthorizationServerSettings}. - * * @return the {@link AuthorizationServerSettings} */ AuthorizationServerSettings getAuthorizationServerSettings(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java index fa70ae44..0519109c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/AuthorizationServerContextHolder.java @@ -16,13 +16,15 @@ package org.springframework.security.oauth2.server.authorization.context; /** - * A holder of the {@link AuthorizationServerContext} that associates it with the current thread using a {@code ThreadLocal}. + * A holder of the {@link AuthorizationServerContext} that associates it with the current + * thread using a {@code ThreadLocal}. * * @author Joe Grandja * @since 0.2.2 * @see AuthorizationServerContext */ public final class AuthorizationServerContextHolder { + private static final ThreadLocal holder = new ThreadLocal<>(); private AuthorizationServerContextHolder() { @@ -30,7 +32,6 @@ public final class AuthorizationServerContextHolder { /** * Returns the {@link AuthorizationServerContext} bound to the current thread. - * * @return the {@link AuthorizationServerContext} */ public static AuthorizationServerContext getContext() { @@ -39,13 +40,13 @@ public final class AuthorizationServerContextHolder { /** * Bind the given {@link AuthorizationServerContext} to the current thread. - * * @param authorizationServerContext the {@link AuthorizationServerContext} */ public static void setContext(AuthorizationServerContext authorizationServerContext) { if (authorizationServerContext == null) { resetContext(); - } else { + } + else { holder.set(authorizationServerContext); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java index 865fb974..bc0b5670 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/context/Context.java @@ -28,20 +28,20 @@ public interface Context { /** * Returns the value of the attribute associated to the key. - * * @param key the key for the attribute * @param the type of the value for the attribute - * @return the value of the attribute associated to the key, or {@code null} if not available + * @return the value of the attribute associated to the key, or {@code null} if not + * available */ @Nullable V get(Object key); /** * Returns the value of the attribute associated to the key. - * * @param key the key for the attribute * @param the type of the value for the attribute - * @return the value of the attribute associated to the key, or {@code null} if not available or not of the specified type + * @return the value of the attribute associated to the key, or {@code null} if not + * available or not of the specified type */ @Nullable default V get(Class key) { @@ -51,10 +51,11 @@ public interface Context { } /** - * Returns {@code true} if an attribute associated to the key exists, {@code false} otherwise. - * + * Returns {@code true} if an attribute associated to the key exists, {@code false} + * otherwise. * @param key the key for the attribute - * @return {@code true} if an attribute associated to the key exists, {@code false} otherwise + * @return {@code true} if an attribute associated to the key exists, {@code false} + * otherwise */ boolean hasKey(Object key); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java index af156ec0..8219fecf 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverter.java @@ -38,7 +38,8 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat import org.springframework.util.Assert; /** - * A {@link HttpMessageConverter} for an {@link OAuth2AuthorizationServerMetadata OAuth 2.0 Authorization Server Metadata Response}. + * A {@link HttpMessageConverter} for an {@link OAuth2AuthorizationServerMetadata OAuth + * 2.0 Authorization Server Metadata Response}. * * @author Daniel Garnier-Moiroux * @since 0.1.1 @@ -48,12 +49,14 @@ import org.springframework.util.Assert; public class OAuth2AuthorizationServerMetadataHttpMessageConverter extends AbstractHttpMessageConverter { - private static final ParameterizedTypeReference> STRING_OBJECT_MAP = - new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { + }; - private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); + private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters + .getJsonMessageConverter(); private Converter, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter = new OAuth2AuthorizationServerMetadataConverter(); + private Converter> authorizationServerMetadataParametersConverter = OAuth2AuthorizationServerMetadata::getClaims; public OAuth2AuthorizationServerMetadataHttpMessageConverter() { @@ -67,65 +70,74 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter @Override @SuppressWarnings("unchecked") - protected OAuth2AuthorizationServerMetadata readInternal(Class clazz, HttpInputMessage inputMessage) - throws HttpMessageNotReadableException { + protected OAuth2AuthorizationServerMetadata readInternal(Class clazz, + HttpInputMessage inputMessage) throws HttpMessageNotReadableException { try { - Map authorizationServerMetadataParameters = - (Map) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, inputMessage); + Map authorizationServerMetadataParameters = (Map) this.jsonMessageConverter + .read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.authorizationServerMetadataConverter.convert(authorizationServerMetadataParameters); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotReadableException( - "An error occurred reading the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex, inputMessage); + "An error occurred reading the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex, + inputMessage); } } @Override - protected void writeInternal(OAuth2AuthorizationServerMetadata authorizationServerMetadata, HttpOutputMessage outputMessage) - throws HttpMessageNotWritableException { + protected void writeInternal(OAuth2AuthorizationServerMetadata authorizationServerMetadata, + HttpOutputMessage outputMessage) throws HttpMessageNotWritableException { try { - Map authorizationServerMetadataResponseParameters = - this.authorizationServerMetadataParametersConverter.convert(authorizationServerMetadata); - this.jsonMessageConverter.write( - authorizationServerMetadataResponseParameters, - STRING_OBJECT_MAP.getType(), - MediaType.APPLICATION_JSON, - outputMessage - ); - } catch (Exception ex) { + Map authorizationServerMetadataResponseParameters = this.authorizationServerMetadataParametersConverter + .convert(authorizationServerMetadata); + this.jsonMessageConverter.write(authorizationServerMetadataResponseParameters, STRING_OBJECT_MAP.getType(), + MediaType.APPLICATION_JSON, outputMessage); + } + catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the OAuth 2.0 Authorization Server Metadata: " + ex.getMessage(), ex); } } /** - * Sets the {@link Converter} used for converting the OAuth 2.0 Authorization Server Metadata - * parameters to an {@link OAuth2AuthorizationServerMetadata}. - * - * @param authorizationServerMetadataConverter the {@link Converter} used for converting to - * an {@link OAuth2AuthorizationServerMetadata}. + * Sets the {@link Converter} used for converting the OAuth 2.0 Authorization Server + * Metadata parameters to an {@link OAuth2AuthorizationServerMetadata}. + * @param authorizationServerMetadataConverter the {@link Converter} used for + * converting to an {@link OAuth2AuthorizationServerMetadata}. */ - public final void setAuthorizationServerMetadataConverter(Converter, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter) { + public final void setAuthorizationServerMetadataConverter( + Converter, OAuth2AuthorizationServerMetadata> authorizationServerMetadataConverter) { Assert.notNull(authorizationServerMetadataConverter, "authorizationServerMetadataConverter cannot be null"); this.authorizationServerMetadataConverter = authorizationServerMetadataConverter; } /** - * Sets the {@link Converter} used for converting the {@link OAuth2AuthorizationServerMetadata} to a - * {@code Map} representation of the OAuth 2.0 Authorization Server Metadata. - * - * @param authorizationServerMetadataParametersConverter the {@link Converter} used for converting to a - * {@code Map} representation of the OAuth 2.0 Authorization Server Metadata. + * Sets the {@link Converter} used for converting the + * {@link OAuth2AuthorizationServerMetadata} to a {@code Map} representation of the + * OAuth 2.0 Authorization Server Metadata. + * @param authorizationServerMetadataParametersConverter the {@link Converter} used + * for converting to a {@code Map} representation of the OAuth 2.0 Authorization + * Server Metadata. */ - public final void setAuthorizationServerMetadataParametersConverter(Converter> authorizationServerMetadataParametersConverter) { - Assert.notNull(authorizationServerMetadataParametersConverter, "authorizationServerMetadataParametersConverter cannot be null"); + public final void setAuthorizationServerMetadataParametersConverter( + Converter> authorizationServerMetadataParametersConverter) { + Assert.notNull(authorizationServerMetadataParametersConverter, + "authorizationServerMetadataParametersConverter cannot be null"); this.authorizationServerMetadataParametersConverter = authorizationServerMetadataParametersConverter; } - private static final class OAuth2AuthorizationServerMetadataConverter implements Converter, OAuth2AuthorizationServerMetadata> { - private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); + private static final class OAuth2AuthorizationServerMetadataConverter + implements Converter, OAuth2AuthorizationServerMetadata> { + + private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService + .getSharedInstance(); + private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class); + private final ClaimTypeConverter claimTypeConverter; private OAuth2AuthorizationServerMetadataConverter() { @@ -136,18 +148,27 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter Map> claimConverters = new HashMap<>(); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, urlConverter); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, urlConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, urlConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.DEVICE_AUTHORIZATION_ENDPOINT, + urlConverter); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, urlConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + collectionStringConverter); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, urlConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, collectionStringConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, collectionStringConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, + collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, + collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, + collectionStringConverter); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, urlConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, + collectionStringConverter); claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, urlConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, collectionStringConverter); - claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, collectionStringConverter); + claimConverters.put( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, + collectionStringConverter); + claimConverters.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, + collectionStringConverter); this.claimTypeConverter = new ClaimTypeConverter(claimConverters); } @@ -160,6 +181,7 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverter private static Converter getConverter(TypeDescriptor targetDescriptor) { return (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor); } + } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java index cbb764a2..46310698 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverter.java @@ -46,7 +46,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * A {@link HttpMessageConverter} for an {@link OAuth2TokenIntrospection OAuth 2.0 Token Introspection Response}. + * A {@link HttpMessageConverter} for an {@link OAuth2TokenIntrospection OAuth 2.0 Token + * Introspection Response}. * * @author Gerardo Roza * @author Joe Grandja @@ -54,14 +55,17 @@ import org.springframework.util.StringUtils; * @see AbstractHttpMessageConverter * @see OAuth2TokenIntrospection */ -public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMessageConverter { +public class OAuth2TokenIntrospectionHttpMessageConverter + extends AbstractHttpMessageConverter { private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { }; - private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); + private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters + .getJsonMessageConverter(); private Converter, OAuth2TokenIntrospection> tokenIntrospectionConverter = new MapOAuth2TokenIntrospectionConverter(); + private Converter> tokenIntrospectionParametersConverter = new OAuth2TokenIntrospectionMapConverter(); public OAuth2TokenIntrospectionHttpMessageConverter() { @@ -75,13 +79,14 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe @Override @SuppressWarnings("unchecked") - protected OAuth2TokenIntrospection readInternal(Class clazz, HttpInputMessage inputMessage) - throws HttpMessageNotReadableException { + protected OAuth2TokenIntrospection readInternal(Class clazz, + HttpInputMessage inputMessage) throws HttpMessageNotReadableException { try { Map tokenIntrospectionParameters = (Map) this.jsonMessageConverter - .read(STRING_OBJECT_MAP.getType(), null, inputMessage); + .read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.tokenIntrospectionConverter.convert(tokenIntrospectionParameters); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotReadableException( "An error occurred reading the Token Introspection Response: " + ex.getMessage(), ex, inputMessage); } @@ -92,19 +97,21 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe throws HttpMessageNotWritableException { try { Map tokenIntrospectionResponseParameters = this.tokenIntrospectionParametersConverter - .convert(tokenIntrospection); + .convert(tokenIntrospection); this.jsonMessageConverter.write(tokenIntrospectionResponseParameters, STRING_OBJECT_MAP.getType(), MediaType.APPLICATION_JSON, outputMessage); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the Token Introspection Response: " + ex.getMessage(), ex); } } /** - * Sets the {@link Converter} used for converting the Token Introspection Response parameters to an {@link OAuth2TokenIntrospection}. - * - * @param tokenIntrospectionConverter the {@link Converter} used for converting to an {@link OAuth2TokenIntrospection} + * Sets the {@link Converter} used for converting the Token Introspection Response + * parameters to an {@link OAuth2TokenIntrospection}. + * @param tokenIntrospectionConverter the {@link Converter} used for converting to an + * {@link OAuth2TokenIntrospection} */ public final void setTokenIntrospectionConverter( Converter, OAuth2TokenIntrospection> tokenIntrospectionConverter) { @@ -115,9 +122,9 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe /** * Sets the {@link Converter} used for converting an {@link OAuth2TokenIntrospection} * to a {@code Map} representation of the Token Introspection Response parameters. - * - * @param tokenIntrospectionParametersConverter the {@link Converter} used for converting to a - * {@code Map} representation of the Token Introspection Response parameters + * @param tokenIntrospectionParametersConverter the {@link Converter} used for + * converting to a {@code Map} representation of the Token Introspection Response + * parameters */ public final void setTokenIntrospectionParametersConverter( Converter> tokenIntrospectionParametersConverter) { @@ -128,12 +135,19 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe private static final class MapOAuth2TokenIntrospectionConverter implements Converter, OAuth2TokenIntrospection> { - private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); + private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService + .getSharedInstance(); + private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); + private static final TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class); + private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class); + private final ClaimTypeConverter claimTypeConverter; private MapOAuth2TokenIntrospectionConverter() { @@ -146,7 +160,8 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe Map> claimConverters = new HashMap<>(); claimConverters.put(OAuth2TokenIntrospectionClaimNames.ACTIVE, booleanConverter); - claimConverters.put(OAuth2TokenIntrospectionClaimNames.SCOPE, MapOAuth2TokenIntrospectionConverter::convertScope); + claimConverters.put(OAuth2TokenIntrospectionClaimNames.SCOPE, + MapOAuth2TokenIntrospectionConverter::convertScope); claimConverters.put(OAuth2TokenIntrospectionClaimNames.CLIENT_ID, stringConverter); claimConverters.put(OAuth2TokenIntrospectionClaimNames.USERNAME, stringConverter); claimConverters.put(OAuth2TokenIntrospectionClaimNames.TOKEN_TYPE, stringConverter); @@ -176,6 +191,7 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe } return Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), " ")); } + } private static final class OAuth2TokenIntrospectionMapConverter @@ -185,7 +201,8 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe public Map convert(OAuth2TokenIntrospection source) { Map responseClaims = new LinkedHashMap<>(source.getClaims()); if (!CollectionUtils.isEmpty(source.getScopes())) { - responseClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, StringUtils.collectionToDelimitedString(source.getScopes(), " ")); + responseClaims.put(OAuth2TokenIntrospectionClaimNames.SCOPE, + StringUtils.collectionToDelimitedString(source.getScopes(), " ")); } if (source.getExpiresAt() != null) { responseClaims.put(OAuth2TokenIntrospectionClaimNames.EXP, source.getExpiresAt().getEpochSecond()); @@ -198,6 +215,7 @@ public class OAuth2TokenIntrospectionHttpMessageConverter extends AbstractHttpMe } return responseClaims; } + } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java index eee09cc4..fc7dd6c6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/JwsAlgorithmMixin.java @@ -31,4 +31,5 @@ import org.springframework.security.oauth2.jose.jws.SignatureAlgorithm; @JsonAutoDetect(fieldVisibility = JsonAutoDetect.Visibility.ANY, getterVisibility = JsonAutoDetect.Visibility.NONE, isGetterVisibility = JsonAutoDetect.Visibility.NONE) abstract class JwsAlgorithmMixin { + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java index ec8a6ab3..a8605c07 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationRequestDeserializer.java @@ -63,8 +63,8 @@ final class OAuth2AuthorizationRequestDeserializer extends JsonDeserializer2. Client Metadata - * @see 3.1. Client Registration Metadata + * @see 2. + * Client Metadata + * @see 3.1. + * Client Registration Metadata */ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { /** * Returns the Client Identifier {@code (client_id)}. - * * @return the Client Identifier */ default String getClientId() { @@ -51,8 +54,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the time at which the Client Identifier was issued {@code (client_id_issued_at)}. - * + * Returns the time at which the Client Identifier was issued + * {@code (client_id_issued_at)}. * @return the time at which the Client Identifier was issued */ default Instant getClientIdIssuedAt() { @@ -61,7 +64,6 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { /** * Returns the Client Secret {@code (client_secret)}. - * * @return the Client Secret */ default String getClientSecret() { @@ -69,8 +71,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the time at which the {@code client_secret} will expire {@code (client_secret_expires_at)}. - * + * Returns the time at which the {@code client_secret} will expire + * {@code (client_secret_expires_at)}. * @return the time at which the {@code client_secret} will expire */ default Instant getClientSecretExpiresAt() { @@ -78,8 +80,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the name of the Client to be presented to the End-User {@code (client_name)}. - * + * Returns the name of the Client to be presented to the End-User + * {@code (client_name)}. * @return the name of the Client to be presented to the End-User */ default String getClientName() { @@ -87,8 +89,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the redirection {@code URI} values used by the Client {@code (redirect_uris)}. - * + * Returns the redirection {@code URI} values used by the Client + * {@code (redirect_uris)}. * @return the redirection {@code URI} values used by the Client */ default List getRedirectUris() { @@ -96,10 +98,10 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the post logout redirection {@code URI} values used by the Client {@code (post_logout_redirect_uris)}. - * The {@code post_logout_redirect_uri} parameter is used by the client when requesting - * that the End-User's User Agent be redirected to after a logout has been performed. - * + * Returns the post logout redirection {@code URI} values used by the Client + * {@code (post_logout_redirect_uris)}. The {@code post_logout_redirect_uri} parameter + * is used by the client when requesting that the End-User's User Agent be redirected + * to after a logout has been performed. * @return the post logout redirection {@code URI} values used by the Client * @since 1.1 */ @@ -108,8 +110,8 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the authentication method used by the Client for the Token Endpoint {@code (token_endpoint_auth_method)}. - * + * Returns the authentication method used by the Client for the Token Endpoint + * {@code (token_endpoint_auth_method)}. * @return the authentication method used by the Client for the Token Endpoint */ default String getTokenEndpointAuthenticationMethod() { @@ -117,11 +119,13 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate - * the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods {@code (token_endpoint_auth_signing_alg)}. - * - * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate the Client at the Token Endpoint + * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the + * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and + * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} + * authentication methods {@code (token_endpoint_auth_signing_alg)}. + * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint * @since 0.2.2 */ default String getTokenEndpointAuthenticationSigningAlgorithm() { @@ -129,27 +133,30 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using {@code (grant_types)}. - * - * @return the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using + * Returns the OAuth 2.0 {@code grant_type} values that the Client will restrict + * itself to using {@code (grant_types)}. + * @return the OAuth 2.0 {@code grant_type} values that the Client will restrict + * itself to using */ default List getGrantTypes() { return getClaimAsStringList(OidcClientMetadataClaimNames.GRANT_TYPES); } /** - * Returns the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using {@code (response_types)}. - * - * @return the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using + * Returns the OAuth 2.0 {@code response_type} values that the Client will restrict + * itself to using {@code (response_types)}. + * @return the OAuth 2.0 {@code response_type} values that the Client will restrict + * itself to using */ default List getResponseTypes() { return getClaimAsStringList(OidcClientMetadataClaimNames.RESPONSE_TYPES); } /** - * Returns the OAuth 2.0 {@code scope} values that the Client will restrict itself to using {@code (scope)}. - * - * @return the OAuth 2.0 {@code scope} values that the Client will restrict itself to using + * Returns the OAuth 2.0 {@code scope} values that the Client will restrict itself to + * using {@code (scope)}. + * @return the OAuth 2.0 {@code scope} values that the Client will restrict itself to + * using */ default List getScopes() { return getClaimAsStringList(OidcClientMetadataClaimNames.SCOPE); @@ -157,7 +164,6 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { /** * Returns the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)}. - * * @return the {@code URL} for the Client's JSON Web Key Set {@code (jwks_uri)} * @since 0.2.2 */ @@ -166,18 +172,21 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client {@code (id_token_signed_response_alg)}. - * - * @return the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client + * Returns the {@link SignatureAlgorithm JWS} algorithm required for signing the + * {@link OidcIdToken ID Token} issued to the Client + * {@code (id_token_signed_response_alg)}. + * @return the {@link SignatureAlgorithm JWS} algorithm required for signing the + * {@link OidcIdToken ID Token} issued to the Client */ default String getIdTokenSignedResponseAlgorithm() { return getClaimAsString(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG); } /** - * Returns the Registration Access Token that can be used at the Client Configuration Endpoint. - * - * @return the Registration Access Token that can be used at the Client Configuration Endpoint + * Returns the Registration Access Token that can be used at the Client Configuration + * Endpoint. + * @return the Registration Access Token that can be used at the Client Configuration + * Endpoint * @since 0.2.1 */ default String getRegistrationAccessToken() { @@ -185,9 +194,10 @@ public interface OidcClientMetadataClaimAccessor extends ClaimAccessor { } /** - * Returns the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used. - * - * @return the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used + * Returns the {@code URL} of the Client Configuration Endpoint where the Registration + * Access Token can be used. + * @return the {@code URL} of the Client Configuration Endpoint where the Registration + * Access Token can be used * @since 0.2.1 */ default URL getRegistrationClientUrl() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimNames.java index d39990e5..6d835958 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientMetadataClaimNames.java @@ -27,8 +27,12 @@ import org.springframework.security.oauth2.jwt.Jwt; * @author Ovidiu Popa * @author Joe Grandja * @since 0.1.1 - * @see 2. Client Metadata - * @see 3.1. Client Registration Metadata + * @see 2. + * Client Metadata + * @see 3.1. + * Client Registration Metadata */ public final class OidcClientMetadataClaimNames { @@ -48,7 +52,8 @@ public final class OidcClientMetadataClaimNames { public static final String CLIENT_SECRET = "client_secret"; /** - * {@code client_secret_expires_at} - the time at which the {@code client_secret} will expire or 0 if it will not expire + * {@code client_secret_expires_at} - the time at which the {@code client_secret} will + * expire or 0 if it will not expire */ public static final String CLIENT_SECRET_EXPIRES_AT = "client_secret_expires_at"; @@ -63,38 +68,45 @@ public final class OidcClientMetadataClaimNames { public static final String REDIRECT_URIS = "redirect_uris"; /** - * {@code post_logout_redirect_uris} - the post logout redirection {@code URI} values used by the Client. - * The {@code post_logout_redirect_uri} parameter is used by the client when requesting - * that the End-User's User Agent be redirected to after a logout has been performed. + * {@code post_logout_redirect_uris} - the post logout redirection {@code URI} values + * used by the Client. The {@code post_logout_redirect_uri} parameter is used by the + * client when requesting that the End-User's User Agent be redirected to after a + * logout has been performed. * @since 1.1 */ public static final String POST_LOGOUT_REDIRECT_URIS = "post_logout_redirect_uris"; /** - * {@code token_endpoint_auth_method} - the authentication method used by the Client for the Token Endpoint + * {@code token_endpoint_auth_method} - the authentication method used by the Client + * for the Token Endpoint */ public static final String TOKEN_ENDPOINT_AUTH_METHOD = "token_endpoint_auth_method"; /** - * {@code token_endpoint_auth_signing_alg} - the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} - * used to authenticate the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods + * {@code token_endpoint_auth_signing_alg} - the {@link JwsAlgorithm JWS} algorithm + * that must be used for signing the {@link Jwt JWT} used to authenticate the Client + * at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT + * private_key_jwt} and {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT + * client_secret_jwt} authentication methods * @since 0.2.2 */ public static final String TOKEN_ENDPOINT_AUTH_SIGNING_ALG = "token_endpoint_auth_signing_alg"; /** - * {@code grant_types} - the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using + * {@code grant_types} - the OAuth 2.0 {@code grant_type} values that the Client will + * restrict itself to using */ public static final String GRANT_TYPES = "grant_types"; /** - * {@code response_types} - the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using + * {@code response_types} - the OAuth 2.0 {@code response_type} values that the Client + * will restrict itself to using */ public static final String RESPONSE_TYPES = "response_types"; /** - * {@code scope} - a space-separated list of OAuth 2.0 {@code scope} values that the Client will restrict itself to using + * {@code scope} - a space-separated list of OAuth 2.0 {@code scope} values that the + * Client will restrict itself to using */ public static final String SCOPE = "scope"; @@ -105,18 +117,21 @@ public final class OidcClientMetadataClaimNames { public static final String JWKS_URI = "jwks_uri"; /** - * {@code id_token_signed_response_alg} - the {@link JwsAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client + * {@code id_token_signed_response_alg} - the {@link JwsAlgorithm JWS} algorithm + * required for signing the {@link OidcIdToken ID Token} issued to the Client */ public static final String ID_TOKEN_SIGNED_RESPONSE_ALG = "id_token_signed_response_alg"; /** - * {@code registration_access_token} - the Registration Access Token that can be used at the Client Configuration Endpoint + * {@code registration_access_token} - the Registration Access Token that can be used + * at the Client Configuration Endpoint * @since 0.2.1 */ public static final String REGISTRATION_ACCESS_TOKEN = "registration_access_token"; /** - * {@code registration_client_uri} - the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used + * {@code registration_client_uri} - the {@code URL} of the Client Configuration + * Endpoint where the Registration Access Token can be used * @since 0.2.1 */ public static final String REGISTRATION_CLIENT_URI = "registration_client_uri"; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistration.java index 07490727..a43bd291 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistration.java @@ -35,21 +35,29 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * A representation of an OpenID Client Registration Request and Response, - * which is sent to and returned from the Client Registration Endpoint, - * and contains a set of claims about the Client's Registration information. - * The claims are defined by the OpenID Connect Dynamic Client Registration 1.0 specification. + * A representation of an OpenID Client Registration Request and Response, which is sent + * to and returned from the Client Registration Endpoint, and contains a set of claims + * about the Client's Registration information. The claims are defined by the OpenID + * Connect Dynamic Client Registration 1.0 specification. * * @author Ovidiu Popa * @author Joe Grandja * @since 0.1.1 * @see OidcClientMetadataClaimAccessor - * @see 3.1. Client Registration Request - * @see 3.2. Client Registration Response - * @see 3.1. Client Registration Metadata + * @see 3.1. + * Client Registration Request + * @see 3.2. + * Client Registration Response + * @see 3.1. + * Client Registration Metadata */ public final class OidcClientRegistration implements OidcClientMetadataClaimAccessor, Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Map claims; private OidcClientRegistration(Map claims) { @@ -59,7 +67,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Returns the metadata as claims. - * * @return a {@code Map} of the metadata as claims */ @Override @@ -69,7 +76,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Constructs a new {@link Builder} with empty claims. - * * @return the {@link Builder} */ public static Builder builder() { @@ -78,19 +84,18 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Constructs a new {@link Builder} with the provided claims. - * * @param claims the claims to initialize the builder */ public static Builder withClaims(Map claims) { Assert.notEmpty(claims, "claims cannot be empty"); - return new Builder() - .claims(c -> c.putAll(claims)); + return new Builder().claims(c -> c.putAll(claims)); } /** * Helps configure an {@link OidcClientRegistration}. */ public static class Builder { + private final Map claims = new LinkedHashMap<>(); private Builder() { @@ -98,7 +103,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Sets the Client Identifier, REQUIRED. - * * @param clientId the Client Identifier * @return the {@link Builder} for further configuration */ @@ -108,7 +112,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Sets the time at which the Client Identifier was issued, OPTIONAL. - * * @param clientIdIssuedAt the time at which the Client Identifier was issued * @return the {@link Builder} for further configuration */ @@ -118,7 +121,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Sets the Client Secret, OPTIONAL. - * * @param clientSecret the Client Secret * @return the {@link Builder} for further configuration */ @@ -127,9 +129,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the time at which the {@code client_secret} will expire or {@code null} if it will not expire, REQUIRED if {@code client_secret} was issued. - * - * @param clientSecretExpiresAt the time at which the {@code client_secret} will expire or {@code null} if it will not expire + * Sets the time at which the {@code client_secret} will expire or {@code null} if + * it will not expire, REQUIRED if {@code client_secret} was issued. + * @param clientSecretExpiresAt the time at which the {@code client_secret} will + * expire or {@code null} if it will not expire * @return the {@link Builder} for further configuration */ public Builder clientSecretExpiresAt(Instant clientSecretExpiresAt) { @@ -138,7 +141,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Sets the name of the Client to be presented to the End-User, OPTIONAL. - * * @param clientName the name of the Client to be presented to the End-User * @return the {@link Builder} for further configuration */ @@ -148,7 +150,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Add the redirection {@code URI} used by the Client, REQUIRED. - * * @param redirectUri the redirection {@code URI} used by the Client * @return the {@link Builder} for further configuration */ @@ -160,8 +161,8 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * A {@code Consumer} of the redirection {@code URI} values used by the Client, * allowing the ability to add, replace, or remove, REQUIRED. - * - * @param redirectUrisConsumer a {@code Consumer} of the redirection {@code URI} values used by the Client + * @param redirectUrisConsumer a {@code Consumer} of the redirection {@code URI} + * values used by the Client * @return the {@link Builder} for further configuration */ public Builder redirectUris(Consumer> redirectUrisConsumer) { @@ -170,11 +171,12 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Add the post logout redirection {@code URI} used by the Client, OPTIONAL. - * The {@code post_logout_redirect_uri} parameter is used by the client when requesting - * that the End-User's User Agent be redirected to after a logout has been performed. - * - * @param postLogoutRedirectUri the post logout redirection {@code URI} used by the Client + * Add the post logout redirection {@code URI} used by the Client, OPTIONAL. The + * {@code post_logout_redirect_uri} parameter is used by the client when + * requesting that the End-User's User Agent be redirected to after a logout has + * been performed. + * @param postLogoutRedirectUri the post logout redirection {@code URI} used by + * the Client * @return the {@link Builder} for further configuration * @since 1.1 */ @@ -184,10 +186,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * A {@code Consumer} of the post logout redirection {@code URI} values used by the Client, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param postLogoutRedirectUrisConsumer a {@code Consumer} of the post logout redirection {@code URI} values used by the Client + * A {@code Consumer} of the post logout redirection {@code URI} values used by + * the Client, allowing the ability to add, replace, or remove, OPTIONAL. + * @param postLogoutRedirectUrisConsumer a {@code Consumer} of the post logout + * redirection {@code URI} values used by the Client * @return the {@link Builder} for further configuration * @since 1.1 */ @@ -197,9 +199,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the authentication method used by the Client for the Token Endpoint, OPTIONAL. - * - * @param tokenEndpointAuthenticationMethod the authentication method used by the Client for the Token Endpoint + * Sets the authentication method used by the Client for the Token Endpoint, + * OPTIONAL. + * @param tokenEndpointAuthenticationMethod the authentication method used by the + * Client for the Token Endpoint * @return the {@link Builder} for further configuration */ public Builder tokenEndpointAuthenticationMethod(String tokenEndpointAuthenticationMethod) { @@ -207,12 +210,14 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate - * the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods, OPTIONAL. - - * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} - * used to authenticate the Client at the Token Endpoint + * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the + * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and + * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} + * authentication methods, OPTIONAL. + * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm + * that must be used for signing the {@link Jwt JWT} used to authenticate the + * Client at the Token Endpoint * @return the {@link Builder} for further configuration * @since 0.2.2 */ @@ -221,9 +226,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Add the OAuth 2.0 {@code grant_type} that the Client will restrict itself to using, OPTIONAL. - * - * @param grantType the OAuth 2.0 {@code grant_type} that the Client will restrict itself to using + * Add the OAuth 2.0 {@code grant_type} that the Client will restrict itself to + * using, OPTIONAL. + * @param grantType the OAuth 2.0 {@code grant_type} that the Client will restrict + * itself to using * @return the {@link Builder} for further configuration */ public Builder grantType(String grantType) { @@ -232,10 +238,11 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client will restrict itself to using + * A {@code Consumer} of the OAuth 2.0 {@code grant_type} values that the Client + * will restrict itself to using, allowing the ability to add, replace, or remove, + * OPTIONAL. + * @param grantTypesConsumer a {@code Consumer} of the OAuth 2.0 + * {@code grant_type} values that the Client will restrict itself to using * @return the {@link Builder} for further configuration */ public Builder grantTypes(Consumer> grantTypesConsumer) { @@ -244,9 +251,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Add the OAuth 2.0 {@code response_type} that the Client will restrict itself to using, OPTIONAL. - * - * @param responseType the OAuth 2.0 {@code response_type} that the Client will restrict itself to using + * Add the OAuth 2.0 {@code response_type} that the Client will restrict itself to + * using, OPTIONAL. + * @param responseType the OAuth 2.0 {@code response_type} that the Client will + * restrict itself to using * @return the {@link Builder} for further configuration */ public Builder responseType(String responseType) { @@ -255,21 +263,23 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * A {@code Consumer} of the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 {@code response_type} values that the Client will restrict itself to using + * A {@code Consumer} of the OAuth 2.0 {@code response_type} values that the + * Client will restrict itself to using, allowing the ability to add, replace, or + * remove, OPTIONAL. + * @param responseTypesConsumer a {@code Consumer} of the OAuth 2.0 + * {@code response_type} values that the Client will restrict itself to using * @return the {@link Builder} for further configuration */ - public Builder responseTypes(Consumer> responseTypesConsumer) { + public Builder responseTypes(Consumer> responseTypesConsumer) { acceptClaimValues(OidcClientMetadataClaimNames.RESPONSE_TYPES, responseTypesConsumer); return this; } /** - * Add the OAuth 2.0 {@code scope} that the Client will restrict itself to using, OPTIONAL. - * - * @param scope the OAuth 2.0 {@code scope} that the Client will restrict itself to using + * Add the OAuth 2.0 {@code scope} that the Client will restrict itself to using, + * OPTIONAL. + * @param scope the OAuth 2.0 {@code scope} that the Client will restrict itself + * to using * @return the {@link Builder} for further configuration */ public Builder scope(String scope) { @@ -278,20 +288,20 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * A {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will restrict itself to using, - * allowing the ability to add, replace, or remove, OPTIONAL. - * - * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will restrict itself to using + * A {@code Consumer} of the OAuth 2.0 {@code scope} values that the Client will + * restrict itself to using, allowing the ability to add, replace, or remove, + * OPTIONAL. + * @param scopesConsumer a {@code Consumer} of the OAuth 2.0 {@code scope} values + * that the Client will restrict itself to using * @return the {@link Builder} for further configuration */ - public Builder scopes(Consumer> scopesConsumer) { + public Builder scopes(Consumer> scopesConsumer) { acceptClaimValues(OidcClientMetadataClaimNames.SCOPE, scopesConsumer); return this; } /** * Sets the {@code URL} for the Client's JSON Web Key Set, OPTIONAL. - * * @param jwkSetUrl the {@code URL} for the Client's JSON Web Key Set * @return the {@link Builder} for further configuration * @since 0.2.2 @@ -301,9 +311,11 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client, OPTIONAL. - * - * @param idTokenSignedResponseAlgorithm the {@link SignatureAlgorithm JWS} algorithm required for signing the {@link OidcIdToken ID Token} issued to the Client + * Sets the {@link SignatureAlgorithm JWS} algorithm required for signing the + * {@link OidcIdToken ID Token} issued to the Client, OPTIONAL. + * @param idTokenSignedResponseAlgorithm the {@link SignatureAlgorithm JWS} + * algorithm required for signing the {@link OidcIdToken ID Token} issued to the + * Client * @return the {@link Builder} for further configuration */ public Builder idTokenSignedResponseAlgorithm(String idTokenSignedResponseAlgorithm) { @@ -311,9 +323,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the Registration Access Token that can be used at the Client Configuration Endpoint, OPTIONAL. - * - * @param registrationAccessToken the Registration Access Token that can be used at the Client Configuration Endpoint + * Sets the Registration Access Token that can be used at the Client Configuration + * Endpoint, OPTIONAL. + * @param registrationAccessToken the Registration Access Token that can be used + * at the Client Configuration Endpoint * @return the {@link Builder} for further configuration * @since 0.2.1 */ @@ -322,9 +335,10 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } /** - * Sets the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used, OPTIONAL. - * - * @param registrationClientUrl the {@code URL} of the Client Configuration Endpoint where the Registration Access Token can be used + * Sets the {@code URL} of the Client Configuration Endpoint where the + * Registration Access Token can be used, OPTIONAL. + * @param registrationClientUrl the {@code URL} of the Client Configuration + * Endpoint where the Registration Access Token can be used * @return the {@link Builder} for further configuration * @since 0.2.1 */ @@ -334,8 +348,7 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Sets the claim. - * - * @param name the claim name + * @param name the claim name * @param value the claim value * @return the {@link Builder} for further configuration */ @@ -349,7 +362,6 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Provides access to every {@link #claim(String, Object)} declared so far * allowing the ability to add, replace, or remove. - * * @param claimsConsumer a {@code Consumer} of the claims * @return the {@link Builder} for further configurations */ @@ -361,9 +373,7 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce /** * Validate the claims and build the {@link OidcClientRegistration}. *

- * The following claims are REQUIRED: - * {@code client_id}, {@code redirect_uris}. - * + * The following claims are REQUIRED: {@code client_id}, {@code redirect_uris}. * @return the {@link OidcClientRegistration} */ public OidcClientRegistration build() { @@ -372,34 +382,47 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce } private void validate() { - if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null || - this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET) != null) { + if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null + || this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET) != null) { Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID), "client_id cannot be null"); } if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT) != null) { - Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT), "client_id_issued_at must be of type Instant"); + Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT), + "client_id_issued_at must be of type Instant"); } if (this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT) != null) { - Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET), "client_secret cannot be null"); - Assert.isInstanceOf(Instant.class, this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT), "client_secret_expires_at must be of type Instant"); + Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET), + "client_secret cannot be null"); + Assert.isInstanceOf(Instant.class, + this.claims.get(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT), + "client_secret_expires_at must be of type Instant"); } Assert.notNull(this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris cannot be null"); - Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris must be of type List"); - Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), "redirect_uris cannot be empty"); + Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), + "redirect_uris must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.REDIRECT_URIS), + "redirect_uris cannot be empty"); if (this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS) != null) { - Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), "post_logout_redirect_uris must be of type List"); - Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), "post_logout_redirect_uris cannot be empty"); + Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), + "post_logout_redirect_uris must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS), + "post_logout_redirect_uris cannot be empty"); } if (this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES) != null) { - Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), "grant_types must be of type List"); - Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), "grant_types cannot be empty"); + Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), + "grant_types must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.GRANT_TYPES), + "grant_types cannot be empty"); } if (this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES) != null) { - Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), "response_types must be of type List"); - Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), "response_types cannot be empty"); + Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), + "response_types must be of type List"); + Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.RESPONSE_TYPES), + "response_types cannot be empty"); } if (this.claims.get(OidcClientMetadataClaimNames.SCOPE) != null) { - Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.SCOPE), "scope must be of type List"); + Assert.isInstanceOf(List.class, this.claims.get(OidcClientMetadataClaimNames.SCOPE), + "scope must be of type List"); Assert.notEmpty((List) this.claims.get(OidcClientMetadataClaimNames.SCOPE), "scope cannot be empty"); } if (this.claims.get(OidcClientMetadataClaimNames.JWKS_URI) != null) { @@ -431,7 +454,8 @@ public final class OidcClientRegistration implements OidcClientMetadataClaimAcce try { new URI(url.toString()).toURL(); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(errorMessage, ex); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfiguration.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfiguration.java index 6309c4f1..0c8a0d59 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfiguration.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfiguration.java @@ -26,17 +26,19 @@ import org.springframework.security.oauth2.server.authorization.AbstractOAuth2Au import org.springframework.util.Assert; /** - * A representation of an OpenID Provider Configuration Response, - * which is returned from an Issuer's Discovery Endpoint, - * and contains a set of claims about the OpenID Provider's configuration. - * The claims are defined by the OpenID Connect Discovery 1.0 specification. + * A representation of an OpenID Provider Configuration Response, which is returned from + * an Issuer's Discovery Endpoint, and contains a set of claims about the OpenID + * Provider's configuration. The claims are defined by the OpenID Connect Discovery 1.0 + * specification. * * @author Daniel Garnier-Moiroux * @author Joe Grandja * @since 0.1.0 * @see AbstractOAuth2AuthorizationServerMetadata * @see OidcProviderMetadataClaimAccessor - * @see 4.2. OpenID Provider Configuration Response + * @see 4.2. + * OpenID Provider Configuration Response */ public final class OidcProviderConfiguration extends AbstractOAuth2AuthorizationServerMetadata implements OidcProviderMetadataClaimAccessor { @@ -47,7 +49,6 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization /** * Constructs a new {@link Builder} with empty claims. - * * @return the {@link Builder} */ public static Builder builder() { @@ -56,13 +57,11 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization /** * Constructs a new {@link Builder} with the provided claims. - * * @param claims the claims to initialize the builder */ public static Builder withClaims(Map claims) { Assert.notEmpty(claims, "claims cannot be empty"); - return new Builder() - .claims(c -> c.putAll(claims)); + return new Builder().claims(c -> c.putAll(claims)); } /** @@ -74,9 +73,8 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } /** - * Add this Subject Type to the collection of {@code subject_types_supported} in the resulting - * {@link OidcProviderConfiguration}, REQUIRED. - * + * Add this Subject Type to the collection of {@code subject_types_supported} in + * the resulting {@link OidcProviderConfiguration}, REQUIRED. * @param subjectType the Subject Type that the OpenID Provider supports * @return the {@link Builder} for further configuration */ @@ -86,8 +84,8 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } /** - * A {@code Consumer} of the Subject Types(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 subjectTypesConsumer a {@code Consumer} of the Subject Types(s) * @return the {@link Builder} for further configuration */ @@ -97,10 +95,11 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } /** - * Add this {@link JwsAlgorithm JWS} signing algorithm to the collection of {@code id_token_signing_alg_values_supported} - * in the resulting {@link OidcProviderConfiguration}, REQUIRED. - * - * @param signingAlgorithm the {@link JwsAlgorithm JWS} signing algorithm supported for the {@link OidcIdToken ID Token} + * Add this {@link JwsAlgorithm JWS} signing algorithm to the collection of + * {@code id_token_signing_alg_values_supported} in the resulting + * {@link OidcProviderConfiguration}, REQUIRED. + * @param signingAlgorithm the {@link JwsAlgorithm JWS} signing algorithm + * supported for the {@link OidcIdToken ID Token} * @return the {@link Builder} for further configuration */ public Builder idTokenSigningAlgorithm(String signingAlgorithm) { @@ -109,21 +108,23 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } /** - * A {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the {@link OidcIdToken ID Token} - * allowing the ability to add, replace, or remove. - * - * @param signingAlgorithmsConsumer a {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the {@link OidcIdToken ID Token} + * A {@code Consumer} of the {@link JwsAlgorithm JWS} signing algorithms for the + * {@link OidcIdToken ID Token} allowing the ability to add, replace, or remove. + * @param signingAlgorithmsConsumer a {@code Consumer} of the {@link JwsAlgorithm + * JWS} signing algorithms for the {@link OidcIdToken ID Token} * @return the {@link Builder} for further configuration */ public Builder idTokenSigningAlgorithms(Consumer> signingAlgorithmsConsumer) { - acceptClaimValues(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, signingAlgorithmsConsumer); + acceptClaimValues(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, + signingAlgorithmsConsumer); return this; } /** - * Use this {@code userinfo_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL. - * - * @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint + * Use this {@code userinfo_endpoint} in the resulting + * {@link OidcProviderConfiguration}, OPTIONAL. + * @param userInfoEndpoint the {@code URL} of the OpenID Connect 1.0 UserInfo + * Endpoint * @return the {@link Builder} for further configuration * @since 0.2.2 */ @@ -132,9 +133,10 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } /** - * Use this {@code end_session_endpoint} in the resulting {@link OidcProviderConfiguration}, OPTIONAL. - * - * @param endSessionEndpoint the {@code URL} of the OpenID Connect 1.0 End Session Endpoint + * Use this {@code end_session_endpoint} in the resulting + * {@link OidcProviderConfiguration}, OPTIONAL. + * @param endSessionEndpoint the {@code URL} of the OpenID Connect 1.0 End Session + * Endpoint * @return the {@link Builder} for further configuration * @since 1.1 */ @@ -145,11 +147,10 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization /** * Validate the claims and build the {@link OidcProviderConfiguration}. *

- * The following claims are REQUIRED: - * {@code issuer}, {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri}, + * The following claims are REQUIRED: {@code issuer}, + * {@code authorization_endpoint}, {@code token_endpoint}, {@code jwks_uri}, * {@code response_types_supported}, {@code subject_types_supported} and * {@code id_token_signing_alg_values_supported}. - * * @return the {@link OidcProviderConfiguration} */ @Override @@ -162,17 +163,27 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization protected void validate() { super.validate(); Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.JWKS_URI), "jwksUri cannot be null"); - Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be null"); - Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes must be of type List"); - Assert.notEmpty((List) getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), "subjectTypes cannot be empty"); - Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be null"); - Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms must be of type List"); - Assert.notEmpty((List) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), "idTokenSigningAlgorithms cannot be empty"); + Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), + "subjectTypes cannot be null"); + Assert.isInstanceOf(List.class, getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), + "subjectTypes must be of type List"); + Assert.notEmpty((List) getClaims().get(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED), + "subjectTypes cannot be empty"); + Assert.notNull(getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), + "idTokenSigningAlgorithms cannot be null"); + Assert.isInstanceOf(List.class, + getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), + "idTokenSigningAlgorithms must be of type List"); + Assert.notEmpty( + (List) getClaims().get(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED), + "idTokenSigningAlgorithms cannot be empty"); if (getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT) != null) { - validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT), "userInfoEndpoint must be a valid URL"); + validateURL(getClaims().get(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT), + "userInfoEndpoint must be a valid URL"); } if (getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT) != null) { - validateURL(getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT), "endSessionEndpoint must be a valid URL"); + validateURL(getClaims().get(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT), + "endSessionEndpoint must be a valid URL"); } } @@ -194,4 +205,5 @@ public final class OidcProviderConfiguration extends AbstractOAuth2Authorization } } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimAccessor.java index 4afb1aaa..906c9ae5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimAccessor.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimAccessor.java @@ -25,8 +25,8 @@ import org.springframework.security.oauth2.jwt.Jwt; import org.springframework.security.oauth2.server.authorization.OAuth2AuthorizationServerMetadataClaimAccessor; /** - * A {@link ClaimAccessor} for the "claims" that can be returned - * in the OpenID Provider Configuration Response. + * A {@link ClaimAccessor} for the "claims" that can be returned in the OpenID Provider + * Configuration Response. * * @author Daniel Garnier-Moiroux * @author Joe Grandja @@ -35,13 +35,14 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat * @see OAuth2AuthorizationServerMetadataClaimAccessor * @see OidcProviderMetadataClaimNames * @see OidcProviderConfiguration - * @see 3. OpenID Provider Metadata + * @see 3. OpenID + * Provider Metadata */ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationServerMetadataClaimAccessor { /** * Returns the Subject Identifier types supported {@code (subject_types_supported)}. - * * @return the Subject Identifier types supported */ default List getSubjectTypes() { @@ -49,18 +50,19 @@ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationSe } /** - * Returns the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token} - * to encode the claims in a {@link Jwt} {@code (id_token_signing_alg_values_supported)}. - * - * @return the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token} + * Returns the {@link JwsAlgorithm JWS} signing algorithms supported for the + * {@link OidcIdToken ID Token} to encode the claims in a {@link Jwt} + * {@code (id_token_signing_alg_values_supported)}. + * @return the {@link JwsAlgorithm JWS} signing algorithms supported for the + * {@link OidcIdToken ID Token} */ default List getIdTokenSigningAlgorithms() { return getClaimAsStringList(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED); } /** - * Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint {@code (userinfo_endpoint)}. - * + * Returns the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint + * {@code (userinfo_endpoint)}. * @return the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint * @since 0.2.2 */ @@ -69,8 +71,8 @@ public interface OidcProviderMetadataClaimAccessor extends OAuth2AuthorizationSe } /** - * Returns the {@code URL} of the OpenID Connect 1.0 End Session Endpoint {@code (end_session_endpoint)}. - * + * Returns the {@code URL} of the OpenID Connect 1.0 End Session Endpoint + * {@code (end_session_endpoint)}. * @return the {@code URL} of the OpenID Connect 1.0 End Session Endpoint * @since 1.1 */ diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java index 00e5d6ca..bd951fd5 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderMetadataClaimNames.java @@ -27,7 +27,9 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat * @author Joe Grandja * @since 0.1.0 * @see OAuth2AuthorizationServerMetadataClaimNames - * @see 3. OpenID Provider Metadata + * @see 3. OpenID + * Provider Metadata */ public final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationServerMetadataClaimNames { @@ -37,18 +39,21 @@ public final class OidcProviderMetadataClaimNames extends OAuth2AuthorizationSer public static final String SUBJECT_TYPES_SUPPORTED = "subject_types_supported"; /** - * {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS} signing algorithms supported for the {@link OidcIdToken ID Token} + * {@code id_token_signing_alg_values_supported} - the {@link JwsAlgorithm JWS} + * signing algorithms supported for the {@link OidcIdToken ID Token} */ public static final String ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED = "id_token_signing_alg_values_supported"; /** - * {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo Endpoint + * {@code userinfo_endpoint} - the {@code URL} of the OpenID Connect 1.0 UserInfo + * Endpoint * @since 0.2.2 */ public static final String USER_INFO_ENDPOINT = "userinfo_endpoint"; /** - * {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End Session Endpoint + * {@code end_session_endpoint} - the {@code URL} of the OpenID Connect 1.0 End + * Session Endpoint * @since 1.1 */ public static final String END_SESSION_ENDPOINT = "end_session_endpoint"; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java index cd8cac4d..b920684b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcAuthenticationProviderUtils.java @@ -32,8 +32,7 @@ final class OidcAuthenticationProviderUtils { private OidcAuthenticationProviderUtils() { } - static OAuth2Authorization invalidate( - OAuth2Authorization authorization, T token) { + static OAuth2Authorization invalidate(OAuth2Authorization authorization, T token) { // @formatter:off OAuth2Authorization.Builder authorizationBuilder = OAuth2Authorization.from(authorization) @@ -60,4 +59,5 @@ final class OidcAuthenticationProviderUtils { return authorizationBuilder.build(); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java index c790957b..1311b635 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProvider.java @@ -42,7 +42,8 @@ import org.springframework.util.Assert; import org.springframework.util.StringUtils; /** - * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client Configuration Endpoint. + * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client + * Configuration Endpoint. * * @author Ovidiu Popa * @author Joe Grandja @@ -53,18 +54,25 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationService * @see OidcClientRegistrationAuthenticationToken * @see OidcClientRegistrationAuthenticationProvider - * @see 4. Client Configuration Endpoint + * @see 4. + * Client Configuration Endpoint */ public final class OidcClientConfigurationAuthenticationProvider implements AuthenticationProvider { + static final String DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE = "client.read"; + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private Converter clientRegistrationConverter; /** - * Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OidcClientConfigurationAuthenticationProvider} using the + * provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service */ @@ -90,27 +98,29 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = - (OidcClientRegistrationAuthenticationToken) authentication; + OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication; if (!StringUtils.hasText(clientRegistrationAuthentication.getClientId())) { // This is not a Client Configuration Request. - // Return null to allow OidcClientRegistrationAuthenticationProvider to handle it. + // Return null to allow OidcClientRegistrationAuthenticationProvider to handle + // it. return null; } // Validate the "registration" access token AbstractOAuth2TokenAuthenticationToken accessTokenAuthentication = null; - if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) { - accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) clientRegistrationAuthentication.getPrincipal(); + if (AbstractOAuth2TokenAuthenticationToken.class + .isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) { + accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) clientRegistrationAuthentication + .getPrincipal(); } if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue(); - OAuth2Authorization authorization = this.authorizationService.findByToken( - accessTokenValue, OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue, + OAuth2TokenType.ACCESS_TOKEN); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } @@ -133,11 +143,12 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth return OidcClientRegistrationAuthenticationToken.class.isAssignableFrom(authentication); } - private OidcClientRegistrationAuthenticationToken findRegistration(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication, + private OidcClientRegistrationAuthenticationToken findRegistration( + OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication, OAuth2Authorization authorization) { - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId( - clientRegistrationAuthentication.getClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findByClientId(clientRegistrationAuthentication.getClientId()); if (registeredClient == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_CLIENT); } @@ -161,14 +172,16 @@ public final class OidcClientConfigurationAuthenticationProvider implements Auth } @SuppressWarnings("unchecked") - private static void checkScope(OAuth2Authorization.Token authorizedAccessToken, Set requiredScope) { + private static void checkScope(OAuth2Authorization.Token authorizedAccessToken, + Set requiredScope) { Collection authorizedScope = Collections.emptySet(); if (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) { authorizedScope = (Collection) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE); } if (!authorizedScope.containsAll(requiredScope)) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); - } else if (authorizedScope.size() != requiredScope.size()) { + } + else if (authorizedScope.size() != requiredScope.size()) { // Restrict the access token to only contain the required scope throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java index 92611f8f..c79290f8 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProvider.java @@ -64,7 +64,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client Registration Endpoint. + * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 Dynamic Client + * Registration Endpoint. * * @author Ovidiu Popa * @author Joe Grandja @@ -77,29 +78,41 @@ import org.springframework.util.StringUtils; * @see OidcClientRegistrationAuthenticationToken * @see OidcClientConfigurationAuthenticationProvider * @see PasswordEncoder - * @see 3. Client Registration Endpoint + * @see 3. + * Client Registration Endpoint */ public final class OidcClientRegistrationAuthenticationProvider implements AuthenticationProvider { + private static final String ERROR_URI = "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError"; + private static final String DEFAULT_CLIENT_REGISTRATION_AUTHORIZED_SCOPE = "client.create"; + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final OAuth2TokenGenerator tokenGenerator; + private Converter clientRegistrationConverter; + private Converter registeredClientConverter; + private PasswordEncoder passwordEncoder; /** - * Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OidcClientRegistrationAuthenticationProvider} using the + * provided parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service * @param tokenGenerator the token generator * @since 0.2.3 */ public OidcClientRegistrationAuthenticationProvider(RegisteredClientRepository registeredClientRepository, - OAuth2AuthorizationService authorizationService, OAuth2TokenGenerator tokenGenerator) { + OAuth2AuthorizationService authorizationService, + OAuth2TokenGenerator tokenGenerator) { Assert.notNull(registeredClientRepository, "registeredClientRepository cannot be null"); Assert.notNull(authorizationService, "authorizationService cannot be null"); Assert.notNull(tokenGenerator, "tokenGenerator cannot be null"); @@ -113,27 +126,29 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = - (OidcClientRegistrationAuthenticationToken) authentication; + OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication = (OidcClientRegistrationAuthenticationToken) authentication; if (clientRegistrationAuthentication.getClientRegistration() == null) { // This is not a Client Registration Request. - // Return null to allow OidcClientConfigurationAuthenticationProvider to handle it. + // Return null to allow OidcClientConfigurationAuthenticationProvider to + // handle it. return null; } // Validate the "initial" access token AbstractOAuth2TokenAuthenticationToken accessTokenAuthentication = null; - if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) { - accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) clientRegistrationAuthentication.getPrincipal(); + if (AbstractOAuth2TokenAuthenticationToken.class + .isAssignableFrom(clientRegistrationAuthentication.getPrincipal().getClass())) { + accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) clientRegistrationAuthentication + .getPrincipal(); } if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue(); - OAuth2Authorization authorization = this.authorizationService.findByToken( - accessTokenValue, OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue, + OAuth2TokenType.ACCESS_TOKEN); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } @@ -157,12 +172,14 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe } /** - * Sets the {@link Converter} used for converting an {@link OidcClientRegistration} to a {@link RegisteredClient}. - * - * @param registeredClientConverter the {@link Converter} used for converting an {@link OidcClientRegistration} to a {@link RegisteredClient} + * Sets the {@link Converter} used for converting an {@link OidcClientRegistration} to + * a {@link RegisteredClient}. + * @param registeredClientConverter the {@link Converter} used for converting an + * {@link OidcClientRegistration} to a {@link RegisteredClient} * @since 0.4.0 */ - public void setRegisteredClientConverter(Converter registeredClientConverter) { + public void setRegisteredClientConverter( + Converter registeredClientConverter) { Assert.notNull(registeredClientConverter, "registeredClientConverter cannot be null"); this.registeredClientConverter = registeredClientConverter; } @@ -179,9 +196,10 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe } /** - * Sets the {@link PasswordEncoder} used to encode the {@link RegisteredClient#getClientSecret() client secret}. - * If not set, the client secret will be encoded using {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}. - * + * Sets the {@link PasswordEncoder} used to encode the + * {@link RegisteredClient#getClientSecret() client secret}. If not set, the client + * secret will be encoded using + * {@link PasswordEncoderFactories#createDelegatingPasswordEncoder()}. * @param passwordEncoder the {@link PasswordEncoder} used to encode the client secret * @since 1.1.0 */ @@ -190,38 +208,47 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe this.passwordEncoder = passwordEncoder; } - private OidcClientRegistrationAuthenticationToken registerClient(OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication, + private OidcClientRegistrationAuthenticationToken registerClient( + OidcClientRegistrationAuthenticationToken clientRegistrationAuthentication, OAuth2Authorization authorization) { if (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getRedirectUris())) { - throwInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI, OidcClientMetadataClaimNames.REDIRECT_URIS); + throwInvalidClientRegistration(OAuth2ErrorCodes.INVALID_REDIRECT_URI, + OidcClientMetadataClaimNames.REDIRECT_URIS); } - if (!isValidRedirectUris(clientRegistrationAuthentication.getClientRegistration().getPostLogoutRedirectUris())) { - throwInvalidClientRegistration("invalid_client_metadata", OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); + if (!isValidRedirectUris( + clientRegistrationAuthentication.getClientRegistration().getPostLogoutRedirectUris())) { + throwInvalidClientRegistration("invalid_client_metadata", + OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); } if (!isValidTokenEndpointAuthenticationMethod(clientRegistrationAuthentication.getClientRegistration())) { - throwInvalidClientRegistration("invalid_client_metadata", OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD); + throwInvalidClientRegistration("invalid_client_metadata", + OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD); } if (this.logger.isTraceEnabled()) { this.logger.trace("Validated client registration request parameters"); } - RegisteredClient registeredClient = this.registeredClientConverter.convert(clientRegistrationAuthentication.getClientRegistration()); + RegisteredClient registeredClient = this.registeredClientConverter + .convert(clientRegistrationAuthentication.getClientRegistration()); if (StringUtils.hasText(registeredClient.getClientSecret())) { // Encode the client secret RegisteredClient updatedRegisteredClient = RegisteredClient.from(registeredClient) - .clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret())) - .build(); + .clientSecret(this.passwordEncoder.encode(registeredClient.getClientSecret())) + .build(); this.registeredClientRepository.save(updatedRegisteredClient); - if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(clientRegistrationAuthentication.getClientRegistration().getTokenEndpointAuthenticationMethod())) { + if (ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue() + .equals(clientRegistrationAuthentication.getClientRegistration() + .getTokenEndpointAuthenticationMethod())) { // gh-1344 Return the hashed client_secret registeredClient = updatedRegisteredClient; } - } else { + } + else { this.registeredClientRepository.save(registeredClient); } @@ -232,9 +259,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe OAuth2Authorization registeredClientAuthorization = registerAccessToken(registeredClient); // Invalidate the "initial" access token as it can only be used once - authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getAccessToken().getToken()); + authorization = OidcAuthenticationProviderUtils.invalidate(authorization, + authorization.getAccessToken().getToken()); if (authorization.getRefreshToken() != null) { - authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getRefreshToken().getToken()); + authorization = OidcAuthenticationProviderUtils.invalidate(authorization, + authorization.getRefreshToken().getToken()); } this.authorizationService.save(authorization); @@ -242,10 +271,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe this.logger.trace("Saved authorization with invalidated initial access token"); } - Map clientRegistrationClaims = this.clientRegistrationConverter.convert(registeredClient).getClaims(); + Map clientRegistrationClaims = this.clientRegistrationConverter.convert(registeredClient) + .getClaims(); OidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(clientRegistrationClaims) - .registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue()) - .build(); + .registrationAccessToken(registeredClientAuthorization.getAccessToken().getToken().getTokenValue()) + .build(); if (this.logger.isTraceEnabled()) { this.logger.trace("Authenticated client registration request"); @@ -257,10 +287,12 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe private OAuth2Authorization registerAccessToken(RegisteredClient registeredClient) { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, - registeredClient.getClientAuthenticationMethods().iterator().next(), registeredClient.getClientSecret()); + registeredClient.getClientAuthenticationMethods().iterator().next(), + registeredClient.getClientSecret()); Set authorizedScopes = new HashSet<>(); - authorizedScopes.add(OidcClientConfigurationAuthenticationProvider.DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE); + authorizedScopes + .add(OidcClientConfigurationAuthenticationProvider.DEFAULT_CLIENT_CONFIGURATION_AUTHORIZED_SCOPE); authorizedScopes = Collections.unmodifiableSet(authorizedScopes); // @formatter:off @@ -296,9 +328,11 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe .authorizedScopes(authorizedScopes); // @formatter:on if (registrationAccessToken instanceof ClaimAccessor) { - authorizationBuilder.token(accessToken, (metadata) -> - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, ((ClaimAccessor) registrationAccessToken).getClaims())); - } else { + authorizationBuilder.token(accessToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, + ((ClaimAccessor) registrationAccessToken).getClaims())); + } + else { authorizationBuilder.accessToken(accessToken); } @@ -314,14 +348,16 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe } @SuppressWarnings("unchecked") - private static void checkScope(OAuth2Authorization.Token authorizedAccessToken, Set requiredScope) { + private static void checkScope(OAuth2Authorization.Token authorizedAccessToken, + Set requiredScope) { Collection authorizedScope = Collections.emptySet(); if (authorizedAccessToken.getClaims().containsKey(OAuth2ParameterNames.SCOPE)) { authorizedScope = (Collection) authorizedAccessToken.getClaims().get(OAuth2ParameterNames.SCOPE); } if (!authorizedScope.containsAll(requiredScope)) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); - } else if (authorizedScope.size() != requiredScope.size()) { + } + else if (authorizedScope.size() != requiredScope.size()) { // Restrict the access token to only contain the required scope throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } @@ -338,7 +374,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe if (validRedirectUri.getFragment() != null) { return false; } - } catch (URISyntaxException ex) { + } + catch (URISyntaxException ex) { return false; } } @@ -350,8 +387,8 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe String authenticationMethod = clientRegistration.getTokenEndpointAuthenticationMethod(); String authenticationSigningAlgorithm = clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm(); - if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod) && - !ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(authenticationMethod)) { + if (!ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod) + && !ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue().equals(authenticationMethod)) { return !StringUtils.hasText(authenticationSigningAlgorithm); } @@ -360,21 +397,18 @@ public final class OidcClientRegistrationAuthenticationProvider implements Authe } if (ClientAuthenticationMethod.PRIVATE_KEY_JWT.getValue().equals(authenticationMethod)) { - return clientRegistration.getJwkSetUrl() != null && - (!StringUtils.hasText(authenticationSigningAlgorithm) || - SignatureAlgorithm.from(authenticationSigningAlgorithm) != null); - } else { + return clientRegistration.getJwkSetUrl() != null && (!StringUtils.hasText(authenticationSigningAlgorithm) + || SignatureAlgorithm.from(authenticationSigningAlgorithm) != null); + } + else { // client_secret_jwt - return !StringUtils.hasText(authenticationSigningAlgorithm) || - MacAlgorithm.from(authenticationSigningAlgorithm) != null; + return !StringUtils.hasText(authenticationSigningAlgorithm) + || MacAlgorithm.from(authenticationSigningAlgorithm) != null; } } private static void throwInvalidClientRegistration(String errorCode, String fieldName) { - OAuth2Error error = new OAuth2Error( - errorCode, - "Invalid Client Registration: " + fieldName, - ERROR_URI); + OAuth2Error error = new OAuth2Error(errorCode, "Invalid Client Registration: " + fieldName, ERROR_URI); throw new OAuth2AuthenticationException(error); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java index c14f9201..37cd52f8 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationToken.java @@ -25,7 +25,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client Registration (and Configuration) Endpoint. + * An {@link Authentication} implementation used for OpenID Connect 1.0 Dynamic Client + * Registration (and Configuration) Endpoint. * * @author Joe Grandja * @author Ovidiu Popa @@ -36,18 +37,23 @@ import org.springframework.util.Assert; * @see OidcClientConfigurationAuthenticationProvider */ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Authentication principal; + private final OidcClientRegistration clientRegistration; + private final String clientId; /** - * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided + * parameters. * @param principal the authenticated principal * @param clientRegistration the client registration */ - public OidcClientRegistrationAuthenticationToken(Authentication principal, OidcClientRegistration clientRegistration) { + public OidcClientRegistrationAuthenticationToken(Authentication principal, + OidcClientRegistration clientRegistration) { super(Collections.emptyList()); Assert.notNull(principal, "principal cannot be null"); Assert.notNull(clientRegistration, "clientRegistration cannot be null"); @@ -58,8 +64,8 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic } /** - * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OidcClientRegistrationAuthenticationToken} using the provided + * parameters. * @param principal the authenticated principal * @param clientId the client identifier * @since 0.2.1 @@ -86,7 +92,6 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic /** * Returns the client registration. - * * @return the client registration */ public OidcClientRegistration getClientRegistration() { @@ -95,7 +100,6 @@ public class OidcClientRegistrationAuthenticationToken extends AbstractAuthentic /** * Returns the client identifier. - * * @return the client identifier * @since 0.2.1 */ diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java index 8b59322a..fcbb591c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProvider.java @@ -47,29 +47,36 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 RP-Initiated Logout Endpoint. + * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 RP-Initiated + * Logout Endpoint. * * @author Joe Grandja * @since 1.1 * @see RegisteredClientRepository * @see OAuth2AuthorizationService * @see SessionRegistry - * @see 2. RP-Initiated Logout + * @see 2. + * RP-Initiated Logout */ public final class OidcLogoutAuthenticationProvider implements AuthenticationProvider { - private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = - new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private final Log logger = LogFactory.getLog(getClass()); + private final RegisteredClientRepository registeredClientRepository; + private final OAuth2AuthorizationService authorizationService; + private final SessionRegistry sessionRegistry; /** - * Constructs an {@code OidcLogoutAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OidcLogoutAuthenticationProvider} using the provided + * parameters. * @param registeredClientRepository the repository of registered clients * @param authorizationService the authorization service - * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect sessions + * @param sessionRegistry the {@link SessionRegistry} used to track OpenID Connect + * sessions */ public OidcLogoutAuthenticationProvider(RegisteredClientRepository registeredClientRepository, OAuth2AuthorizationService authorizationService, SessionRegistry sessionRegistry) { @@ -83,11 +90,10 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OidcLogoutAuthenticationToken oidcLogoutAuthentication = - (OidcLogoutAuthenticationToken) authentication; + OidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication; - OAuth2Authorization authorization = this.authorizationService.findByToken( - oidcLogoutAuthentication.getIdTokenHint(), ID_TOKEN_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService + .findByToken(oidcLogoutAuthentication.getIdTokenHint(), ID_TOKEN_TOKEN_TYPE); if (authorization == null) { throwError(OAuth2ErrorCodes.INVALID_TOKEN, "id_token_hint"); } @@ -97,13 +103,13 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro } OAuth2Authorization.Token authorizedIdToken = authorization.getToken(OidcIdToken.class); - if (authorizedIdToken.isInvalidated() || - authorizedIdToken.isBeforeUse()) { // Expired ID Token should be accepted + if (authorizedIdToken.isInvalidated() || authorizedIdToken.isBeforeUse()) { + // Expired ID Token should be accepted throwError(OAuth2ErrorCodes.INVALID_TOKEN, "id_token_hint"); } - RegisteredClient registeredClient = this.registeredClientRepository.findById( - authorization.getRegisteredClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findById(authorization.getRegisteredClientId()); if (this.logger.isTraceEnabled()) { this.logger.trace("Retrieved registered client"); @@ -113,16 +119,16 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro // Validate client identity List audClaim = idToken.getAudience(); - if (CollectionUtils.isEmpty(audClaim) || - !audClaim.contains(registeredClient.getClientId())) { + if (CollectionUtils.isEmpty(audClaim) || !audClaim.contains(registeredClient.getClientId())) { throwError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.AUD); } - if (StringUtils.hasText(oidcLogoutAuthentication.getClientId()) && - !oidcLogoutAuthentication.getClientId().equals(registeredClient.getClientId())) { + if (StringUtils.hasText(oidcLogoutAuthentication.getClientId()) + && !oidcLogoutAuthentication.getClientId().equals(registeredClient.getClientId())) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID); } - if (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri()) && - !registeredClient.getPostLogoutRedirectUris().contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) { + if (StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri()) + && !registeredClient.getPostLogoutRedirectUris() + .contains(oidcLogoutAuthentication.getPostLogoutRedirectUri())) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, "post_logout_redirect_uri"); } @@ -134,28 +140,28 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro if (oidcLogoutAuthentication.isPrincipalAuthenticated()) { Authentication currentUserPrincipal = (Authentication) oidcLogoutAuthentication.getPrincipal(); Authentication authorizedUserPrincipal = authorization.getAttribute(Principal.class.getName()); - if (!StringUtils.hasText(idToken.getSubject()) || - !currentUserPrincipal.getName().equals(authorizedUserPrincipal.getName())) { + if (!StringUtils.hasText(idToken.getSubject()) + || !currentUserPrincipal.getName().equals(authorizedUserPrincipal.getName())) { throwError(OAuth2ErrorCodes.INVALID_TOKEN, IdTokenClaimNames.SUB); } // Check for active session if (StringUtils.hasText(oidcLogoutAuthentication.getSessionId())) { - SessionInformation sessionInformation = findSessionInformation( - currentUserPrincipal, oidcLogoutAuthentication.getSessionId()); + SessionInformation sessionInformation = findSessionInformation(currentUserPrincipal, + oidcLogoutAuthentication.getSessionId()); if (sessionInformation != null) { String sessionIdHash; try { sessionIdHash = createHash(sessionInformation.getSessionId()); - } catch (NoSuchAlgorithmException ex) { + } + catch (NoSuchAlgorithmException ex) { OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.SERVER_ERROR, "Failed to compute hash for Session ID.", null); throw new OAuth2AuthenticationException(error); } String sidClaim = idToken.getClaim("sid"); - if (!StringUtils.hasText(sidClaim) || - !sidClaim.equals(sessionIdHash)) { + if (!StringUtils.hasText(sidClaim) || !sidClaim.equals(sessionIdHash)) { throwError(OAuth2ErrorCodes.INVALID_TOKEN, "sid"); } } @@ -191,9 +197,7 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro } private static void throwError(String errorCode, String parameterName) { - OAuth2Error error = new OAuth2Error( - errorCode, - "OpenID Connect 1.0 Logout Request Parameter: " + parameterName, + OAuth2Error error = new OAuth2Error(errorCode, "OpenID Connect 1.0 Logout Request Parameter: " + parameterName, "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling"); throw new OAuth2AuthenticationException(error); } @@ -203,4 +207,5 @@ public final class OidcLogoutAuthenticationProvider implements AuthenticationPro byte[] digest = md.digest(value.getBytes(StandardCharsets.US_ASCII)); return Base64.getUrlEncoder().withoutPadding().encodeToString(digest); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationToken.java index 1e086c1b..7134c79f 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationToken.java @@ -26,7 +26,8 @@ import org.springframework.security.oauth2.server.authorization.util.SpringAutho import org.springframework.util.Assert; /** - * An {@link Authentication} implementation used for OpenID Connect 1.0 RP-Initiated Logout Endpoint. + * An {@link Authentication} implementation used for OpenID Connect 1.0 RP-Initiated + * Logout Endpoint. * * @author Joe Grandja * @since 1.1 @@ -34,24 +35,35 @@ import org.springframework.util.Assert; * @see OidcLogoutAuthenticationProvider */ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final String idTokenHint; + private final OidcIdToken idToken; + private final Authentication principal; + private final String sessionId; + private final String clientId; + private final String postLogoutRedirectUri; + private final String state; /** * Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters. - * - * @param idTokenHint the ID Token previously issued by the Provider to the Client and used as a hint about the End-User's current authenticated session with the Client + * @param idTokenHint the ID Token previously issued by the Provider to the Client and + * used as a hint about the End-User's current authenticated session with the Client * @param principal the authenticated principal representing the End-User - * @param sessionId the End-User's current authenticated session identifier with the Provider + * @param sessionId the End-User's current authenticated session identifier with the + * Provider * @param clientId the client identifier the ID Token was issued to - * @param postLogoutRedirectUri the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed - * @param state the opaque value used by the Client to maintain state between the logout request and the callback to the {@code postLogoutRedirectUri} + * @param postLogoutRedirectUri the URI which the Client is requesting that the + * End-User's User Agent be redirected to after a logout has been performed + * @param state the opaque value used by the Client to maintain state between the + * logout request and the callback to the {@code postLogoutRedirectUri} */ public OidcLogoutAuthenticationToken(String idTokenHint, Authentication principal, @Nullable String sessionId, @Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) { @@ -70,13 +82,15 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { /** * Constructs an {@code OidcLogoutAuthenticationToken} using the provided parameters. - * * @param idToken the ID Token previously issued by the Provider to the Client * @param principal the authenticated principal representing the End-User - * @param sessionId the End-User's current authenticated session identifier with the Provider + * @param sessionId the End-User's current authenticated session identifier with the + * Provider * @param clientId the client identifier the ID Token was issued to - * @param postLogoutRedirectUri the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed - * @param state the opaque value used by the Client to maintain state between the logout request and the callback to the {@code postLogoutRedirectUri} + * @param postLogoutRedirectUri the URI which the Client is requesting that the + * End-User's User Agent be redirected to after a logout has been performed + * @param state the opaque value used by the Client to maintain state between the + * logout request and the callback to the {@code postLogoutRedirectUri} */ public OidcLogoutAuthenticationToken(OidcIdToken idToken, Authentication principal, @Nullable String sessionId, @Nullable String clientId, @Nullable String postLogoutRedirectUri, @Nullable String state) { @@ -95,7 +109,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { /** * Returns the authenticated principal representing the End-User. - * * @return the authenticated principal representing the End-User */ @Override @@ -104,13 +117,14 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { } /** - * Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise. - * - * @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false} otherwise + * Returns {@code true} if {@link #getPrincipal()} is authenticated, {@code false} + * otherwise. + * @return {@code true} if {@link #getPrincipal()} is authenticated, {@code false} + * otherwise */ public boolean isPrincipalAuthenticated() { - return !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass()) && - this.principal.isAuthenticated(); + return !AnonymousAuthenticationToken.class.isAssignableFrom(this.principal.getClass()) + && this.principal.isAuthenticated(); } @Override @@ -119,9 +133,8 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { } /** - * Returns the ID Token previously issued by the Provider to the Client and used as a hint - * about the End-User's current authenticated session with the Client. - * + * Returns the ID Token previously issued by the Provider to the Client and used as a + * hint about the End-User's current authenticated session with the Client. * @return the ID Token previously issued by the Provider to the Client */ public String getIdTokenHint() { @@ -130,7 +143,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { /** * Returns the ID Token previously issued by the Provider to the Client. - * * @return the ID Token previously issued by the Provider to the Client */ @Nullable @@ -140,7 +152,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { /** * Returns the End-User's current authenticated session identifier with the Provider. - * * @return the End-User's current authenticated session identifier with the Provider */ @Nullable @@ -150,7 +161,6 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { /** * Returns the client identifier the ID Token was issued to. - * * @return the client identifier */ @Nullable @@ -159,9 +169,10 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { } /** - * Returns the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed. - * - * @return the URI which the Client is requesting that the End-User's User Agent be redirected to after a logout has been performed + * Returns the URI which the Client is requesting that the End-User's User Agent be + * redirected to after a logout has been performed. + * @return the URI which the Client is requesting that the End-User's User Agent be + * redirected to after a logout has been performed */ @Nullable public String getPostLogoutRedirectUri() { @@ -169,9 +180,10 @@ public class OidcLogoutAuthenticationToken extends AbstractAuthenticationToken { } /** - * Returns the opaque value used by the Client to maintain state between the logout request and the callback to the {@link #getPostLogoutRedirectUri()}. - * - * @return the opaque value used by the Client to maintain state between the logout request and the callback to the {@link #getPostLogoutRedirectUri()} + * Returns the opaque value used by the Client to maintain state between the logout + * request and the callback to the {@link #getPostLogoutRedirectUri()}. + * @return the opaque value used by the Client to maintain state between the logout + * request and the callback to the {@link #getPostLogoutRedirectUri()} */ @Nullable public String getState() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java index 28cd32a6..054bc18d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationContext.java @@ -28,8 +28,9 @@ import org.springframework.security.oauth2.server.authorization.authentication.O import org.springframework.util.Assert; /** - * An {@link OAuth2AuthenticationContext} that holds an {@link OidcUserInfoAuthenticationToken} and additional information - * and is used when mapping claims to an instance of {@link OidcUserInfo}. + * An {@link OAuth2AuthenticationContext} that holds an + * {@link OidcUserInfoAuthenticationToken} and additional information and is used when + * mapping claims to an instance of {@link OidcUserInfo}. * * @author Joe Grandja * @since 0.2.1 @@ -38,6 +39,7 @@ import org.springframework.util.Assert; * @see OidcUserInfoAuthenticationProvider#setUserInfoMapper(Function) */ public final class OidcUserInfoAuthenticationContext implements OAuth2AuthenticationContext { + private final Map context; private OidcUserInfoAuthenticationContext(Map context) { @@ -59,7 +61,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica /** * Returns the {@link OAuth2AccessToken OAuth 2.0 Access Token}. - * * @return the {@link OAuth2AccessToken} */ public OAuth2AccessToken getAccessToken() { @@ -68,7 +69,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica /** * Returns the {@link OAuth2Authorization authorization}. - * * @return the {@link OAuth2Authorization} */ public OAuth2Authorization getAuthorization() { @@ -76,8 +76,8 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica } /** - * Constructs a new {@link Builder} with the provided {@link OidcUserInfoAuthenticationToken}. - * + * Constructs a new {@link Builder} with the provided + * {@link OidcUserInfoAuthenticationToken}. * @param authentication the {@link OidcUserInfoAuthenticationToken} * @return the {@link Builder} */ @@ -96,7 +96,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica /** * Sets the {@link OAuth2AccessToken OAuth 2.0 Access Token}. - * * @param accessToken the {@link OAuth2AccessToken} * @return the {@link Builder} for further configuration */ @@ -106,7 +105,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica /** * Sets the {@link OAuth2Authorization authorization}. - * * @param authorization the {@link OAuth2Authorization} * @return the {@link Builder} for further configuration */ @@ -116,7 +114,6 @@ public final class OidcUserInfoAuthenticationContext implements OAuth2Authentica /** * Builds a new {@link OidcUserInfoAuthenticationContext}. - * * @return the {@link OidcUserInfoAuthenticationContext} */ public OidcUserInfoAuthenticationContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java index 1e64ec40..19e39087 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProvider.java @@ -43,21 +43,26 @@ import org.springframework.security.oauth2.server.resource.authentication.Abstra import org.springframework.util.Assert; /** - * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 UserInfo Endpoint. + * An {@link AuthenticationProvider} implementation for OpenID Connect 1.0 UserInfo + * Endpoint. * * @author Steve Riesenberg * @since 0.2.1 * @see OAuth2AuthorizationService - * @see 5.3. UserInfo Endpoint + * @see 5.3. + * UserInfo Endpoint */ public final class OidcUserInfoAuthenticationProvider implements AuthenticationProvider { + private final Log logger = LogFactory.getLog(getClass()); + private final OAuth2AuthorizationService authorizationService; + private Function userInfoMapper = new DefaultOidcUserInfoMapper(); /** - * Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided parameters. - * + * Constructs an {@code OidcUserInfoAuthenticationProvider} using the provided + * parameters. * @param authorizationService the authorization service */ public OidcUserInfoAuthenticationProvider(OAuth2AuthorizationService authorizationService) { @@ -67,12 +72,13 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { - OidcUserInfoAuthenticationToken userInfoAuthentication = - (OidcUserInfoAuthenticationToken) authentication; + OidcUserInfoAuthenticationToken userInfoAuthentication = (OidcUserInfoAuthenticationToken) authentication; AbstractOAuth2TokenAuthenticationToken accessTokenAuthentication = null; - if (AbstractOAuth2TokenAuthenticationToken.class.isAssignableFrom(userInfoAuthentication.getPrincipal().getClass())) { - accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) userInfoAuthentication.getPrincipal(); + if (AbstractOAuth2TokenAuthenticationToken.class + .isAssignableFrom(userInfoAuthentication.getPrincipal().getClass())) { + accessTokenAuthentication = (AbstractOAuth2TokenAuthenticationToken) userInfoAuthentication + .getPrincipal(); } if (accessTokenAuthentication == null || !accessTokenAuthentication.isAuthenticated()) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); @@ -80,8 +86,8 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP String accessTokenValue = accessTokenAuthentication.getToken().getTokenValue(); - OAuth2Authorization authorization = this.authorizationService.findByToken( - accessTokenValue, OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization authorization = this.authorizationService.findByToken(accessTokenValue, + OAuth2TokenType.ACCESS_TOKEN); if (authorization == null) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); } @@ -108,11 +114,11 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP this.logger.trace("Validated user info request"); } - OidcUserInfoAuthenticationContext authenticationContext = - OidcUserInfoAuthenticationContext.with(userInfoAuthentication) - .accessToken(authorizedAccessToken.getToken()) - .authorization(authorization) - .build(); + OidcUserInfoAuthenticationContext authenticationContext = OidcUserInfoAuthenticationContext + .with(userInfoAuthentication) + .accessToken(authorizedAccessToken.getToken()) + .authorization(authorization) + .build(); OidcUserInfo userInfo = this.userInfoMapper.apply(authenticationContext); if (this.logger.isTraceEnabled()) { @@ -128,27 +134,33 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP } /** - * Sets the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} - * to an instance of {@link OidcUserInfo} for the UserInfo response. + * Sets the {@link Function} used to extract claims from + * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} + * for the UserInfo response. * *

- * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the {@link OidcUserInfoAuthenticationToken}, - * as well as, the following context attributes: + * The {@link OidcUserInfoAuthenticationContext} gives the mapper access to the + * {@link OidcUserInfoAuthenticationToken}, as well as, the following context + * attributes: *

    - *
  • {@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the bearer token used to make the request.
  • - *
  • {@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the {@link OidcIdToken} and - * {@link OAuth2AccessToken} associated with the bearer token used to make the request.
  • + *
  • {@link OidcUserInfoAuthenticationContext#getAccessToken()} containing the + * bearer token used to make the request.
  • + *
  • {@link OidcUserInfoAuthenticationContext#getAuthorization()} containing the + * {@link OidcIdToken} and {@link OAuth2AccessToken} associated with the bearer token + * used to make the request.
  • *
- * - * @param userInfoMapper the {@link Function} used to extract claims from {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} + * @param userInfoMapper the {@link Function} used to extract claims from + * {@link OidcUserInfoAuthenticationContext} to an instance of {@link OidcUserInfo} */ public void setUserInfoMapper(Function userInfoMapper) { Assert.notNull(userInfoMapper, "userInfoMapper cannot be null"); this.userInfoMapper = userInfoMapper; } - private static final class DefaultOidcUserInfoMapper implements Function { + private static final class DefaultOidcUserInfoMapper + implements Function { + // @formatter:off private static final List EMAIL_CLAIMS = Arrays.asList( StandardClaimNames.EMAIL, StandardClaimNames.EMAIL_VERIFIED @@ -173,6 +185,7 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP StandardClaimNames.LOCALE, StandardClaimNames.UPDATED_AT ); + // @formatter:on @Override public OidcUserInfo apply(OidcUserInfoAuthenticationContext authenticationContext) { @@ -185,7 +198,8 @@ public final class OidcUserInfoAuthenticationProvider implements AuthenticationP return new OidcUserInfo(scopeRequestedClaims); } - private static Map getClaimsRequestedByScope(Map claims, Set requestedScopes) { + private static Map getClaimsRequestedByScope(Map claims, + Set requestedScopes) { Set scopeRequestedClaimNames = new HashSet<>(32); scopeRequestedClaimNames.add(StandardClaimNames.SUB); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationToken.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationToken.java index debc6bcb..c1306a4d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationToken.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationToken.java @@ -33,13 +33,16 @@ import org.springframework.util.Assert; * @see OidcUserInfoAuthenticationProvider */ public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Authentication principal; + private final OidcUserInfo userInfo; /** - * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided + * parameters. * @param principal the principal */ public OidcUserInfoAuthenticationToken(Authentication principal) { @@ -51,8 +54,8 @@ public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken } /** - * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided parameters. - * + * Constructs an {@code OidcUserInfoAuthenticationToken} using the provided + * parameters. * @param principal the authenticated principal * @param userInfo the UserInfo claims */ @@ -77,7 +80,6 @@ public class OidcUserInfoAuthenticationToken extends AbstractAuthenticationToken /** * Returns the UserInfo claims. - * * @return the UserInfo claims */ public OidcUserInfo getUserInfo() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverter.java index 3442c2ae..234db461 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverter.java @@ -45,7 +45,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * A {@link HttpMessageConverter} for an {@link OidcClientRegistration OpenID Client Registration Request and Response}. + * A {@link HttpMessageConverter} for an {@link OidcClientRegistration OpenID Client + * Registration Request and Response}. * * @author Ovidiu Popa * @author Joe Grandja @@ -58,9 +59,11 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { }; - private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); + private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters + .getJsonMessageConverter(); private Converter, OidcClientRegistration> clientRegistrationConverter = new MapOidcClientRegistrationConverter(); + private Converter> clientRegistrationParametersConverter = new OidcClientRegistrationMapConverter(); public OidcClientRegistrationHttpMessageConverter() { @@ -74,13 +77,14 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess @Override @SuppressWarnings("unchecked") - protected OidcClientRegistration readInternal(Class clazz, HttpInputMessage inputMessage) - throws HttpMessageNotReadableException { + protected OidcClientRegistration readInternal(Class clazz, + HttpInputMessage inputMessage) throws HttpMessageNotReadableException { try { Map clientRegistrationParameters = (Map) this.jsonMessageConverter - .read(STRING_OBJECT_MAP.getType(), null, inputMessage); + .read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.clientRegistrationConverter.convert(clientRegistrationParameters); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotReadableException( "An error occurred reading the OpenID Client Registration: " + ex.getMessage(), ex, inputMessage); } @@ -91,19 +95,21 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess throws HttpMessageNotWritableException { try { Map clientRegistrationParameters = this.clientRegistrationParametersConverter - .convert(clientRegistration); + .convert(clientRegistration); this.jsonMessageConverter.write(clientRegistrationParameters, STRING_OBJECT_MAP.getType(), MediaType.APPLICATION_JSON, outputMessage); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the OpenID Client Registration: " + ex.getMessage(), ex); } } /** - * Sets the {@link Converter} used for converting the OpenID Client Registration parameters to an {@link OidcClientRegistration}. - * - * @param clientRegistrationConverter the {@link Converter} used for converting to an {@link OidcClientRegistration} + * Sets the {@link Converter} used for converting the OpenID Client Registration + * parameters to an {@link OidcClientRegistration}. + * @param clientRegistrationConverter the {@link Converter} used for converting to an + * {@link OidcClientRegistration} */ public final void setClientRegistrationConverter( Converter, OidcClientRegistration> clientRegistrationConverter) { @@ -114,9 +120,9 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess /** * Sets the {@link Converter} used for converting the {@link OidcClientRegistration} * to a {@code Map} representation of the OpenID Client Registration parameters. - * - * @param clientRegistrationParametersConverter the {@link Converter} used for converting to a - * {@code Map} representation of the OpenID Client Registration parameters + * @param clientRegistrationParametersConverter the {@link Converter} used for + * converting to a {@code Map} representation of the OpenID Client Registration + * parameters */ public final void setClientRegistrationParametersConverter( Converter> clientRegistrationParametersConverter) { @@ -127,12 +133,19 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess private static final class MapOidcClientRegistrationConverter implements Converter, OidcClientRegistration> { - private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); + private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService + .getSharedInstance(); + private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class); + private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class); + private static final Converter INSTANT_CONVERTER = getConverter(INSTANT_TYPE_DESCRIPTOR); + private final ClaimTypeConverter claimTypeConverter; private MapOidcClientRegistrationConverter() { @@ -145,7 +158,8 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess claimConverters.put(OidcClientMetadataClaimNames.CLIENT_ID, stringConverter); claimConverters.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, INSTANT_CONVERTER); claimConverters.put(OidcClientMetadataClaimNames.CLIENT_SECRET, stringConverter); - claimConverters.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, MapOidcClientRegistrationConverter::convertClientSecretExpiresAt); + claimConverters.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, + MapOidcClientRegistrationConverter::convertClientSecretExpiresAt); claimConverters.put(OidcClientMetadataClaimNames.CLIENT_NAME, stringConverter); claimConverters.put(OidcClientMetadataClaimNames.REDIRECT_URIS, collectionStringConverter); claimConverters.put(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, collectionStringConverter); @@ -187,6 +201,7 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess } return Arrays.asList(StringUtils.delimitedListToStringArray(scope.toString(), " ")); } + } private static final class OidcClientRegistrationMapConverter @@ -196,7 +211,8 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess public Map convert(OidcClientRegistration source) { Map responseClaims = new LinkedHashMap<>(source.getClaims()); if (source.getClientIdIssuedAt() != null) { - responseClaims.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, source.getClientIdIssuedAt().getEpochSecond()); + responseClaims.put(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, + source.getClientIdIssuedAt().getEpochSecond()); } if (source.getClientSecret() != null) { long clientSecretExpiresAt = 0; @@ -206,10 +222,12 @@ public class OidcClientRegistrationHttpMessageConverter extends AbstractHttpMess responseClaims.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt); } if (!CollectionUtils.isEmpty(source.getScopes())) { - responseClaims.put(OidcClientMetadataClaimNames.SCOPE, StringUtils.collectionToDelimitedString(source.getScopes(), " ")); + responseClaims.put(OidcClientMetadataClaimNames.SCOPE, + StringUtils.collectionToDelimitedString(source.getScopes(), " ")); } return responseClaims; } + } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java index 499ceb00..37744fee 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverter.java @@ -38,7 +38,8 @@ import org.springframework.security.oauth2.server.authorization.oidc.OidcProvide import org.springframework.util.Assert; /** - * A {@link HttpMessageConverter} for an {@link OidcProviderConfiguration OpenID Provider Configuration Response}. + * A {@link HttpMessageConverter} for an {@link OidcProviderConfiguration OpenID Provider + * Configuration Response}. * * @author Daniel Garnier-Moiroux * @since 0.1.0 @@ -48,12 +49,14 @@ import org.springframework.util.Assert; public class OidcProviderConfigurationHttpMessageConverter extends AbstractHttpMessageConverter { - private static final ParameterizedTypeReference> STRING_OBJECT_MAP = - new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { + }; - private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters.getJsonMessageConverter(); + private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters + .getJsonMessageConverter(); private Converter, OidcProviderConfiguration> providerConfigurationConverter = new OidcProviderConfigurationConverter(); + private Converter> providerConfigurationParametersConverter = OidcProviderConfiguration::getClaims; public OidcProviderConfigurationHttpMessageConverter() { @@ -67,15 +70,17 @@ public class OidcProviderConfigurationHttpMessageConverter @Override @SuppressWarnings("unchecked") - protected OidcProviderConfiguration readInternal(Class clazz, HttpInputMessage inputMessage) - throws HttpMessageNotReadableException { + 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) { + } + catch (Exception ex) { throw new HttpMessageNotReadableException( - "An error occurred reading the OpenID Provider Configuration: " + ex.getMessage(), ex, inputMessage); + "An error occurred reading the OpenID Provider Configuration: " + ex.getMessage(), ex, + inputMessage); } } @@ -83,50 +88,55 @@ public class OidcProviderConfigurationHttpMessageConverter protected void writeInternal(OidcProviderConfiguration providerConfiguration, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException { try { - Map providerConfigurationResponseParameters = - this.providerConfigurationParametersConverter.convert(providerConfiguration); - this.jsonMessageConverter.write( - providerConfigurationResponseParameters, - STRING_OBJECT_MAP.getType(), - MediaType.APPLICATION_JSON, - outputMessage - ); - } catch (Exception ex) { + Map providerConfigurationResponseParameters = this.providerConfigurationParametersConverter + .convert(providerConfiguration); + this.jsonMessageConverter.write(providerConfigurationResponseParameters, STRING_OBJECT_MAP.getType(), + MediaType.APPLICATION_JSON, outputMessage); + } + catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the OpenID Provider Configuration: " + ex.getMessage(), ex); } } /** - * 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} + * 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) { + 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. - * - * @param providerConfigurationParametersConverter the {@link Converter} used for converting to a - * {@code Map} representation of the OpenID Provider Configuration + * Sets the {@link Converter} used for converting the + * {@link OidcProviderConfiguration} to a {@code Map} representation of the OpenID + * Provider Configuration. + * @param providerConfigurationParametersConverter the {@link Converter} used for + * converting to a {@code Map} representation of the OpenID Provider Configuration */ public final void setProviderConfigurationParametersConverter( Converter> providerConfigurationParametersConverter) { - Assert.notNull(providerConfigurationParametersConverter, "providerConfigurationParametersConverter cannot be null"); + Assert.notNull(providerConfigurationParametersConverter, + "providerConfigurationParametersConverter cannot be null"); this.providerConfigurationParametersConverter = providerConfigurationParametersConverter; } - private static final class OidcProviderConfigurationConverter implements Converter, OidcProviderConfiguration> { - private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); + 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); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor URL_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(URL.class); + private final ClaimTypeConverter claimTypeConverter; private OidcProviderConfigurationConverter() { @@ -138,13 +148,15 @@ public class OidcProviderConfigurationHttpMessageConverter 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.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + collectionStringConverter); claimConverters.put(OidcProviderMetadataClaimNames.JWKS_URI, urlConverter); claimConverters.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, 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.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, collectionStringConverter); + claimConverters.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, + collectionStringConverter); claimConverters.put(OidcProviderMetadataClaimNames.SCOPES_SUPPORTED, collectionStringConverter); this.claimTypeConverter = new ClaimTypeConverter(claimConverters); } @@ -158,5 +170,7 @@ public class OidcProviderConfigurationHttpMessageConverter private static Converter getConverter(TypeDescriptor targetDescriptor) { return (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor); } + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverter.java index 479ee1ff..df967aca 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverter.java @@ -37,7 +37,8 @@ import org.springframework.security.oauth2.core.oidc.StandardClaimNames; import org.springframework.util.Assert; /** - * A {@link HttpMessageConverter} for an {@link OidcUserInfo OpenID Connect UserInfo Response}. + * A {@link HttpMessageConverter} for an {@link OidcUserInfo OpenID Connect UserInfo + * Response}. * * @author Ido Salomon * @author Steve Riesenberg @@ -47,13 +48,14 @@ import org.springframework.util.Assert; */ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConverter { - private static final ParameterizedTypeReference> STRING_OBJECT_MAP = - new ParameterizedTypeReference>() {}; + private static final ParameterizedTypeReference> STRING_OBJECT_MAP = new ParameterizedTypeReference>() { + }; - private final GenericHttpMessageConverter jsonMessageConverter = - HttpMessageConverters.getJsonMessageConverter(); + private final GenericHttpMessageConverter jsonMessageConverter = HttpMessageConverters + .getJsonMessageConverter(); private Converter, OidcUserInfo> userInfoConverter = new MapOidcUserInfoConverter(); + private Converter> userInfoParametersConverter = OidcUserInfo::getClaims; public OidcUserInfoHttpMessageConverter() { @@ -70,10 +72,11 @@ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConvert protected OidcUserInfo readInternal(Class clazz, HttpInputMessage inputMessage) throws HttpMessageNotReadableException { try { - Map userInfoParameters = - (Map) this.jsonMessageConverter.read(STRING_OBJECT_MAP.getType(), null, inputMessage); + Map userInfoParameters = (Map) this.jsonMessageConverter + .read(STRING_OBJECT_MAP.getType(), null, inputMessage); return this.userInfoConverter.convert(userInfoParameters); - } catch (Exception ex) { + } + catch (Exception ex) { throw new HttpMessageNotReadableException( "An error occurred reading the UserInfo response: " + ex.getMessage(), ex, inputMessage); } @@ -83,25 +86,21 @@ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConvert protected void writeInternal(OidcUserInfo oidcUserInfo, HttpOutputMessage outputMessage) throws HttpMessageNotWritableException { try { - Map userInfoResponseParameters = - this.userInfoParametersConverter.convert(oidcUserInfo); - this.jsonMessageConverter.write( - userInfoResponseParameters, - STRING_OBJECT_MAP.getType(), - MediaType.APPLICATION_JSON, - outputMessage - ); - } catch (Exception ex) { + Map userInfoResponseParameters = this.userInfoParametersConverter.convert(oidcUserInfo); + this.jsonMessageConverter.write(userInfoResponseParameters, STRING_OBJECT_MAP.getType(), + MediaType.APPLICATION_JSON, outputMessage); + } + catch (Exception ex) { throw new HttpMessageNotWritableException( "An error occurred writing the UserInfo response: " + ex.getMessage(), ex); } } /** - * Sets the {@link Converter} used for converting the UserInfo parameters - * to an {@link OidcUserInfo}. - * - * @param userInfoConverter the {@link Converter} used for converting to an {@link OidcUserInfo} + * Sets the {@link Converter} used for converting the UserInfo parameters to an + * {@link OidcUserInfo}. + * @param userInfoConverter the {@link Converter} used for converting to an + * {@link OidcUserInfo} */ public final void setUserInfoConverter(Converter, OidcUserInfo> userInfoConverter) { Assert.notNull(userInfoConverter, "userInfoConverter cannot be null"); @@ -111,7 +110,6 @@ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConvert /** * Sets the {@link Converter} used for converting the {@link OidcUserInfo} to a * {@code Map} representation of the UserInfo. - * * @param userInfoParametersConverter the {@link Converter} used for converting to a * {@code Map} representation of the UserInfo */ @@ -122,12 +120,21 @@ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConvert } private static final class MapOidcUserInfoConverter implements Converter, OidcUserInfo> { - private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService.getSharedInstance(); + + private static final ClaimConversionService CLAIM_CONVERSION_SERVICE = ClaimConversionService + .getSharedInstance(); + private static final TypeDescriptor OBJECT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Object.class); + private static final TypeDescriptor BOOLEAN_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Boolean.class); + private static final TypeDescriptor STRING_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(String.class); + private static final TypeDescriptor INSTANT_TYPE_DESCRIPTOR = TypeDescriptor.valueOf(Instant.class); - private static final TypeDescriptor STRING_OBJECT_MAP_DESCRIPTOR = TypeDescriptor.map(Map.class, STRING_TYPE_DESCRIPTOR, OBJECT_TYPE_DESCRIPTOR); + + private static final TypeDescriptor STRING_OBJECT_MAP_DESCRIPTOR = TypeDescriptor.map(Map.class, + STRING_TYPE_DESCRIPTOR, OBJECT_TYPE_DESCRIPTOR); + private final ClaimTypeConverter claimTypeConverter; private MapOidcUserInfoConverter() { @@ -170,5 +177,7 @@ public class OidcUserInfoHttpMessageConverter extends AbstractHttpMessageConvert private static Converter getConverter(TypeDescriptor targetDescriptor) { return (source) -> CLAIM_CONVERSION_SERVICE.convert(source, OBJECT_TYPE_DESCRIPTOR, targetDescriptor); } + } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java index 5de3352e..3fd7bc9d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilter.java @@ -54,7 +54,8 @@ import org.springframework.util.StringUtils; import org.springframework.web.filter.OncePerRequestFilter; /** - * A {@code Filter} that processes OpenID Connect 1.0 Dynamic Client Registration (and Client Read) Requests. + * A {@code Filter} that processes OpenID Connect 1.0 Dynamic Client Registration (and + * Client Read) Requests. * * @author Ovidiu Popa * @author Joe Grandja @@ -64,28 +65,37 @@ import org.springframework.web.filter.OncePerRequestFilter; * @see OidcClientRegistrationAuthenticationConverter * @see OidcClientRegistrationAuthenticationProvider * @see OidcClientConfigurationAuthenticationProvider - * @see 3. Client Registration Endpoint - * @see 4. Client Configuration Endpoint + * @see 3. + * Client Registration Endpoint + * @see 4. + * Client Configuration Endpoint */ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for OpenID Client Registration requests. */ private static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = "/connect/register"; private final AuthenticationManager authenticationManager; + private final RequestMatcher clientRegistrationEndpointMatcher; - private final HttpMessageConverter clientRegistrationHttpMessageConverter = - new OidcClientRegistrationHttpMessageConverter(); - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + private AuthenticationConverter authenticationConverter = new OidcClientRegistrationAuthenticationConverter(); + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendClientRegistrationResponse; + private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; /** - * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OidcClientRegistrationEndpointFilter(AuthenticationManager authenticationManager) { @@ -93,10 +103,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } /** - * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OidcClientRegistrationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager - * @param clientRegistrationEndpointUri the endpoint {@code URI} for OpenID Client Registration requests + * @param clientRegistrationEndpointUri the endpoint {@code URI} for OpenID Client + * Registration requests */ public OidcClientRegistrationEndpointFilter(AuthenticationManager authenticationManager, String clientRegistrationEndpointUri) { @@ -104,14 +115,13 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi Assert.hasText(clientRegistrationEndpointUri, "clientRegistrationEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; this.clientRegistrationEndpointMatcher = new OrRequestMatcher( - new AntPathRequestMatcher( - clientRegistrationEndpointUri, HttpMethod.POST.name()), + new AntPathRequestMatcher(clientRegistrationEndpointUri, HttpMethod.POST.name()), createClientConfigurationMatcher(clientRegistrationEndpointUri)); } private static RequestMatcher createClientConfigurationMatcher(String clientRegistrationEndpointUri) { - RequestMatcher clientConfigurationGetMatcher = new AntPathRequestMatcher( - clientRegistrationEndpointUri, HttpMethod.GET.name()); + RequestMatcher clientConfigurationGetMatcher = new AntPathRequestMatcher(clientRegistrationEndpointUri, + HttpMethod.GET.name()); RequestMatcher clientIdMatcher = request -> { String clientId = request.getParameter(OAuth2ParameterNames.CLIENT_ID); @@ -133,18 +143,20 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi try { Authentication clientRegistrationAuthentication = this.authenticationConverter.convert(request); - Authentication clientRegistrationAuthenticationResult = - this.authenticationManager.authenticate(clientRegistrationAuthentication); + Authentication clientRegistrationAuthenticationResult = this.authenticationManager + .authenticate(clientRegistrationAuthentication); - this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, clientRegistrationAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, + clientRegistrationAuthenticationResult); + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Client registration request failed: %s", ex.getError()), ex); } this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex); - } catch (Exception ex) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_REQUEST, + } + catch (Exception ex) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "OpenID Connect 1.0 Client Registration Error: " + ex.getMessage(), "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError"); if (this.logger.isTraceEnabled()) { @@ -152,16 +164,19 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } this.authenticationFailureHandler.onAuthenticationFailure(request, response, new OAuth2AuthenticationException(error)); - } finally { + } + finally { SecurityContextHolder.clearContext(); } } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} - * to an instance of {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter an {@link AuthenticationConverter} used when attempting to extract a Client Registration Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract a Client + * Registration Request from {@link HttpServletRequest} to an instance of + * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the + * request. + * @param authenticationConverter an {@link AuthenticationConverter} used when + * attempting to extract a Client Registration Request from {@link HttpServletRequest} * @since 0.4.0 */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { @@ -170,10 +185,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} - * and returning the {@link OidcClientRegistration Client Registration Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcClientRegistrationAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcClientRegistrationAuthenticationToken} and returning the + * {@link OidcClientRegistration Client Registration Response}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OidcClientRegistrationAuthenticationToken} * @see 0.4.0 */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { @@ -182,10 +198,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} * @since 0.4.0 */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { @@ -196,11 +213,12 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi private void sendClientRegistrationResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { OidcClientRegistration clientRegistration = ((OidcClientRegistrationAuthenticationToken) authentication) - .getClientRegistration(); + .getClientRegistration(); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); if (HttpMethod.POST.name().equals(request.getMethod())) { httpResponse.setStatusCode(HttpStatus.CREATED); - } else { + } + else { httpResponse.setStatusCode(HttpStatus.OK); } this.clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpResponse); @@ -212,9 +230,11 @@ public final class OidcClientRegistrationEndpointFilter extends OncePerRequestFi HttpStatus httpStatus = HttpStatus.BAD_REQUEST; if (OAuth2ErrorCodes.INVALID_TOKEN.equals(error.getErrorCode())) { httpStatus = HttpStatus.UNAUTHORIZED; - } else if (OAuth2ErrorCodes.INSUFFICIENT_SCOPE.equals(error.getErrorCode())) { + } + else if (OAuth2ErrorCodes.INSUFFICIENT_SCOPE.equals(error.getErrorCode())) { httpStatus = HttpStatus.FORBIDDEN; - } else if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) { + } + else if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) { httpStatus = HttpStatus.UNAUTHORIZED; } ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilter.java index b2d102eb..aa3b9dd3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilter.java @@ -61,27 +61,35 @@ import org.springframework.web.util.UriUtils; * @since 1.1 * @see OidcLogoutAuthenticationConverter * @see OidcLogoutAuthenticationProvider - * @see 2. RP-Initiated Logout + * @see 2. + * RP-Initiated Logout */ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { /** - * The default endpoint {@code URI} for OpenID Connect 1.0 RP-Initiated Logout Requests. + * The default endpoint {@code URI} for OpenID Connect 1.0 RP-Initiated Logout + * Requests. */ private static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = "/connect/logout"; private final AuthenticationManager authenticationManager; + private final RequestMatcher logoutEndpointMatcher; + private final LogoutHandler logoutHandler; + private final LogoutSuccessHandler logoutSuccessHandler; + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::performLogout; + private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; /** * Constructs an {@code OidcLogoutEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager */ public OidcLogoutEndpointFilter(AuthenticationManager authenticationManager) { @@ -90,12 +98,11 @@ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { /** * Constructs an {@code OidcLogoutEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager - * @param logoutEndpointUri the endpoint {@code URI} for OpenID Connect 1.0 RP-Initiated Logout Requests + * @param logoutEndpointUri the endpoint {@code URI} for OpenID Connect 1.0 + * RP-Initiated Logout Requests */ - public OidcLogoutEndpointFilter(AuthenticationManager authenticationManager, - String logoutEndpointUri) { + public OidcLogoutEndpointFilter(AuthenticationManager authenticationManager, String logoutEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(logoutEndpointUri, "logoutEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; @@ -121,18 +128,20 @@ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { try { Authentication oidcLogoutAuthentication = this.authenticationConverter.convert(request); - Authentication oidcLogoutAuthenticationResult = - this.authenticationManager.authenticate(oidcLogoutAuthentication); + Authentication oidcLogoutAuthenticationResult = this.authenticationManager + .authenticate(oidcLogoutAuthentication); - this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, oidcLogoutAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, + oidcLogoutAuthenticationResult); + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Logout request failed: %s", ex.getError()), ex); } this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex); - } catch (Exception ex) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_REQUEST, + } + catch (Exception ex) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "OpenID Connect 1.0 RP-Initiated Logout Error: " + ex.getMessage(), "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling"); if (this.logger.isTraceEnabled()) { @@ -144,10 +153,11 @@ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest} - * to an instance of {@link OidcLogoutAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Logout Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract a Logout + * Request from {@link HttpServletRequest} to an instance of + * {@link OidcLogoutAuthenticationToken} used for authenticating the request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract a Logout Request from {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -155,10 +165,10 @@ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken} - * and performing the logout. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcLogoutAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcLogoutAuthenticationToken} and performing the logout. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OidcLogoutAuthenticationToken} */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -166,43 +176,44 @@ public final class OidcLogoutEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); this.authenticationFailureHandler = authenticationFailureHandler; } - private void performLogout(HttpServletRequest request, HttpServletResponse response, - Authentication authentication) throws IOException, ServletException { + private void performLogout(HttpServletRequest request, HttpServletResponse response, Authentication authentication) + throws IOException, ServletException { OidcLogoutAuthenticationToken oidcLogoutAuthentication = (OidcLogoutAuthenticationToken) authentication; // Check for active user session - if (oidcLogoutAuthentication.isPrincipalAuthenticated() && - StringUtils.hasText(oidcLogoutAuthentication.getSessionId())) { + if (oidcLogoutAuthentication.isPrincipalAuthenticated() + && StringUtils.hasText(oidcLogoutAuthentication.getSessionId())) { // Perform logout - this.logoutHandler.logout(request, response, - (Authentication) oidcLogoutAuthentication.getPrincipal()); + this.logoutHandler.logout(request, response, (Authentication) oidcLogoutAuthentication.getPrincipal()); } - if (oidcLogoutAuthentication.isAuthenticated() && - StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())) { + if (oidcLogoutAuthentication.isAuthenticated() + && StringUtils.hasText(oidcLogoutAuthentication.getPostLogoutRedirectUri())) { // Perform post-logout redirect UriComponentsBuilder uriBuilder = UriComponentsBuilder - .fromUriString(oidcLogoutAuthentication.getPostLogoutRedirectUri()); + .fromUriString(oidcLogoutAuthentication.getPostLogoutRedirectUri()); String redirectUri; if (StringUtils.hasText(oidcLogoutAuthentication.getState())) { - uriBuilder.queryParam( - OAuth2ParameterNames.STATE, + uriBuilder.queryParam(OAuth2ParameterNames.STATE, UriUtils.encode(oidcLogoutAuthentication.getState(), StandardCharsets.UTF_8)); } - redirectUri = uriBuilder.build(true).toUriString(); // build(true) -> Components are explicitly encoded + // build(true) -> Components are explicitly encoded + redirectUri = uriBuilder.build(true).toUriString(); this.redirectStrategy.sendRedirect(request, response, redirectUri); - } else { + } + else { // Perform default redirect this.logoutSuccessHandler.onLogoutSuccess(request, response, (Authentication) oidcLogoutAuthentication.getPrincipal()); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java index 1cdc155f..d4dc1a3c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilter.java @@ -51,29 +51,35 @@ import org.springframework.web.util.UriComponentsBuilder; * @since 0.1.0 * @see OidcProviderConfiguration * @see AuthorizationServerSettings - * @see 4.1. OpenID Provider Configuration Request + * @see 4.1. + * OpenID Provider Configuration Request */ public final class OidcProviderConfigurationEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for OpenID Provider Configuration requests. */ private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration"; private final RequestMatcher requestMatcher = new AntPathRequestMatcher( - DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, - HttpMethod.GET.name()); - private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = - new OidcProviderConfigurationHttpMessageConverter(); - private Consumer providerConfigurationCustomizer = (providerConfiguration) -> {}; + DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI, HttpMethod.GET.name()); + + private final OidcProviderConfigurationHttpMessageConverter providerConfigurationHttpMessageConverter = new OidcProviderConfigurationHttpMessageConverter(); + + private Consumer providerConfigurationCustomizer = (providerConfiguration) -> { + }; /** - * Sets the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder} - * allowing the ability to customize the claims of the OpenID Provider's configuration. - * - * @param providerConfigurationCustomizer the {@code Consumer} providing access to the {@link OidcProviderConfiguration.Builder} + * Sets the {@code Consumer} providing access to the + * {@link OidcProviderConfiguration.Builder} allowing the ability to customize the + * claims of the OpenID Provider's configuration. + * @param providerConfigurationCustomizer the {@code Consumer} providing access to the + * {@link OidcProviderConfiguration.Builder} * @since 0.4.0 */ - public void setProviderConfigurationCustomizer(Consumer providerConfigurationCustomizer) { + public void setProviderConfigurationCustomizer( + Consumer providerConfigurationCustomizer) { Assert.notNull(providerConfigurationCustomizer, "providerConfigurationCustomizer cannot be null"); this.providerConfigurationCustomizer = providerConfigurationCustomizer; } @@ -89,7 +95,8 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); String issuer = authorizationServerContext.getIssuer(); - AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings(); + AuthorizationServerSettings authorizationServerSettings = authorizationServerContext + .getAuthorizationServerSettings(); OidcProviderConfiguration.Builder providerConfiguration = OidcProviderConfiguration.builder() .issuer(issuer) @@ -117,8 +124,8 @@ public final class OidcProviderConfigurationEndpointFilter extends OncePerReques this.providerConfigurationCustomizer.accept(providerConfiguration); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); - this.providerConfigurationHttpMessageConverter.write( - providerConfiguration.build(), MediaType.APPLICATION_JSON, httpResponse); + this.providerConfigurationHttpMessageConverter.write(providerConfiguration.build(), MediaType.APPLICATION_JSON, + httpResponse); } private static Consumer> clientAuthenticationMethods() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilter.java index 610b3871..bafa3dc8 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilter.java @@ -57,7 +57,8 @@ import org.springframework.web.filter.OncePerRequestFilter; * @since 0.2.1 * @see OidcUserInfo * @see OidcUserInfoAuthenticationProvider - * @see 5.3. UserInfo Endpoint + * @see 5.3. + * UserInfo Endpoint */ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { @@ -67,18 +68,21 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { private static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = "/userinfo"; private final AuthenticationManager authenticationManager; + private final RequestMatcher userInfoEndpointMatcher; - private final HttpMessageConverter userInfoHttpMessageConverter = - new OidcUserInfoHttpMessageConverter(); - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter userInfoHttpMessageConverter = new OidcUserInfoHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + private AuthenticationConverter authenticationConverter = this::createAuthentication; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendUserInfoResponse; + private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; /** * Constructs an {@code OidcUserInfoEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager */ public OidcUserInfoEndpointFilter(AuthenticationManager authenticationManager) { @@ -87,9 +91,9 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { /** * Constructs an {@code OidcUserInfoEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager - * @param userInfoEndpointUri the endpoint {@code URI} for OpenID Connect 1.0 UserInfo Requests + * @param userInfoEndpointUri the endpoint {@code URI} for OpenID Connect 1.0 UserInfo + * Requests */ public OidcUserInfoEndpointFilter(AuthenticationManager authenticationManager, String userInfoEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); @@ -112,18 +116,19 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { try { Authentication userInfoAuthentication = this.authenticationConverter.convert(request); - Authentication userInfoAuthenticationResult = - this.authenticationManager.authenticate(userInfoAuthentication); + Authentication userInfoAuthenticationResult = this.authenticationManager + .authenticate(userInfoAuthentication); this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, userInfoAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("User info request failed: %s", ex.getError()), ex); } this.authenticationFailureHandler.onAuthenticationFailure(request, response, ex); - } catch (Exception ex) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_REQUEST, + } + catch (Exception ex) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "OpenID Connect 1.0 UserInfo Error: " + ex.getMessage(), "https://openid.net/specs/openid-connect-core-1_0.html#UserInfoError"); if (this.logger.isTraceEnabled()) { @@ -131,16 +136,18 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { } this.authenticationFailureHandler.onAuthenticationFailure(request, response, new OAuth2AuthenticationException(error)); - } finally { + } + finally { SecurityContextHolder.clearContext(); } } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest} - * to an instance of {@link OidcUserInfoAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an UserInfo Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract an + * UserInfo Request from {@link HttpServletRequest} to an instance of + * {@link OidcUserInfoAuthenticationToken} used for authenticating the request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract an UserInfo Request from {@link HttpServletRequest} * @since 0.4.0 */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { @@ -149,10 +156,11 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken} - * and returning the {@link OidcUserInfo UserInfo Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OidcUserInfoAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OidcUserInfoAuthenticationToken} and returning the {@link OidcUserInfo + * UserInfo Response}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OidcUserInfoAuthenticationToken} * @since 0.4.0 */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { @@ -161,10 +169,11 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} * @since 0.4.0 */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { @@ -190,7 +199,8 @@ public final class OidcUserInfoEndpointFilter extends OncePerRequestFilter { HttpStatus httpStatus = HttpStatus.BAD_REQUEST; if (error.getErrorCode().equals(OAuth2ErrorCodes.INVALID_TOKEN)) { httpStatus = HttpStatus.UNAUTHORIZED; - } else if (error.getErrorCode().equals(OAuth2ErrorCodes.INSUFFICIENT_SCOPE)) { + } + else if (error.getErrorCode().equals(OAuth2ErrorCodes.INSUFFICIENT_SCOPE)) { httpStatus = HttpStatus.FORBIDDEN; } ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java index c4933398..e383138f 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcClientRegistrationAuthenticationConverter.java @@ -34,8 +34,9 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an OpenID Connect 1.0 Dynamic Client Registration (or Client Read) Request from {@link HttpServletRequest} - * and then converts to an {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. + * Attempts to extract an OpenID Connect 1.0 Dynamic Client Registration (or Client Read) + * Request from {@link HttpServletRequest} and then converts to an + * {@link OidcClientRegistrationAuthenticationToken} used for authenticating the request. * * @author Joe Grandja * @since 0.4.0 @@ -44,8 +45,8 @@ import org.springframework.util.StringUtils; * @see OidcClientRegistrationEndpointFilter */ public final class OidcClientRegistrationAuthenticationConverter implements AuthenticationConverter { - private final HttpMessageConverter clientRegistrationHttpMessageConverter = - new OidcClientRegistrationHttpMessageConverter(); + + private final HttpMessageConverter clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter(); @Override public Authentication convert(HttpServletRequest request) { @@ -54,11 +55,11 @@ public final class OidcClientRegistrationAuthenticationConverter implements Auth if ("POST".equals(request.getMethod())) { OidcClientRegistration clientRegistration; try { - clientRegistration = this.clientRegistrationHttpMessageConverter.read( - OidcClientRegistration.class, new ServletServerHttpRequest(request)); - } catch (Exception ex) { - OAuth2Error error = new OAuth2Error( - OAuth2ErrorCodes.INVALID_REQUEST, + clientRegistration = this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, + new ServletServerHttpRequest(request)); + } + catch (Exception ex) { + OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "OpenID Client Registration Error: " + ex.getMessage(), "https://openid.net/specs/openid-connect-registration-1_0.html#RegistrationError"); throw new OAuth2AuthenticationException(error, ex); @@ -70,8 +71,7 @@ public final class OidcClientRegistrationAuthenticationConverter implements Auth // client_id (REQUIRED) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationConverter.java index dfa1f2cd..5ed7d836 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/oidc/web/authentication/OidcLogoutAuthenticationConverter.java @@ -33,8 +33,9 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an OpenID Connect 1.0 RP-Initiated Logout Request from {@link HttpServletRequest} - * and then converts to an {@link OidcLogoutAuthenticationToken} used for authenticating the request. + * Attempts to extract an OpenID Connect 1.0 RP-Initiated Logout Request from + * {@link HttpServletRequest} and then converts to an + * {@link OidcLogoutAuthenticationToken} used for authenticating the request. * * @author Joe Grandja * @since 1.1 @@ -43,20 +44,18 @@ import org.springframework.util.StringUtils; * @see OidcLogoutEndpointFilter */ public final class OidcLogoutAuthenticationConverter implements AuthenticationConverter { - private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); @Override public Authentication convert(HttpServletRequest request) { - MultiValueMap parameters = - "GET".equals(request.getMethod()) ? - OAuth2EndpointUtils.getQueryParameters(request) : - OAuth2EndpointUtils.getFormParameters(request); + MultiValueMap parameters = "GET".equals(request.getMethod()) + ? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request); - // id_token_hint (REQUIRED) // RECOMMENDED as per spec + // id_token_hint (REQUIRED) // RECOMMENDED as per spec String idTokenHint = parameters.getFirst("id_token_hint"); - if (!StringUtils.hasText(idTokenHint) || - parameters.get("id_token_hint").size() != 1) { + if (!StringUtils.hasText(idTokenHint) || parameters.get("id_token_hint").size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, "id_token_hint"); } @@ -73,33 +72,28 @@ public final class OidcLogoutAuthenticationConverter implements AuthenticationCo // client_id (OPTIONAL) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (StringUtils.hasText(clientId) && - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (StringUtils.hasText(clientId) && parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID); } // post_logout_redirect_uri (OPTIONAL) String postLogoutRedirectUri = parameters.getFirst("post_logout_redirect_uri"); - if (StringUtils.hasText(postLogoutRedirectUri) && - parameters.get("post_logout_redirect_uri").size() != 1) { + if (StringUtils.hasText(postLogoutRedirectUri) && parameters.get("post_logout_redirect_uri").size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, "post_logout_redirect_uri"); } // state (OPTIONAL) String state = parameters.getFirst(OAuth2ParameterNames.STATE); - if (StringUtils.hasText(state) && - parameters.get(OAuth2ParameterNames.STATE).size() != 1) { + if (StringUtils.hasText(state) && parameters.get(OAuth2ParameterNames.STATE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE); } - return new OidcLogoutAuthenticationToken(idTokenHint, principal, - sessionId, clientId, postLogoutRedirectUri, state); + return new OidcLogoutAuthenticationToken(idTokenHint, principal, sessionId, clientId, postLogoutRedirectUri, + state); } private static void throwError(String errorCode, String parameterName) { - OAuth2Error error = new OAuth2Error( - errorCode, - "OpenID Connect 1.0 Logout Request Parameter: " + parameterName, + OAuth2Error error = new OAuth2Error(errorCode, "OpenID Connect 1.0 Logout Request Parameter: " + parameterName, "https://openid.net/specs/openid-connect-rpinitiated-1_0.html#ValidationAndErrorHandling"); throw new OAuth2AuthenticationException(error); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AbstractSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AbstractSettings.java index 5333b1d9..53638bf0 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AbstractSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AbstractSettings.java @@ -32,7 +32,9 @@ import org.springframework.util.Assert; * @since 0.0.2 */ public abstract class AbstractSettings implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; + private final Map settings; protected AbstractSettings(Map settings) { @@ -42,7 +44,6 @@ public abstract class AbstractSettings implements Serializable { /** * Returns a configuration setting. - * * @param name the name of the setting * @param the type of the setting * @return the value of the setting, or {@code null} if not available @@ -55,7 +56,6 @@ public abstract class AbstractSettings implements Serializable { /** * Returns a {@code Map} of the configuration settings. - * * @return a {@code Map} of the configuration settings */ public Map getSettings() { @@ -81,15 +81,14 @@ public abstract class AbstractSettings implements Serializable { @Override public String toString() { - return "AbstractSettings {" + - "settings=" + this.settings + - '}'; + return "AbstractSettings {" + "settings=" + this.settings + '}'; } /** * A builder for subclasses of {@link AbstractSettings}. */ protected static abstract class AbstractBuilder> { + private final Map settings = new HashMap<>(); protected AbstractBuilder() { @@ -97,7 +96,6 @@ public abstract class AbstractSettings implements Serializable { /** * Sets a configuration setting. - * * @param name the name of the setting * @param value the value of the setting * @return the {@link AbstractBuilder} for further configuration @@ -110,10 +108,10 @@ public abstract class AbstractSettings implements Serializable { } /** - * A {@code Consumer} of the configuration settings {@code Map} - * allowing the ability to add, replace, or remove. - * - * @param settingsConsumer a {@link Consumer} of the configuration settings {@code Map} + * A {@code Consumer} of the configuration settings {@code Map} allowing the + * ability to add, replace, or remove. + * @param settingsConsumer a {@link Consumer} of the configuration settings + * {@code Map} * @return the {@link AbstractBuilder} for further configuration */ public B settings(Consumer> settingsConsumer) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java index 4698da24..98860965 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettings.java @@ -36,7 +36,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Returns the URL of the Authorization Server's Issuer Identifier. - * * @return the URL of the Authorization Server's Issuer Identifier */ public String getIssuer() { @@ -44,8 +43,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OAuth 2.0 Authorization endpoint. The default is {@code /oauth2/authorize}. - * + * Returns the OAuth 2.0 Authorization endpoint. The default is + * {@code /oauth2/authorize}. * @return the Authorization endpoint */ public String getAuthorizationEndpoint() { @@ -53,8 +52,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OAuth 2.0 Device Authorization endpoint. The default is {@code /oauth2/device_authorization}. - * + * Returns the OAuth 2.0 Device Authorization endpoint. The default is + * {@code /oauth2/device_authorization}. * @return the Device Authorization endpoint * @since 1.1 */ @@ -63,8 +62,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OAuth 2.0 Device Verification endpoint. The default is {@code /oauth2/device_verification}. - * + * Returns the OAuth 2.0 Device Verification endpoint. The default is + * {@code /oauth2/device_verification}. * @return the Device Verification endpoint * @since 1.1 */ @@ -74,7 +73,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Returns the OAuth 2.0 Token endpoint. The default is {@code /oauth2/token}. - * * @return the Token endpoint */ public String getTokenEndpoint() { @@ -83,7 +81,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Returns the JWK Set endpoint. The default is {@code /oauth2/jwks}. - * * @return the JWK Set endpoint */ public String getJwkSetEndpoint() { @@ -91,8 +88,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OAuth 2.0 Token Revocation endpoint. The default is {@code /oauth2/revoke}. - * + * Returns the OAuth 2.0 Token Revocation endpoint. The default is + * {@code /oauth2/revoke}. * @return the Token Revocation endpoint */ public String getTokenRevocationEndpoint() { @@ -100,8 +97,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OAuth 2.0 Token Introspection endpoint. The default is {@code /oauth2/introspect}. - * + * Returns the OAuth 2.0 Token Introspection endpoint. The default is + * {@code /oauth2/introspect}. * @return the Token Introspection endpoint */ public String getTokenIntrospectionEndpoint() { @@ -109,8 +106,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OpenID Connect 1.0 Client Registration endpoint. The default is {@code /connect/register}. - * + * Returns the OpenID Connect 1.0 Client Registration endpoint. The default is + * {@code /connect/register}. * @return the OpenID Connect 1.0 Client Registration endpoint */ public String getOidcClientRegistrationEndpoint() { @@ -119,7 +116,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Returns the OpenID Connect 1.0 UserInfo endpoint. The default is {@code /userinfo}. - * * @return the OpenID Connect 1.0 UserInfo endpoint */ public String getOidcUserInfoEndpoint() { @@ -127,8 +123,8 @@ public final class AuthorizationServerSettings extends AbstractSettings { } /** - * Returns the OpenID Connect 1.0 Logout endpoint. The default is {@code /connect/logout}. - * + * Returns the OpenID Connect 1.0 Logout endpoint. The default is + * {@code /connect/logout}. * @return the OpenID Connect 1.0 Logout endpoint * @since 1.1 */ @@ -138,33 +134,29 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Constructs a new {@link Builder} with the default settings. - * * @return the {@link Builder} */ public static Builder builder() { - return new Builder() - .authorizationEndpoint("/oauth2/authorize") - .deviceAuthorizationEndpoint("/oauth2/device_authorization") - .deviceVerificationEndpoint("/oauth2/device_verification") - .tokenEndpoint("/oauth2/token") - .jwkSetEndpoint("/oauth2/jwks") - .tokenRevocationEndpoint("/oauth2/revoke") - .tokenIntrospectionEndpoint("/oauth2/introspect") - .oidcClientRegistrationEndpoint("/connect/register") - .oidcUserInfoEndpoint("/userinfo") - .oidcLogoutEndpoint("/connect/logout"); + return new Builder().authorizationEndpoint("/oauth2/authorize") + .deviceAuthorizationEndpoint("/oauth2/device_authorization") + .deviceVerificationEndpoint("/oauth2/device_verification") + .tokenEndpoint("/oauth2/token") + .jwkSetEndpoint("/oauth2/jwks") + .tokenRevocationEndpoint("/oauth2/revoke") + .tokenIntrospectionEndpoint("/oauth2/introspect") + .oidcClientRegistrationEndpoint("/connect/register") + .oidcUserInfoEndpoint("/userinfo") + .oidcLogoutEndpoint("/connect/logout"); } /** * Constructs a new {@link Builder} with the provided settings. - * * @param settings the settings to initialize the builder * @return the {@link Builder} */ public static Builder withSettings(Map settings) { Assert.notEmpty(settings, "settings cannot be empty"); - return new Builder() - .settings(s -> s.putAll(settings)); + return new Builder().settings(s -> s.putAll(settings)); } /** @@ -177,7 +169,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the URL the Authorization Server uses as its Issuer Identifier. - * * @param issuer the URL the Authorization Server uses as its Issuer Identifier. * @return the {@link Builder} for further configuration */ @@ -187,7 +178,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the OAuth 2.0 Authorization endpoint. - * * @param authorizationEndpoint the Authorization endpoint * @return the {@link Builder} for further configuration */ @@ -197,29 +187,28 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the OAuth 2.0 Device Authorization endpoint. - * * @param deviceAuthorizationEndpoint the Device Authorization endpoint * @return the {@link Builder} for further configuration * @since 1.1 */ public Builder deviceAuthorizationEndpoint(String deviceAuthorizationEndpoint) { - return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT, deviceAuthorizationEndpoint); + return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_AUTHORIZATION_ENDPOINT, + deviceAuthorizationEndpoint); } /** * Sets the OAuth 2.0 Device Verification endpoint. - * * @param deviceVerificationEndpoint the Device Verification endpoint * @return the {@link Builder} for further configuration * @since 1.1 */ public Builder deviceVerificationEndpoint(String deviceVerificationEndpoint) { - return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT, deviceVerificationEndpoint); + return setting(ConfigurationSettingNames.AuthorizationServer.DEVICE_VERIFICATION_ENDPOINT, + deviceVerificationEndpoint); } /** * Sets the OAuth 2.0 Token endpoint. - * * @param tokenEndpoint the Token endpoint * @return the {@link Builder} for further configuration */ @@ -229,7 +218,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the JWK Set endpoint. - * * @param jwkSetEndpoint the JWK Set endpoint * @return the {@link Builder} for further configuration */ @@ -239,37 +227,37 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the OAuth 2.0 Token Revocation endpoint. - * * @param tokenRevocationEndpoint the Token Revocation endpoint * @return the {@link Builder} for further configuration */ public Builder tokenRevocationEndpoint(String tokenRevocationEndpoint) { - return setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_REVOCATION_ENDPOINT, tokenRevocationEndpoint); + return setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_REVOCATION_ENDPOINT, + tokenRevocationEndpoint); } /** * Sets the OAuth 2.0 Token Introspection endpoint. - * * @param tokenIntrospectionEndpoint the Token Introspection endpoint * @return the {@link Builder} for further configuration */ public Builder tokenIntrospectionEndpoint(String tokenIntrospectionEndpoint) { - return setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_INTROSPECTION_ENDPOINT, tokenIntrospectionEndpoint); + return setting(ConfigurationSettingNames.AuthorizationServer.TOKEN_INTROSPECTION_ENDPOINT, + tokenIntrospectionEndpoint); } /** * Sets the OpenID Connect 1.0 Client Registration endpoint. - * - * @param oidcClientRegistrationEndpoint the OpenID Connect 1.0 Client Registration endpoint + * @param oidcClientRegistrationEndpoint the OpenID Connect 1.0 Client + * Registration endpoint * @return the {@link Builder} for further configuration */ public Builder oidcClientRegistrationEndpoint(String oidcClientRegistrationEndpoint) { - return setting(ConfigurationSettingNames.AuthorizationServer.OIDC_CLIENT_REGISTRATION_ENDPOINT, oidcClientRegistrationEndpoint); + return setting(ConfigurationSettingNames.AuthorizationServer.OIDC_CLIENT_REGISTRATION_ENDPOINT, + oidcClientRegistrationEndpoint); } /** * Sets the OpenID Connect 1.0 UserInfo endpoint. - * * @param oidcUserInfoEndpoint the OpenID Connect 1.0 UserInfo endpoint * @return the {@link Builder} for further configuration */ @@ -279,7 +267,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Sets the OpenID Connect 1.0 Logout endpoint. - * * @param oidcLogoutEndpoint the OpenID Connect 1.0 Logout endpoint * @return the {@link Builder} for further configuration * @since 1.1 @@ -290,7 +277,6 @@ public final class AuthorizationServerSettings extends AbstractSettings { /** * Builds the {@link AuthorizationServerSettings}. - * * @return the {@link AuthorizationServerSettings} */ @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettings.java index 131010b0..d60762a4 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettings.java @@ -37,20 +37,21 @@ public final class ClientSettings extends AbstractSettings { } /** - * Returns {@code true} if the client is required to provide a proof key challenge and verifier - * when performing the Authorization Code Grant flow. The default is {@code false}. - * - * @return {@code true} if the client is required to provide a proof key challenge and verifier, {@code false} otherwise + * Returns {@code true} if the client is required to provide a proof key challenge and + * verifier when performing the Authorization Code Grant flow. The default is + * {@code false}. + * @return {@code true} if the client is required to provide a proof key challenge and + * verifier, {@code false} otherwise */ public boolean isRequireProofKey() { return getSetting(ConfigurationSettingNames.Client.REQUIRE_PROOF_KEY); } /** - * Returns {@code true} if authorization consent is required when the client requests access. - * The default is {@code false}. - * - * @return {@code true} if authorization consent is required when the client requests access, {@code false} otherwise + * Returns {@code true} if authorization consent is required when the client requests + * access. The default is {@code false}. + * @return {@code true} if authorization consent is required when the client requests + * access, {@code false} otherwise */ public boolean isRequireAuthorizationConsent() { return getSetting(ConfigurationSettingNames.Client.REQUIRE_AUTHORIZATION_CONSENT); @@ -58,7 +59,6 @@ public final class ClientSettings extends AbstractSettings { /** * Returns the {@code URL} for the Client's JSON Web Key Set. - * * @return the {@code URL} for the Client's JSON Web Key Set * @since 0.2.2 */ @@ -67,11 +67,13 @@ public final class ClientSettings extends AbstractSettings { } /** - * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate - * the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods. - * - * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate the Client at the Token Endpoint + * Returns the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the + * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and + * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} + * authentication methods. + * @return the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint * @since 0.2.2 */ public JwsAlgorithm getTokenEndpointAuthenticationSigningAlgorithm() { @@ -80,25 +82,20 @@ public final class ClientSettings extends AbstractSettings { /** * Constructs a new {@link Builder} with the default settings. - * * @return the {@link Builder} */ public static Builder builder() { - return new Builder() - .requireProofKey(false) - .requireAuthorizationConsent(false); + return new Builder().requireProofKey(false).requireAuthorizationConsent(false); } /** * Constructs a new {@link Builder} with the provided settings. - * * @param settings the settings to initialize the builder * @return the {@link Builder} */ public static Builder withSettings(Map settings) { Assert.notEmpty(settings, "settings cannot be empty"); - return new Builder() - .settings(s -> s.putAll(settings)); + return new Builder().settings(s -> s.putAll(settings)); } /** @@ -110,10 +107,10 @@ public final class ClientSettings extends AbstractSettings { } /** - * Set to {@code true} if the client is required to provide a proof key challenge and verifier - * when performing the Authorization Code Grant flow. - * - * @param requireProofKey {@code true} if the client is required to provide a proof key challenge and verifier, {@code false} otherwise + * Set to {@code true} if the client is required to provide a proof key challenge + * and verifier when performing the Authorization Code Grant flow. + * @param requireProofKey {@code true} if the client is required to provide a + * proof key challenge and verifier, {@code false} otherwise * @return the {@link Builder} for further configuration */ public Builder requireProofKey(boolean requireProofKey) { @@ -121,10 +118,11 @@ public final class ClientSettings extends AbstractSettings { } /** - * Set to {@code true} if authorization consent is required when the client requests access. - * This applies to all interactive flows (e.g. {@code authorization_code} and {@code device_code}). - * - * @param requireAuthorizationConsent {@code true} if authorization consent is required when the client requests access, {@code false} otherwise + * Set to {@code true} if authorization consent is required when the client + * requests access. This applies to all interactive flows (e.g. + * {@code authorization_code} and {@code device_code}). + * @param requireAuthorizationConsent {@code true} if authorization consent is + * required when the client requests access, {@code false} otherwise * @return the {@link Builder} for further configuration */ public Builder requireAuthorizationConsent(boolean requireAuthorizationConsent) { @@ -133,7 +131,6 @@ public final class ClientSettings extends AbstractSettings { /** * Sets the {@code URL} for the Client's JSON Web Key Set. - * * @param jwkSetUrl the {@code URL} for the Client's JSON Web Key Set * @return the {@link Builder} for further configuration * @since 0.2.2 @@ -143,22 +140,24 @@ public final class ClientSettings extends AbstractSettings { } /** - * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} used to authenticate - * the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods. - - * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} - * used to authenticate the Client at the Token Endpoint + * Sets the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the + * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and + * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} + * authentication methods. + * @param authenticationSigningAlgorithm the {@link JwsAlgorithm JWS} algorithm + * that must be used for signing the {@link Jwt JWT} used to authenticate the + * Client at the Token Endpoint * @return the {@link Builder} for further configuration * @since 0.2.2 */ public Builder tokenEndpointAuthenticationSigningAlgorithm(JwsAlgorithm authenticationSigningAlgorithm) { - return setting(ConfigurationSettingNames.Client.TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM, authenticationSigningAlgorithm); + return setting(ConfigurationSettingNames.Client.TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM, + authenticationSigningAlgorithm); } /** * Builds the {@link ClientSettings}. - * * @return the {@link ClientSettings} */ @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java index c51e545a..522630d6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/ConfigurationSettingNames.java @@ -28,6 +28,7 @@ import org.springframework.security.oauth2.jwt.Jwt; * @since 0.2.0 */ public final class ConfigurationSettingNames { + private static final String SETTINGS_NAMESPACE = "settings."; private ConfigurationSettingNames() { @@ -37,19 +38,22 @@ public final class ConfigurationSettingNames { * The names for client configuration settings. */ public static final class Client { + private static final String CLIENT_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE.concat("client."); /** - * Set to {@code true} if the client is required to provide a proof key challenge and verifier - * when performing the Authorization Code Grant flow. + * Set to {@code true} if the client is required to provide a proof key challenge + * and verifier when performing the Authorization Code Grant flow. */ public static final String REQUIRE_PROOF_KEY = CLIENT_SETTINGS_NAMESPACE.concat("require-proof-key"); /** - * Set to {@code true} if authorization consent is required when the client requests access. - * This applies to all interactive flows (e.g. {@code authorization_code} and {@code device_code}). + * Set to {@code true} if authorization consent is required when the client + * requests access. This applies to all interactive flows (e.g. + * {@code authorization_code} and {@code device_code}). */ - public static final String REQUIRE_AUTHORIZATION_CONSENT = CLIENT_SETTINGS_NAMESPACE.concat("require-authorization-consent"); + public static final String REQUIRE_AUTHORIZATION_CONSENT = CLIENT_SETTINGS_NAMESPACE + .concat("require-authorization-consent"); /** * Set the {@code URL} for the Client's JSON Web Key Set. @@ -58,12 +62,15 @@ public final class ConfigurationSettingNames { public static final String JWK_SET_URL = CLIENT_SETTINGS_NAMESPACE.concat("jwk-set-url"); /** - * Set the {@link JwsAlgorithm JWS} algorithm that must be used for signing the {@link Jwt JWT} - * used to authenticate the Client at the Token Endpoint for the {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and - * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} authentication methods. + * Set the {@link JwsAlgorithm JWS} algorithm that must be used for signing the + * {@link Jwt JWT} used to authenticate the Client at the Token Endpoint for the + * {@link ClientAuthenticationMethod#PRIVATE_KEY_JWT private_key_jwt} and + * {@link ClientAuthenticationMethod#CLIENT_SECRET_JWT client_secret_jwt} + * authentication methods. * @since 0.2.2 */ - public static final String TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM = CLIENT_SETTINGS_NAMESPACE.concat("token-endpoint-authentication-signing-algorithm"); + public static final String TOKEN_ENDPOINT_AUTHENTICATION_SIGNING_ALGORITHM = CLIENT_SETTINGS_NAMESPACE + .concat("token-endpoint-authentication-signing-algorithm"); private Client() { } @@ -74,7 +81,9 @@ public final class ConfigurationSettingNames { * The names for authorization server configuration settings. */ public static final class AuthorizationServer { - private static final String AUTHORIZATION_SERVER_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE.concat("authorization-server."); + + private static final String AUTHORIZATION_SERVER_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE + .concat("authorization-server."); /** * Set the URL the Authorization Server uses as its Issuer Identifier. @@ -84,17 +93,20 @@ public final class ConfigurationSettingNames { /** * Set the OAuth 2.0 Authorization endpoint. */ - public static final String AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("authorization-endpoint"); + public static final String AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("authorization-endpoint"); /** * Set the OAuth 2.0 Device Authorization endpoint. */ - public static final String DEVICE_AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("device-authorization-endpoint"); + public static final String DEVICE_AUTHORIZATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("device-authorization-endpoint"); /** * Set the OAuth 2.0 Device Verification endpoint. */ - public static final String DEVICE_VERIFICATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("device-verification-endpoint"); + public static final String DEVICE_VERIFICATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("device-verification-endpoint"); /** * Set the OAuth 2.0 Token endpoint. @@ -104,33 +116,39 @@ public final class ConfigurationSettingNames { /** * Set the JWK Set endpoint. */ - public static final String JWK_SET_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("jwk-set-endpoint"); + public static final String JWK_SET_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("jwk-set-endpoint"); /** * Set the OAuth 2.0 Token Revocation endpoint. */ - public static final String TOKEN_REVOCATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("token-revocation-endpoint"); + public static final String TOKEN_REVOCATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("token-revocation-endpoint"); /** * Set the OAuth 2.0 Token Introspection endpoint. */ - public static final String TOKEN_INTROSPECTION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("token-introspection-endpoint"); + public static final String TOKEN_INTROSPECTION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("token-introspection-endpoint"); /** * Set the OpenID Connect 1.0 Client Registration endpoint. */ - public static final String OIDC_CLIENT_REGISTRATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("oidc-client-registration-endpoint"); + public static final String OIDC_CLIENT_REGISTRATION_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("oidc-client-registration-endpoint"); /** * Set the OpenID Connect 1.0 UserInfo endpoint. */ - public static final String OIDC_USER_INFO_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("oidc-user-info-endpoint"); + public static final String OIDC_USER_INFO_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("oidc-user-info-endpoint"); /** * Set the OpenID Connect 1.0 Logout endpoint. * @since 1.1 */ - public static final String OIDC_LOGOUT_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE.concat("oidc-logout-endpoint"); + public static final String OIDC_LOGOUT_ENDPOINT = AUTHORIZATION_SERVER_SETTINGS_NAMESPACE + .concat("oidc-logout-endpoint"); private AuthorizationServer() { } @@ -141,18 +159,21 @@ public final class ConfigurationSettingNames { * The names for token configuration settings. */ public static final class Token { + private static final String TOKEN_SETTINGS_NAMESPACE = SETTINGS_NAMESPACE.concat("token."); /** * Set the time-to-live for an authorization code. * @since 0.4.0 */ - public static final String AUTHORIZATION_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE.concat("authorization-code-time-to-live"); + public static final String AUTHORIZATION_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE + .concat("authorization-code-time-to-live"); /** * Set the time-to-live for an access token. */ - public static final String ACCESS_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE.concat("access-token-time-to-live"); + public static final String ACCESS_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE + .concat("access-token-time-to-live"); /** * Set the {@link OAuth2TokenFormat token format} for an access token. @@ -164,23 +185,27 @@ public final class ConfigurationSettingNames { * Set the time-to-live for a device code. * @since 1.1 */ - public static final String DEVICE_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE.concat("device-code-time-to-live"); + public static final String DEVICE_CODE_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE + .concat("device-code-time-to-live"); /** - * Set to {@code true} if refresh tokens are reused when returning the access token response, - * or {@code false} if a new refresh token is issued. + * Set to {@code true} if refresh tokens are reused when returning the access + * token response, or {@code false} if a new refresh token is issued. */ public static final String REUSE_REFRESH_TOKENS = TOKEN_SETTINGS_NAMESPACE.concat("reuse-refresh-tokens"); /** * Set the time-to-live for a refresh token. */ - public static final String REFRESH_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE.concat("refresh-token-time-to-live"); + public static final String REFRESH_TOKEN_TIME_TO_LIVE = TOKEN_SETTINGS_NAMESPACE + .concat("refresh-token-time-to-live"); /** - * Set the {@link SignatureAlgorithm JWS} algorithm for signing the {@link OidcIdToken ID Token}. + * Set the {@link SignatureAlgorithm JWS} algorithm for signing the + * {@link OidcIdToken ID Token}. */ - public static final String ID_TOKEN_SIGNATURE_ALGORITHM = TOKEN_SETTINGS_NAMESPACE.concat("id-token-signature-algorithm"); + public static final String ID_TOKEN_SIGNATURE_ALGORITHM = TOKEN_SETTINGS_NAMESPACE + .concat("id-token-signature-algorithm"); private Token() { } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/OAuth2TokenFormat.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/OAuth2TokenFormat.java index d01bb429..ff389244 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/OAuth2TokenFormat.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/OAuth2TokenFormat.java @@ -27,17 +27,19 @@ import org.springframework.util.Assert; * @since 0.2.3 */ public final class OAuth2TokenFormat implements Serializable { + private static final long serialVersionUID = SpringAuthorizationServerVersion.SERIAL_VERSION_UID; /** - * Self-contained tokens use a protected, time-limited data structure that contains token metadata - * and claims of the user and/or client. JSON Web Token (JWT) is a widely used format. + * Self-contained tokens use a protected, time-limited data structure that contains + * token metadata and claims of the user and/or client. JSON Web Token (JWT) is a + * widely used format. */ public static final OAuth2TokenFormat SELF_CONTAINED = new OAuth2TokenFormat("self-contained"); /** - * Reference (opaque) tokens are unique identifiers that serve as a reference - * to the token metadata and claims of the user and/or client, stored at the provider. + * Reference (opaque) tokens are unique identifiers that serve as a reference to the + * token metadata and claims of the user and/or client, stored at the provider. */ public static final OAuth2TokenFormat REFERENCE = new OAuth2TokenFormat("reference"); @@ -45,7 +47,6 @@ public final class OAuth2TokenFormat implements Serializable { /** * Constructs an {@code OAuth2TokenFormat} using the provided value. - * * @param value the value of the token format */ public OAuth2TokenFormat(String value) { @@ -55,7 +56,6 @@ public final class OAuth2TokenFormat implements Serializable { /** * Returns the value of the token format. - * * @return the value of the token format */ public String getValue() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java index 2cbb024c..67f29f2c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettings.java @@ -38,7 +38,6 @@ public final class TokenSettings extends AbstractSettings { /** * Returns the time-to-live for an authorization code. The default is 5 minutes. - * * @return the time-to-live for an authorization code * @since 0.4.0 */ @@ -48,7 +47,6 @@ public final class TokenSettings extends AbstractSettings { /** * Returns the time-to-live for an access token. The default is 5 minutes. - * * @return the time-to-live for an access token */ public Duration getAccessTokenTimeToLive() { @@ -56,9 +54,8 @@ public final class TokenSettings extends AbstractSettings { } /** - * Returns the token format for an access token. - * The default is {@link OAuth2TokenFormat#SELF_CONTAINED}. - * + * Returns the token format for an access token. The default is + * {@link OAuth2TokenFormat#SELF_CONTAINED}. * @return the token format for an access token * @since 0.2.3 */ @@ -68,7 +65,6 @@ public final class TokenSettings extends AbstractSettings { /** * Returns the time-to-live for a device code. The default is 5 minutes. - * * @return the time-to-live for a device code * @since 1.1 */ @@ -77,8 +73,9 @@ public final class TokenSettings extends AbstractSettings { } /** - * Returns {@code true} if refresh tokens are reused when returning the access token response, - * or {@code false} if a new refresh token is issued. The default is {@code true}. + * Returns {@code true} if refresh tokens are reused when returning the access token + * response, or {@code false} if a new refresh token is issued. The default is + * {@code true}. */ public boolean isReuseRefreshTokens() { return getSetting(ConfigurationSettingNames.Token.REUSE_REFRESH_TOKENS); @@ -86,7 +83,6 @@ public final class TokenSettings extends AbstractSettings { /** * Returns the time-to-live for a refresh token. The default is 60 minutes. - * * @return the time-to-live for a refresh token */ public Duration getRefreshTokenTimeToLive() { @@ -94,10 +90,11 @@ public final class TokenSettings extends AbstractSettings { } /** - * Returns the {@link SignatureAlgorithm JWS} algorithm for signing the {@link OidcIdToken ID Token}. - * The default is {@link SignatureAlgorithm#RS256 RS256}. - * - * @return the {@link SignatureAlgorithm JWS} algorithm for signing the {@link OidcIdToken ID Token} + * Returns the {@link SignatureAlgorithm JWS} algorithm for signing the + * {@link OidcIdToken ID Token}. The default is {@link SignatureAlgorithm#RS256 + * RS256}. + * @return the {@link SignatureAlgorithm JWS} algorithm for signing the + * {@link OidcIdToken ID Token} */ public SignatureAlgorithm getIdTokenSignatureAlgorithm() { return getSetting(ConfigurationSettingNames.Token.ID_TOKEN_SIGNATURE_ALGORITHM); @@ -105,30 +102,26 @@ public final class TokenSettings extends AbstractSettings { /** * Constructs a new {@link Builder} with the default settings. - * * @return the {@link Builder} */ public static Builder builder() { - return new Builder() - .authorizationCodeTimeToLive(Duration.ofMinutes(5)) - .accessTokenTimeToLive(Duration.ofMinutes(5)) - .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) - .deviceCodeTimeToLive(Duration.ofMinutes(5)) - .reuseRefreshTokens(true) - .refreshTokenTimeToLive(Duration.ofMinutes(60)) - .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256); + return new Builder().authorizationCodeTimeToLive(Duration.ofMinutes(5)) + .accessTokenTimeToLive(Duration.ofMinutes(5)) + .accessTokenFormat(OAuth2TokenFormat.SELF_CONTAINED) + .deviceCodeTimeToLive(Duration.ofMinutes(5)) + .reuseRefreshTokens(true) + .refreshTokenTimeToLive(Duration.ofMinutes(60)) + .idTokenSignatureAlgorithm(SignatureAlgorithm.RS256); } /** * Constructs a new {@link Builder} with the provided settings. - * * @param settings the settings to initialize the builder * @return the {@link Builder} */ public static Builder withSettings(Map settings) { Assert.notEmpty(settings, "settings cannot be empty"); - return new Builder() - .settings(s -> s.putAll(settings)); + return new Builder().settings(s -> s.putAll(settings)); } /** @@ -140,34 +133,36 @@ public final class TokenSettings extends AbstractSettings { } /** - * Set the time-to-live for an authorization code. Must be greater than {@code Duration.ZERO}. - * A maximum authorization code lifetime of 10 minutes is RECOMMENDED. - * + * Set the time-to-live for an authorization code. Must be greater than + * {@code Duration.ZERO}. A maximum authorization code lifetime of 10 minutes is + * RECOMMENDED. * @param authorizationCodeTimeToLive the time-to-live for an authorization code * @return the {@link Builder} for further configuration * @since 0.4.0 */ public Builder authorizationCodeTimeToLive(Duration authorizationCodeTimeToLive) { Assert.notNull(authorizationCodeTimeToLive, "authorizationCodeTimeToLive cannot be null"); - Assert.isTrue(authorizationCodeTimeToLive.getSeconds() > 0, "authorizationCodeTimeToLive must be greater than Duration.ZERO"); - return setting(ConfigurationSettingNames.Token.AUTHORIZATION_CODE_TIME_TO_LIVE, authorizationCodeTimeToLive); + Assert.isTrue(authorizationCodeTimeToLive.getSeconds() > 0, + "authorizationCodeTimeToLive must be greater than Duration.ZERO"); + return setting(ConfigurationSettingNames.Token.AUTHORIZATION_CODE_TIME_TO_LIVE, + authorizationCodeTimeToLive); } /** - * Set the time-to-live for an access token. Must be greater than {@code Duration.ZERO}. - * + * Set the time-to-live for an access token. Must be greater than + * {@code Duration.ZERO}. * @param accessTokenTimeToLive the time-to-live for an access token * @return the {@link Builder} for further configuration */ public Builder accessTokenTimeToLive(Duration accessTokenTimeToLive) { Assert.notNull(accessTokenTimeToLive, "accessTokenTimeToLive cannot be null"); - Assert.isTrue(accessTokenTimeToLive.getSeconds() > 0, "accessTokenTimeToLive must be greater than Duration.ZERO"); + Assert.isTrue(accessTokenTimeToLive.getSeconds() > 0, + "accessTokenTimeToLive must be greater than Duration.ZERO"); return setting(ConfigurationSettingNames.Token.ACCESS_TOKEN_TIME_TO_LIVE, accessTokenTimeToLive); } /** * Set the token format for an access token. - * * @param accessTokenFormat the token format for an access token * @return the {@link Builder} for further configuration * @since 0.2.3 @@ -178,23 +173,24 @@ public final class TokenSettings extends AbstractSettings { } /** - * Set the time-to-live for a device code. Must be greater than {@code Duration.ZERO}. - * + * Set the time-to-live for a device code. Must be greater than + * {@code Duration.ZERO}. * @param deviceCodeTimeToLive the time-to-live for a device code * @return the {@link Builder} for further configuration * @since 1.1 */ public Builder deviceCodeTimeToLive(Duration deviceCodeTimeToLive) { Assert.notNull(deviceCodeTimeToLive, "deviceCodeTimeToLive cannot be null"); - Assert.isTrue(deviceCodeTimeToLive.getSeconds() > 0, "deviceCodeTimeToLive must be greater than Duration.ZERO"); + Assert.isTrue(deviceCodeTimeToLive.getSeconds() > 0, + "deviceCodeTimeToLive must be greater than Duration.ZERO"); return setting(ConfigurationSettingNames.Token.DEVICE_CODE_TIME_TO_LIVE, deviceCodeTimeToLive); } /** - * Set to {@code true} if refresh tokens are reused when returning the access token response, - * or {@code false} if a new refresh token is issued. - * - * @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} to issue new refresh tokens + * Set to {@code true} if refresh tokens are reused when returning the access + * token response, or {@code false} if a new refresh token is issued. + * @param reuseRefreshTokens {@code true} to reuse refresh tokens, {@code false} + * to issue new refresh tokens * @return the {@link Builder} for further configuration */ public Builder reuseRefreshTokens(boolean reuseRefreshTokens) { @@ -202,21 +198,23 @@ public final class TokenSettings extends AbstractSettings { } /** - * Set the time-to-live for a refresh token. Must be greater than {@code Duration.ZERO}. - * + * Set the time-to-live for a refresh token. Must be greater than + * {@code Duration.ZERO}. * @param refreshTokenTimeToLive the time-to-live for a refresh token * @return the {@link Builder} for further configuration */ public Builder refreshTokenTimeToLive(Duration refreshTokenTimeToLive) { Assert.notNull(refreshTokenTimeToLive, "refreshTokenTimeToLive cannot be null"); - Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0, "refreshTokenTimeToLive must be greater than Duration.ZERO"); + Assert.isTrue(refreshTokenTimeToLive.getSeconds() > 0, + "refreshTokenTimeToLive must be greater than Duration.ZERO"); return setting(ConfigurationSettingNames.Token.REFRESH_TOKEN_TIME_TO_LIVE, refreshTokenTimeToLive); } /** - * Sets the {@link SignatureAlgorithm JWS} algorithm for signing the {@link OidcIdToken ID Token}. - * - * @param idTokenSignatureAlgorithm the {@link SignatureAlgorithm JWS} algorithm for signing the {@link OidcIdToken ID Token} + * Sets the {@link SignatureAlgorithm JWS} algorithm for signing the + * {@link OidcIdToken ID Token}. + * @param idTokenSignatureAlgorithm the {@link SignatureAlgorithm JWS} algorithm + * for signing the {@link OidcIdToken ID Token} * @return the {@link Builder} for further configuration */ public Builder idTokenSignatureAlgorithm(SignatureAlgorithm idTokenSignatureAlgorithm) { @@ -226,7 +224,6 @@ public final class TokenSettings extends AbstractSettings { /** * Builds the {@link TokenSettings}. - * * @return the {@link TokenSettings} */ @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java index 4486b573..34b38861 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DefaultOAuth2TokenContext.java @@ -30,6 +30,7 @@ import org.springframework.util.Assert; * @see OAuth2TokenContext */ public final class DefaultOAuth2TokenContext implements OAuth2TokenContext { + private final Map context; private DefaultOAuth2TokenContext(Map context) { @@ -51,7 +52,6 @@ public final class DefaultOAuth2TokenContext implements OAuth2TokenContext { /** * Returns a new {@link Builder}. - * * @return the {@link Builder} */ public static Builder builder() { @@ -68,7 +68,6 @@ public final class DefaultOAuth2TokenContext implements OAuth2TokenContext { /** * Builds a new {@link DefaultOAuth2TokenContext}. - * * @return the {@link DefaultOAuth2TokenContext} */ public DefaultOAuth2TokenContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGenerator.java index 8e5b6330..b3caaf27 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGenerator.java @@ -24,12 +24,12 @@ import org.springframework.security.oauth2.core.OAuth2Token; import org.springframework.util.Assert; /** - * An {@link OAuth2TokenGenerator} that simply delegates to it's - * internal {@code List} of {@link OAuth2TokenGenerator}(s). + * An {@link OAuth2TokenGenerator} that simply delegates to it's internal {@code List} of + * {@link OAuth2TokenGenerator}(s). *

* Each {@link OAuth2TokenGenerator} is given a chance to - * {@link OAuth2TokenGenerator#generate(OAuth2TokenContext)} - * with the first {@code non-null} {@link OAuth2Token} being returned. + * {@link OAuth2TokenGenerator#generate(OAuth2TokenContext)} with the first + * {@code non-null} {@link OAuth2Token} being returned. * * @author Joe Grandja * @since 0.2.3 @@ -38,11 +38,11 @@ import org.springframework.util.Assert; * @see OAuth2RefreshTokenGenerator */ public final class DelegatingOAuth2TokenGenerator implements OAuth2TokenGenerator { + private final List> tokenGenerators; /** * Constructs a {@code DelegatingOAuth2TokenGenerator} using the provided parameters. - * * @param tokenGenerators an array of {@link OAuth2TokenGenerator}(s) */ @SafeVarargs diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContext.java index 1cdd75e7..6c7dec3c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContext.java @@ -38,6 +38,7 @@ import org.springframework.util.Assert; * @see JwtEncoder#encode(JwtEncoderParameters) */ public final class JwtEncodingContext implements OAuth2TokenContext { + private final Map context; private JwtEncodingContext(Map context) { @@ -58,9 +59,8 @@ public final class JwtEncodingContext implements OAuth2TokenContext { } /** - * Returns the {@link JwsHeader.Builder JWS headers} - * allowing the ability to add, replace, or remove. - * + * Returns the {@link JwsHeader.Builder JWS headers} allowing the ability to add, + * replace, or remove. * @return the {@link JwsHeader.Builder} */ public JwsHeader.Builder getJwsHeader() { @@ -68,9 +68,8 @@ public final class JwtEncodingContext implements OAuth2TokenContext { } /** - * Returns the {@link JwtClaimsSet.Builder claims} - * allowing the ability to add, replace, or remove. - * + * Returns the {@link JwtClaimsSet.Builder claims} allowing the ability to add, + * replace, or remove. * @return the {@link JwtClaimsSet.Builder} */ public JwtClaimsSet.Builder getClaims() { @@ -79,7 +78,6 @@ public final class JwtEncodingContext implements OAuth2TokenContext { /** * Constructs a new {@link Builder} with the provided JWS headers and claims. - * * @param jwsHeaderBuilder the JWS headers to initialize the builder * @param claimsBuilder the claims to initialize the builder * @return the {@link Builder} @@ -102,7 +100,6 @@ public final class JwtEncodingContext implements OAuth2TokenContext { /** * Builds a new {@link JwtEncodingContext}. - * * @return the {@link JwtEncodingContext} */ public JwtEncodingContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java index 84247b54..609d1a4c 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/JwtGenerator.java @@ -45,8 +45,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * An {@link OAuth2TokenGenerator} that generates a {@link Jwt} - * used for an {@link OAuth2AccessToken} or {@link OidcIdToken}. + * An {@link OAuth2TokenGenerator} that generates a {@link Jwt} used for an + * {@link OAuth2AccessToken} or {@link OidcIdToken}. * * @author Joe Grandja * @since 0.2.3 @@ -59,12 +59,13 @@ import org.springframework.util.StringUtils; * @see OidcIdToken */ public final class JwtGenerator implements OAuth2TokenGenerator { + private final JwtEncoder jwtEncoder; + private OAuth2TokenCustomizer jwtCustomizer; /** * Constructs a {@code JwtGenerator} using the provided parameters. - * * @param jwtEncoder the jwt encoder */ public JwtGenerator(JwtEncoder jwtEncoder) { @@ -75,6 +76,7 @@ public final class JwtGenerator implements OAuth2TokenGenerator { @Nullable @Override public Jwt generate(OAuth2TokenContext context) { + // @formatter:off if (context.getTokenType() == null || (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) && !OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue()))) { @@ -84,6 +86,7 @@ public final class JwtGenerator implements OAuth2TokenGenerator { !OAuth2TokenFormat.SELF_CONTAINED.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) { return null; } + // @formatter:on String issuer = null; if (context.getAuthorizationServerContext() != null) { @@ -100,7 +103,8 @@ public final class JwtGenerator implements OAuth2TokenGenerator { if (registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm() != null) { jwsAlgorithm = registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm(); } - } else { + } + else { expiresAt = issuedAt.plus(registeredClient.getTokenSettings().getAccessTokenTimeToLive()); } @@ -187,8 +191,8 @@ public final class JwtGenerator implements OAuth2TokenGenerator { * Sets the {@link OAuth2TokenCustomizer} that customizes the * {@link JwtEncodingContext#getJwsHeader() JWS headers} and/or * {@link JwtEncodingContext#getClaims() claims} for the generated {@link Jwt}. - * - * @param jwtCustomizer the {@link OAuth2TokenCustomizer} that customizes the headers and/or claims for the generated {@code Jwt} + * @param jwtCustomizer the {@link OAuth2TokenCustomizer} that customizes the headers + * and/or claims for the generated {@code Jwt} */ public void setJwtCustomizer(OAuth2TokenCustomizer jwtCustomizer) { Assert.notNull(jwtCustomizer, "jwtCustomizer cannot be null"); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java index 4e203335..159660af 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGenerator.java @@ -36,8 +36,8 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.StringUtils; /** - * An {@link OAuth2TokenGenerator} that generates a - * {@link OAuth2TokenFormat#REFERENCE "reference"} (opaque) {@link OAuth2AccessToken}. + * An {@link OAuth2TokenGenerator} that generates a {@link OAuth2TokenFormat#REFERENCE + * "reference"} (opaque) {@link OAuth2AccessToken}. * * @author Joe Grandja * @since 0.2.3 @@ -48,17 +48,21 @@ import org.springframework.util.StringUtils; * @see OAuth2TokenClaimsSet */ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator { - private final StringKeyGenerator accessTokenGenerator = - new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + + private final StringKeyGenerator accessTokenGenerator = new Base64StringKeyGenerator( + Base64.getUrlEncoder().withoutPadding(), 96); + private OAuth2TokenCustomizer accessTokenCustomizer; @Nullable @Override public OAuth2AccessToken generate(OAuth2TokenContext context) { + // @formatter:off if (!OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType()) || !OAuth2TokenFormat.REFERENCE.equals(context.getRegisteredClient().getTokenSettings().getAccessTokenFormat())) { return null; } + // @formatter:on String issuer = null; if (context.getAuthorizationServerContext() != null) { @@ -110,17 +114,18 @@ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator accessTokenCustomizer) { Assert.notNull(accessTokenCustomizer, "accessTokenCustomizer cannot be null"); @@ -128,10 +133,11 @@ public final class OAuth2AccessTokenGenerator implements OAuth2TokenGenerator claims; - private OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, - Instant issuedAt, Instant expiresAt, Set scopes, Map claims) { + private OAuth2AccessTokenClaims(TokenType tokenType, String tokenValue, Instant issuedAt, Instant expiresAt, + Set scopes, Map claims) { super(tokenType, tokenValue, issuedAt, expiresAt, scopes); this.claims = claims; } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java index b1da8c30..25f99d34 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGenerator.java @@ -36,8 +36,9 @@ import org.springframework.security.oauth2.server.authorization.authentication.O * @see OAuth2RefreshToken */ public final class OAuth2RefreshTokenGenerator implements OAuth2TokenGenerator { - private final StringKeyGenerator refreshTokenGenerator = - new Base64StringKeyGenerator(Base64.getUrlEncoder().withoutPadding(), 96); + + private final StringKeyGenerator refreshTokenGenerator = new Base64StringKeyGenerator( + Base64.getUrlEncoder().withoutPadding(), 96); @Nullable @Override diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimAccessor.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimAccessor.java index 75949cd8..18f6c996 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimAccessor.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimAccessor.java @@ -22,7 +22,8 @@ import java.util.List; import org.springframework.security.oauth2.core.ClaimAccessor; /** - * A {@link ClaimAccessor} for the "claims" that may be contained in an {@link OAuth2TokenClaimsSet}. + * A {@link ClaimAccessor} for the "claims" that may be contained in an + * {@link OAuth2TokenClaimsSet}. * * @author Joe Grandja * @since 0.2.3 @@ -33,8 +34,8 @@ import org.springframework.security.oauth2.core.ClaimAccessor; public interface OAuth2TokenClaimAccessor extends ClaimAccessor { /** - * Returns the Issuer {@code (iss)} claim which identifies the principal that issued the OAuth 2.0 Token. - * + * Returns the Issuer {@code (iss)} claim which identifies the principal that issued + * the OAuth 2.0 Token. * @return the Issuer identifier */ default URL getIssuer() { @@ -42,8 +43,8 @@ public interface OAuth2TokenClaimAccessor extends ClaimAccessor { } /** - * Returns the Subject {@code (sub)} claim which identifies the principal that is the subject of the OAuth 2.0 Token. - * + * Returns the Subject {@code (sub)} claim which identifies the principal that is the + * subject of the OAuth 2.0 Token. * @return the Subject identifier */ default String getSubject() { @@ -51,8 +52,8 @@ public interface OAuth2TokenClaimAccessor extends ClaimAccessor { } /** - * Returns the Audience {@code (aud)} claim which identifies the recipient(s) that the OAuth 2.0 Token is intended for. - * + * Returns the Audience {@code (aud)} claim which identifies the recipient(s) that the + * OAuth 2.0 Token is intended for. * @return the Audience(s) that this OAuth 2.0 Token is intended for */ default List getAudience() { @@ -60,37 +61,38 @@ public interface OAuth2TokenClaimAccessor extends ClaimAccessor { } /** - * Returns the Expiration time {@code (exp)} claim which identifies the expiration time on or after - * which the OAuth 2.0 Token MUST NOT be accepted for processing. - * - * @return the Expiration time on or after which the OAuth 2.0 Token MUST NOT be accepted for processing + * Returns the Expiration time {@code (exp)} claim which identifies the expiration + * time on or after which the OAuth 2.0 Token MUST NOT be accepted for processing. + * @return the Expiration time on or after which the OAuth 2.0 Token MUST NOT be + * accepted for processing */ default Instant getExpiresAt() { return getClaimAsInstant(OAuth2TokenClaimNames.EXP); } /** - * Returns the Not Before {@code (nbf)} claim which identifies the time before - * which the OAuth 2.0 Token MUST NOT be accepted for processing. - * - * @return the Not Before time before which the OAuth 2.0 Token MUST NOT be accepted for processing + * Returns the Not Before {@code (nbf)} claim which identifies the time before which + * the OAuth 2.0 Token MUST NOT be accepted for processing. + * @return the Not Before time before which the OAuth 2.0 Token MUST NOT be accepted + * for processing */ default Instant getNotBefore() { return getClaimAsInstant(OAuth2TokenClaimNames.NBF); } /** - * Returns the Issued at {@code (iat)} claim which identifies the time at which the OAuth 2.0 Token was issued. - * - * @return the Issued at claim which identifies the time at which the OAuth 2.0 Token was issued + * Returns the Issued at {@code (iat)} claim which identifies the time at which the + * OAuth 2.0 Token was issued. + * @return the Issued at claim which identifies the time at which the OAuth 2.0 Token + * was issued */ default Instant getIssuedAt() { return getClaimAsInstant(OAuth2TokenClaimNames.IAT); } /** - * Returns the ID {@code (jti)} claim which provides a unique identifier for the OAuth 2.0 Token. - * + * Returns the ID {@code (jti)} claim which provides a unique identifier for the OAuth + * 2.0 Token. * @return the ID claim which provides a unique identifier for the OAuth 2.0 Token */ default String getId() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java index 63f5efe8..beaf0a42 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimNames.java @@ -18,8 +18,8 @@ package org.springframework.security.oauth2.server.authorization.token; import org.springframework.security.oauth2.core.OAuth2Token; /** - * The names of the "claims" that may be contained in an {@link OAuth2TokenClaimsSet} - * and are associated to an {@link OAuth2Token}. + * The names of the "claims" that may be contained in an {@link OAuth2TokenClaimsSet} and + * are associated to an {@link OAuth2Token}. * * @author Joe Grandja * @since 0.2.3 @@ -30,17 +30,20 @@ import org.springframework.security.oauth2.core.OAuth2Token; public final class OAuth2TokenClaimNames { /** - * {@code iss} - the Issuer claim identifies the principal that issued the OAuth 2.0 Token + * {@code iss} - the Issuer claim identifies the principal that issued the OAuth 2.0 + * Token */ public static final String ISS = "iss"; /** - * {@code sub} - the Subject claim identifies the principal that is the subject of the OAuth 2.0 Token + * {@code sub} - the Subject claim identifies the principal that is the subject of the + * OAuth 2.0 Token */ public static final String SUB = "sub"; /** - * {@code aud} - the Audience claim identifies the recipient(s) that the OAuth 2.0 Token is intended for + * {@code aud} - the Audience claim identifies the recipient(s) that the OAuth 2.0 + * Token is intended for */ public static final String AUD = "aud"; @@ -51,13 +54,14 @@ public final class OAuth2TokenClaimNames { public static final String EXP = "exp"; /** - * {@code nbf} - the Not Before claim identifies the time before which the OAuth 2.0 Token - * MUST NOT be accepted for processing + * {@code nbf} - the Not Before claim identifies the time before which the OAuth 2.0 + * Token MUST NOT be accepted for processing */ public static final String NBF = "nbf"; /** - * {@code iat} - The Issued at claim identifies the time at which the OAuth 2.0 Token was issued + * {@code iat} - The Issued at claim identifies the time at which the OAuth 2.0 Token + * was issued */ public static final String IAT = "iat"; diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContext.java index fadfa4e1..155bacd4 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContext.java @@ -23,8 +23,8 @@ import org.springframework.lang.Nullable; import org.springframework.util.Assert; /** - * An {@link OAuth2TokenContext} implementation that provides access - * to the {@link #getClaims() claims} of an OAuth 2.0 Token, allowing the ability to customize. + * An {@link OAuth2TokenContext} implementation that provides access to the + * {@link #getClaims() claims} of an OAuth 2.0 Token, allowing the ability to customize. * * @author Joe Grandja * @since 0.2.3 @@ -32,6 +32,7 @@ import org.springframework.util.Assert; * @see OAuth2TokenClaimsSet.Builder */ public final class OAuth2TokenClaimsContext implements OAuth2TokenContext { + private final Map context; private OAuth2TokenClaimsContext(Map context) { @@ -52,9 +53,8 @@ public final class OAuth2TokenClaimsContext implements OAuth2TokenContext { } /** - * Returns the {@link OAuth2TokenClaimsSet.Builder claims} - * allowing the ability to add, replace, or remove. - * + * Returns the {@link OAuth2TokenClaimsSet.Builder claims} allowing the ability to + * add, replace, or remove. * @return the {@link OAuth2TokenClaimsSet.Builder} */ public OAuth2TokenClaimsSet.Builder getClaims() { @@ -63,7 +63,6 @@ public final class OAuth2TokenClaimsContext implements OAuth2TokenContext { /** * Constructs a new {@link Builder} with the provided claims. - * * @param claimsBuilder the claims to initialize the builder * @return the {@link Builder} */ @@ -83,7 +82,6 @@ public final class OAuth2TokenClaimsContext implements OAuth2TokenContext { /** * Builds a new {@link OAuth2TokenClaimsContext}. - * * @return the {@link OAuth2TokenClaimsContext} */ public OAuth2TokenClaimsContext build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSet.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSet.java index 35416537..b7e56c82 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSet.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSet.java @@ -37,6 +37,7 @@ import org.springframework.util.Assert; * @see OAuth2Token */ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { + private final Map claims; private OAuth2TokenClaimsSet(Map claims) { @@ -50,7 +51,6 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { /** * Returns a new {@link Builder}. - * * @return the {@link Builder} */ public static Builder builder() { @@ -61,14 +61,15 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { * A builder for {@link OAuth2TokenClaimsSet}. */ public static final class Builder { + private final Map claims = new HashMap<>(); private Builder() { } /** - * Sets the issuer {@code (iss)} claim, which identifies the principal that issued the OAuth 2.0 Token. - * + * Sets the issuer {@code (iss)} claim, which identifies the principal that issued + * the OAuth 2.0 Token. * @param issuer the issuer identifier * @return the {@link Builder} */ @@ -77,8 +78,8 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the subject {@code (sub)} claim, which identifies the principal that is the subject of the OAuth 2.0 Token. - * + * Sets the subject {@code (sub)} claim, which identifies the principal that is + * the subject of the OAuth 2.0 Token. * @param subject the subject identifier * @return the {@link Builder} */ @@ -87,8 +88,8 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the audience {@code (aud)} claim, which identifies the recipient(s) that the OAuth 2.0 Token is intended for. - * + * Sets the audience {@code (aud)} claim, which identifies the recipient(s) that + * the OAuth 2.0 Token is intended for. * @param audience the audience that this OAuth 2.0 Token is intended for * @return the {@link Builder} */ @@ -97,10 +98,10 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the expiration time {@code (exp)} claim, which identifies the time on or after - * which the OAuth 2.0 Token MUST NOT be accepted for processing. - * - * @param expiresAt the time on or after which the OAuth 2.0 Token MUST NOT be accepted for processing + * Sets the expiration time {@code (exp)} claim, which identifies the time on or + * after which the OAuth 2.0 Token MUST NOT be accepted for processing. + * @param expiresAt the time on or after which the OAuth 2.0 Token MUST NOT be + * accepted for processing * @return the {@link Builder} */ public Builder expiresAt(Instant expiresAt) { @@ -108,10 +109,10 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the not before {@code (nbf)} claim, which identifies the time before - * which the OAuth 2.0 Token MUST NOT be accepted for processing. - * - * @param notBefore the time before which the OAuth 2.0 Token MUST NOT be accepted for processing + * Sets the not before {@code (nbf)} claim, which identifies the time before which + * the OAuth 2.0 Token MUST NOT be accepted for processing. + * @param notBefore the time before which the OAuth 2.0 Token MUST NOT be accepted + * for processing * @return the {@link Builder} */ public Builder notBefore(Instant notBefore) { @@ -119,8 +120,8 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the issued at {@code (iat)} claim, which identifies the time at which the OAuth 2.0 Token was issued. - * + * Sets the issued at {@code (iat)} claim, which identifies the time at which the + * OAuth 2.0 Token was issued. * @param issuedAt the time at which the OAuth 2.0 Token was issued * @return the {@link Builder} */ @@ -129,8 +130,8 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * Sets the ID {@code (jti)} claim, which provides a unique identifier for the OAuth 2.0 Token. - * + * Sets the ID {@code (jti)} claim, which provides a unique identifier for the + * OAuth 2.0 Token. * @param jti the unique identifier for the OAuth 2.0 Token * @return the {@link Builder} */ @@ -140,7 +141,6 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { /** * Sets the claim. - * * @param name the claim name * @param value the claim value * @return the {@link Builder} @@ -153,8 +153,8 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { } /** - * A {@code Consumer} to be provided access to the claims allowing the ability to add, replace, or remove. - * + * A {@code Consumer} to be provided access to the claims allowing the ability to + * add, replace, or remove. * @param claimsConsumer a {@code Consumer} of the claims */ public Builder claims(Consumer> claimsConsumer) { @@ -164,7 +164,6 @@ public final class OAuth2TokenClaimsSet implements OAuth2TokenClaimAccessor { /** * Builds a new {@link OAuth2TokenClaimsSet}. - * * @return a {@link OAuth2TokenClaimsSet} */ public OAuth2TokenClaimsSet build() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenContext.java index 99e53c8d..b9233caf 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenContext.java @@ -32,8 +32,8 @@ import org.springframework.security.oauth2.server.authorization.context.Context; import org.springframework.util.Assert; /** - * A context that holds information (to be) associated to an OAuth 2.0 Token - * and is used by an {@link OAuth2TokenGenerator} and {@link OAuth2TokenCustomizer}. + * A context that holds information (to be) associated to an OAuth 2.0 Token and is used + * by an {@link OAuth2TokenGenerator} and {@link OAuth2TokenCustomizer}. * * @author Joe Grandja * @since 0.1.0 @@ -45,7 +45,6 @@ public interface OAuth2TokenContext extends Context { /** * Returns the {@link RegisteredClient registered client}. - * * @return the {@link RegisteredClient} */ default RegisteredClient getRegisteredClient() { @@ -53,10 +52,11 @@ public interface OAuth2TokenContext extends Context { } /** - * Returns the {@link Authentication} representing the {@code Principal} resource owner (or client). - * + * Returns the {@link Authentication} representing the {@code Principal} resource + * owner (or client). * @param the type of the {@code Authentication} - * @return the {@link Authentication} representing the {@code Principal} resource owner (or client) + * @return the {@link Authentication} representing the {@code Principal} resource + * owner (or client) */ default T getPrincipal() { return get(AbstractBuilder.PRINCIPAL_AUTHENTICATION_KEY); @@ -64,7 +64,6 @@ public interface OAuth2TokenContext extends Context { /** * Returns the {@link AuthorizationServerContext authorization server context}. - * * @return the {@link AuthorizationServerContext} * @since 0.2.3 */ @@ -74,7 +73,6 @@ public interface OAuth2TokenContext extends Context { /** * Returns the {@link OAuth2Authorization authorization}. - * * @return the {@link OAuth2Authorization}, or {@code null} if not available */ @Nullable @@ -84,18 +82,15 @@ public interface OAuth2TokenContext extends Context { /** * Returns the authorized scope(s). - * * @return the authorized scope(s) */ default Set getAuthorizedScopes() { - return hasKey(AbstractBuilder.AUTHORIZED_SCOPE_KEY) ? - get(AbstractBuilder.AUTHORIZED_SCOPE_KEY) : - Collections.emptySet(); + return hasKey(AbstractBuilder.AUTHORIZED_SCOPE_KEY) ? get(AbstractBuilder.AUTHORIZED_SCOPE_KEY) + : Collections.emptySet(); } /** * Returns the {@link OAuth2TokenType token type}. - * * @return the {@link OAuth2TokenType} */ default OAuth2TokenType getTokenType() { @@ -104,7 +99,6 @@ public interface OAuth2TokenContext extends Context { /** * Returns the {@link AuthorizationGrantType authorization grant type}. - * * @return the {@link AuthorizationGrantType} */ default AuthorizationGrantType getAuthorizationGrantType() { @@ -113,7 +107,6 @@ public interface OAuth2TokenContext extends Context { /** * Returns the {@link Authentication} representing the authorization grant. - * * @param the type of the {@code Authentication} * @return the {@link Authentication} representing the authorization grant */ @@ -128,17 +121,19 @@ public interface OAuth2TokenContext extends Context { * @param the type of the builder */ abstract class AbstractBuilder> { - private static final String PRINCIPAL_AUTHENTICATION_KEY = - Authentication.class.getName().concat(".PRINCIPAL"); - private static final String AUTHORIZED_SCOPE_KEY = - OAuth2Authorization.class.getName().concat(".AUTHORIZED_SCOPE"); - private static final String AUTHORIZATION_GRANT_AUTHENTICATION_KEY = - Authentication.class.getName().concat(".AUTHORIZATION_GRANT"); + + private static final String PRINCIPAL_AUTHENTICATION_KEY = Authentication.class.getName().concat(".PRINCIPAL"); + + private static final String AUTHORIZED_SCOPE_KEY = OAuth2Authorization.class.getName() + .concat(".AUTHORIZED_SCOPE"); + + private static final String AUTHORIZATION_GRANT_AUTHENTICATION_KEY = Authentication.class.getName() + .concat(".AUTHORIZATION_GRANT"); + private final Map context = new HashMap<>(); /** * Sets the {@link RegisteredClient registered client}. - * * @param registeredClient the {@link RegisteredClient} * @return the {@link AbstractBuilder} for further configuration */ @@ -147,9 +142,10 @@ public interface OAuth2TokenContext extends Context { } /** - * Sets the {@link Authentication} representing the {@code Principal} resource owner (or client). - * - * @param principal the {@link Authentication} representing the {@code Principal} resource owner (or client) + * Sets the {@link Authentication} representing the {@code Principal} resource + * owner (or client). + * @param principal the {@link Authentication} representing the {@code Principal} + * resource owner (or client) * @return the {@link AbstractBuilder} for further configuration */ public B principal(Authentication principal) { @@ -158,7 +154,6 @@ public interface OAuth2TokenContext extends Context { /** * Sets the {@link AuthorizationServerContext authorization server context}. - * * @param authorizationServerContext the {@link AuthorizationServerContext} * @return the {@link AbstractBuilder} for further configuration * @since 0.2.3 @@ -169,7 +164,6 @@ public interface OAuth2TokenContext extends Context { /** * Sets the {@link OAuth2Authorization authorization}. - * * @param authorization the {@link OAuth2Authorization} * @return the {@link AbstractBuilder} for further configuration */ @@ -179,7 +173,6 @@ public interface OAuth2TokenContext extends Context { /** * Sets the authorized scope(s). - * * @param authorizedScopes the authorized scope(s) * @return the {@link AbstractBuilder} for further configuration */ @@ -189,7 +182,6 @@ public interface OAuth2TokenContext extends Context { /** * Sets the {@link OAuth2TokenType token type}. - * * @param tokenType the {@link OAuth2TokenType} * @return the {@link AbstractBuilder} for further configuration */ @@ -199,7 +191,6 @@ public interface OAuth2TokenContext extends Context { /** * Sets the {@link AuthorizationGrantType authorization grant type}. - * * @param authorizationGrantType the {@link AuthorizationGrantType} * @return the {@link AbstractBuilder} for further configuration */ @@ -209,8 +200,8 @@ public interface OAuth2TokenContext extends Context { /** * Sets the {@link Authentication} representing the authorization grant. - * - * @param authorizationGrant the {@link Authentication} representing the authorization grant + * @param authorizationGrant the {@link Authentication} representing the + * authorization grant * @return the {@link AbstractBuilder} for further configuration */ public B authorizationGrant(Authentication authorizationGrant) { @@ -219,7 +210,6 @@ public interface OAuth2TokenContext extends Context { /** * Associates an attribute. - * * @param key the key for the attribute * @param value the value of the attribute * @return the {@link AbstractBuilder} for further configuration @@ -232,9 +222,8 @@ public interface OAuth2TokenContext extends Context { } /** - * A {@code Consumer} of the attributes {@code Map} - * allowing the ability to add, replace, or remove. - * + * A {@code Consumer} of the attributes {@code Map} allowing the ability to add, + * replace, or remove. * @param contextConsumer a {@link Consumer} of the attributes {@code Map} * @return the {@link AbstractBuilder} for further configuration */ @@ -259,7 +248,6 @@ public interface OAuth2TokenContext extends Context { /** * Builds a new {@link OAuth2TokenContext}. - * * @return the {@link OAuth2TokenContext} */ public abstract T build(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java index 23a7b906..c76603d2 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenCustomizer.java @@ -16,8 +16,8 @@ package org.springframework.security.oauth2.server.authorization.token; /** - * Implementations of this interface are responsible for customizing the - * OAuth 2.0 Token attributes contained within the {@link OAuth2TokenContext}. + * Implementations of this interface are responsible for customizing the OAuth 2.0 Token + * attributes contained within the {@link OAuth2TokenContext}. * * @author Joe Grandja * @since 0.1.0 @@ -29,7 +29,6 @@ public interface OAuth2TokenCustomizer { /** * Customize the OAuth 2.0 Token attributes. - * * @param context the context containing the OAuth 2.0 Token attributes */ void customize(T context); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java index c46b7a9e..eda7502e 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenGenerator.java @@ -36,15 +36,17 @@ import org.springframework.security.oauth2.server.authorization.OAuth2Authorizat public interface OAuth2TokenGenerator { /** - * Generate an OAuth 2.0 Token using the attributes contained in the {@link OAuth2TokenContext}, - * or return {@code null} if the {@link OAuth2TokenContext#getTokenType()} is not supported. + * Generate an OAuth 2.0 Token using the attributes contained in the + * {@link OAuth2TokenContext}, or return {@code null} if the + * {@link OAuth2TokenContext#getTokenType()} is not supported. * *

- * If the returned {@link OAuth2Token} has a set of claims, it should implement {@link ClaimAccessor} - * in order for it to be stored with the {@link OAuth2Authorization}. - * + * If the returned {@link OAuth2Token} has a set of claims, it should implement + * {@link ClaimAccessor} in order for it to be stored with the + * {@link OAuth2Authorization}. * @param context the context containing the OAuth 2.0 Token attributes - * @return an {@link OAuth2Token} or {@code null} if the {@link OAuth2TokenContext#getTokenType()} is not supported + * @return an {@link OAuth2Token} or {@code null} if the + * {@link OAuth2TokenContext#getTokenType()} is not supported */ @Nullable T generate(OAuth2TokenContext context); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/util/SpringAuthorizationServerVersion.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/util/SpringAuthorizationServerVersion.java index c32cafda..9945df3d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/util/SpringAuthorizationServerVersion.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/util/SpringAuthorizationServerVersion.java @@ -22,8 +22,11 @@ package org.springframework.security.oauth2.server.authorization.util; * @since 0.0.1 */ public final class SpringAuthorizationServerVersion { + private static final int MAJOR = 1; + private static final int MINOR = 2; + private static final int PATCH = 0; /** @@ -34,4 +37,5 @@ public final class SpringAuthorizationServerVersion { public static String getVersion() { return MAJOR + "." + MINOR + "." + PATCH; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/DefaultConsentPage.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/DefaultConsentPage.java index 620a1ec5..7bb0a515 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/DefaultConsentPage.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/DefaultConsentPage.java @@ -33,6 +33,7 @@ import org.springframework.security.oauth2.core.oidc.OidcScopes; * For internal use only. */ class DefaultConsentPage { + private static final MediaType TEXT_HTML_UTF8 = new MediaType("text", "html", StandardCharsets.UTF_8); private DefaultConsentPage() { @@ -42,21 +43,24 @@ class DefaultConsentPage { Authentication principal, Set requestedScopes, Set authorizedScopes, String state, Map additionalParameters) throws IOException { - String consentPage = generateConsentPage(request, clientId, principal, requestedScopes, authorizedScopes, state, additionalParameters); + String consentPage = generateConsentPage(request, clientId, principal, requestedScopes, authorizedScopes, state, + additionalParameters); response.setContentType(TEXT_HTML_UTF8.toString()); response.setContentLength(consentPage.getBytes(StandardCharsets.UTF_8).length); response.getWriter().write(consentPage); } - private static String generateConsentPage(HttpServletRequest request, - String clientId, Authentication principal, Set requestedScopes, Set authorizedScopes, String state, + private static String generateConsentPage(HttpServletRequest request, String clientId, Authentication principal, + Set requestedScopes, Set authorizedScopes, String state, Map additionalParameters) { Set scopesToAuthorize = new HashSet<>(); Set scopesPreviouslyAuthorized = new HashSet<>(); for (String scope : requestedScopes) { if (authorizedScopes.contains(scope)) { scopesPreviouslyAuthorized.add(scope); - } else if (!scope.equals(OidcScopes.OPENID)) { // openid scope does not require consent + } + else if (!scope.equals(OidcScopes.OPENID)) { + // openid scope does not require consent scopesToAuthorize.add(scope); } } @@ -68,8 +72,8 @@ class DefaultConsentPage { // authorizing the correct device. String userCode = additionalParameters.get(OAuth2ParameterNames.USER_CODE); + // @formatter:off StringBuilder builder = new StringBuilder(); - builder.append(""); builder.append(""); builder.append(""); @@ -149,7 +153,9 @@ class DefaultConsentPage { builder.append(""); builder.append(""); builder.append(""); + // @formatter:on return builder.toString(); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilter.java index 350067ea..9cbf87ba 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilter.java @@ -42,17 +42,22 @@ import org.springframework.web.filter.OncePerRequestFilter; * @author Joe Grandja * @since 0.0.1 * @see com.nimbusds.jose.jwk.source.JWKSource - * @see JSON Web Key (JWK) - * @see Section 5 JWK Set Format + * @see JSON Web Key + * (JWK) + * @see Section 5 + * JWK Set Format */ public final class NimbusJwkSetEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for JWK Set requests. */ private static final String DEFAULT_JWK_SET_ENDPOINT_URI = "/oauth2/jwks"; private final JWKSource jwkSource; + private final JWKSelector jwkSelector; + private final RequestMatcher requestMatcher; /** @@ -65,7 +70,6 @@ public final class NimbusJwkSetEndpointFilter extends OncePerRequestFilter { /** * Constructs a {@code NimbusJwkSetEndpointFilter} using the provided parameters. - * * @param jwkSource the {@code com.nimbusds.jose.jwk.source.JWKSource} * @param jwkSetEndpointUri the endpoint {@code URI} for JWK Set requests */ @@ -96,7 +100,8 @@ public final class NimbusJwkSetEndpointFilter extends OncePerRequestFilter { response.setContentType(MediaType.APPLICATION_JSON_VALUE); try (Writer writer = response.getWriter()) { - writer.write(jwkSet.toString()); // toString() excludes private keys + writer.write(jwkSet.toString()); // toString() excludes private keys } } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java index cacb855c..76bb5d0a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilter.java @@ -69,8 +69,8 @@ import org.springframework.web.util.UriComponentsBuilder; import org.springframework.web.util.UriUtils; /** - * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, - * which handles the processing of the OAuth 2.0 Authorization Request and Consent. + * A {@code Filter} for the OAuth 2.0 Authorization Code Grant, which handles the + * processing of the OAuth 2.0 Authorization Request and Consent. * * @author Joe Grandja * @author Paurav Munshi @@ -81,29 +81,45 @@ import org.springframework.web.util.UriUtils; * @see AuthenticationManager * @see OAuth2AuthorizationCodeRequestAuthenticationProvider * @see OAuth2AuthorizationConsentAuthenticationProvider - * @see Section 4.1 Authorization Code Grant - * @see Section 4.1.1 Authorization Request - * @see Section 4.1.2 Authorization Response + * @see Section 4.1 Authorization + * Code Grant + * @see Section 4.1.1 + * Authorization Request + * @see Section 4.1.2 + * Authorization Response */ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for authorization requests. */ private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize"; private final AuthenticationManager authenticationManager; + private final RequestMatcher authorizationEndpointMatcher; + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAuthorizationResponse; + private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; - private SessionAuthenticationStrategy sessionAuthenticationStrategy = (authentication, request, response) -> {}; + + private SessionAuthenticationStrategy sessionAuthenticationStrategy = (authentication, request, response) -> { + }; + private String consentPage; /** - * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OAuth2AuthorizationEndpointFilter(AuthenticationManager authenticationManager) { @@ -111,40 +127,42 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2AuthorizationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager * @param authorizationEndpointUri the endpoint {@code URI} for authorization requests */ - public OAuth2AuthorizationEndpointFilter(AuthenticationManager authenticationManager, String authorizationEndpointUri) { + public OAuth2AuthorizationEndpointFilter(AuthenticationManager authenticationManager, + String authorizationEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(authorizationEndpointUri, "authorizationEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; this.authorizationEndpointMatcher = createDefaultRequestMatcher(authorizationEndpointUri); + // @formatter:off this.authenticationConverter = new DelegatingAuthenticationConverter( Arrays.asList( new OAuth2AuthorizationCodeRequestAuthenticationConverter(), new OAuth2AuthorizationConsentAuthenticationConverter())); + // @formatter:on } private static RequestMatcher createDefaultRequestMatcher(String authorizationEndpointUri) { - RequestMatcher authorizationRequestGetMatcher = new AntPathRequestMatcher( - authorizationEndpointUri, HttpMethod.GET.name()); - RequestMatcher authorizationRequestPostMatcher = new AntPathRequestMatcher( - authorizationEndpointUri, HttpMethod.POST.name()); + RequestMatcher authorizationRequestGetMatcher = new AntPathRequestMatcher(authorizationEndpointUri, + HttpMethod.GET.name()); + RequestMatcher authorizationRequestPostMatcher = new AntPathRequestMatcher(authorizationEndpointUri, + HttpMethod.POST.name()); RequestMatcher openidScopeMatcher = request -> { String scope = request.getParameter(OAuth2ParameterNames.SCOPE); return StringUtils.hasText(scope) && scope.contains(OidcScopes.OPENID); }; - RequestMatcher responseTypeParameterMatcher = request -> - request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; + RequestMatcher responseTypeParameterMatcher = request -> request + .getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; - RequestMatcher authorizationRequestMatcher = new OrRequestMatcher( - authorizationRequestGetMatcher, - new AndRequestMatcher( - authorizationRequestPostMatcher, responseTypeParameterMatcher, openidScopeMatcher)); - RequestMatcher authorizationConsentMatcher = new AndRequestMatcher( - authorizationRequestPostMatcher, new NegatedRequestMatcher(responseTypeParameterMatcher)); + RequestMatcher authorizationRequestMatcher = new OrRequestMatcher(authorizationRequestGetMatcher, + new AndRequestMatcher(authorizationRequestPostMatcher, responseTypeParameterMatcher, + openidScopeMatcher)); + RequestMatcher authorizationConsentMatcher = new AndRequestMatcher(authorizationRequestPostMatcher, + new NegatedRequestMatcher(responseTypeParameterMatcher)); return new OrRequestMatcher(authorizationRequestMatcher, authorizationConsentMatcher); } @@ -162,14 +180,15 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte Authentication authentication = this.authenticationConverter.convert(request); if (authentication instanceof AbstractAuthenticationToken) { ((AbstractAuthenticationToken) authentication) - .setDetails(this.authenticationDetailsSource.buildDetails(request)); + .setDetails(this.authenticationDetailsSource.buildDetails(request)); } Authentication authenticationResult = this.authenticationManager.authenticate(authentication); if (!authenticationResult.isAuthenticated()) { - // If the Principal (Resource Owner) is not authenticated then - // pass through the chain with the expectation that the authentication process - // will commence via AuthenticationEntryPoint + // If the Principal (Resource Owner) is not authenticated then pass + // through the chain + // with the expectation that the authentication process will commence via + // AuthenticationEntryPoint filterChain.doFilter(request, response); return; } @@ -184,13 +203,12 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte return; } - this.sessionAuthenticationStrategy.onAuthentication( - authenticationResult, request, response); + this.sessionAuthenticationStrategy.onAuthentication(authenticationResult, request, response); - this.authenticationSuccessHandler.onAuthenticationSuccess( - request, response, authenticationResult); + this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Authorization request failed: %s", ex.getError()), ex); } @@ -199,22 +217,27 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}. - * - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest} + * Sets the {@link AuthenticationDetailsSource} used for building an authentication + * details instance from {@link HttpServletRequest}. + * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for + * building an authentication details instance from {@link HttpServletRequest} * @since 0.3.1 */ - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); this.authenticationDetailsSource = authenticationDetailsSource; } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or {@link OAuth2AuthorizationConsentAuthenticationToken} - * used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an Authorization Request (or Consent) from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract an + * Authorization Request (or Consent) from {@link HttpServletRequest} to an instance + * of {@link OAuth2AuthorizationCodeRequestAuthenticationToken} or + * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the + * request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract an Authorization Request (or Consent) from + * {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -222,10 +245,11 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * and returning the {@link OAuth2AuthorizationResponse Authorization Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} and returning the + * {@link OAuth2AuthorizationResponse Authorization Response}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -233,10 +257,11 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthorizationCodeRequestAuthenticationException} and returning the + * {@link OAuth2Error Error Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationException} */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); @@ -244,11 +269,12 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Sets the {@link SessionAuthenticationStrategy} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} - * before calling the {@link AuthenticationSuccessHandler}. - * If OpenID Connect is enabled, the default implementation tracks OpenID Connect sessions using a {@link SessionRegistry}. - * - * @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} used for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} + * Sets the {@link SessionAuthenticationStrategy} used for handling an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} before calling the + * {@link AuthenticationSuccessHandler}. If OpenID Connect is enabled, the default + * implementation tracks OpenID Connect sessions using a {@link SessionRegistry}. + * @param sessionAuthenticationStrategy the {@link SessionAuthenticationStrategy} used + * for handling an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} * @since 1.1 */ public void setSessionAuthenticationStrategy(SessionAuthenticationStrategy sessionAuthenticationStrategy) { @@ -257,10 +283,10 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } /** - * Specify the URI to redirect Resource Owners to if consent is required. A default consent - * page will be generated when this attribute is not specified. - * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") + * Specify the URI to redirect Resource Owners to if consent is required. A default + * consent page will be generated when this attribute is not specified. + * @param consentPage the URI of the custom consent page to redirect to if consent is + * required (e.g. "/oauth2/consent") */ public void setConsentPage(String consentPage) { this.consentPage = consentPage; @@ -278,16 +304,18 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte if (hasConsentUri()) { String redirectUri = UriComponentsBuilder.fromUriString(resolveConsentUri(request)) - .queryParam(OAuth2ParameterNames.SCOPE, String.join(" ", requestedScopes)) - .queryParam(OAuth2ParameterNames.CLIENT_ID, clientId) - .queryParam(OAuth2ParameterNames.STATE, state) - .toUriString(); + .queryParam(OAuth2ParameterNames.SCOPE, String.join(" ", requestedScopes)) + .queryParam(OAuth2ParameterNames.CLIENT_ID, clientId) + .queryParam(OAuth2ParameterNames.STATE, state) + .toUriString(); this.redirectStrategy.sendRedirect(request, response, redirectUri); - } else { + } + else { if (this.logger.isTraceEnabled()) { this.logger.trace("Displaying generated consent screen"); } - DefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes, state, Collections.emptyMap()); + DefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes, + state, Collections.emptyMap()); } } @@ -311,31 +339,30 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte private void sendAuthorizationResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication; + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = (OAuth2AuthorizationCodeRequestAuthenticationToken) authentication; UriComponentsBuilder uriBuilder = UriComponentsBuilder - .fromUriString(authorizationCodeRequestAuthentication.getRedirectUri()) - .queryParam(OAuth2ParameterNames.CODE, authorizationCodeRequestAuthentication.getAuthorizationCode().getTokenValue()); + .fromUriString(authorizationCodeRequestAuthentication.getRedirectUri()) + .queryParam(OAuth2ParameterNames.CODE, + authorizationCodeRequestAuthentication.getAuthorizationCode().getTokenValue()); if (StringUtils.hasText(authorizationCodeRequestAuthentication.getState())) { - uriBuilder.queryParam( - OAuth2ParameterNames.STATE, + uriBuilder.queryParam(OAuth2ParameterNames.STATE, UriUtils.encode(authorizationCodeRequestAuthentication.getState(), StandardCharsets.UTF_8)); } - String redirectUri = uriBuilder.build(true).toUriString(); // build(true) -> Components are explicitly encoded + // build(true) -> Components are explicitly encoded + String redirectUri = uriBuilder.build(true).toUriString(); this.redirectStrategy.sendRedirect(request, response, redirectUri); } private void sendErrorResponse(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException { - OAuth2AuthorizationCodeRequestAuthenticationException authorizationCodeRequestAuthenticationException = - (OAuth2AuthorizationCodeRequestAuthenticationException) exception; + OAuth2AuthorizationCodeRequestAuthenticationException authorizationCodeRequestAuthenticationException = (OAuth2AuthorizationCodeRequestAuthenticationException) exception; OAuth2Error error = authorizationCodeRequestAuthenticationException.getError(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authorizationCodeRequestAuthenticationException.getAuthorizationCodeRequestAuthentication(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authorizationCodeRequestAuthenticationException + .getAuthorizationCodeRequestAuthentication(); - if (authorizationCodeRequestAuthentication == null || - !StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) { + if (authorizationCodeRequestAuthentication == null + || !StringUtils.hasText(authorizationCodeRequestAuthentication.getRedirectUri())) { response.sendError(HttpStatus.BAD_REQUEST.value(), error.toString()); return; } @@ -345,24 +372,22 @@ public final class OAuth2AuthorizationEndpointFilter extends OncePerRequestFilte } UriComponentsBuilder uriBuilder = UriComponentsBuilder - .fromUriString(authorizationCodeRequestAuthentication.getRedirectUri()) - .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode()); + .fromUriString(authorizationCodeRequestAuthentication.getRedirectUri()) + .queryParam(OAuth2ParameterNames.ERROR, error.getErrorCode()); if (StringUtils.hasText(error.getDescription())) { - uriBuilder.queryParam( - OAuth2ParameterNames.ERROR_DESCRIPTION, + uriBuilder.queryParam(OAuth2ParameterNames.ERROR_DESCRIPTION, UriUtils.encode(error.getDescription(), StandardCharsets.UTF_8)); } if (StringUtils.hasText(error.getUri())) { - uriBuilder.queryParam( - OAuth2ParameterNames.ERROR_URI, + uriBuilder.queryParam(OAuth2ParameterNames.ERROR_URI, UriUtils.encode(error.getUri(), StandardCharsets.UTF_8)); } if (StringUtils.hasText(authorizationCodeRequestAuthentication.getState())) { - uriBuilder.queryParam( - OAuth2ParameterNames.STATE, + uriBuilder.queryParam(OAuth2ParameterNames.STATE, UriUtils.encode(authorizationCodeRequestAuthentication.getState(), StandardCharsets.UTF_8)); } - String redirectUri = uriBuilder.build(true).toUriString(); // build(true) -> Components are explicitly encoded + // build(true) -> Components are explicitly encoded + String redirectUri = uriBuilder.build(true).toUriString(); this.redirectStrategy.sendRedirect(request, response, redirectUri); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java index c561260c..e8ac47f7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilter.java @@ -49,29 +49,36 @@ import org.springframework.web.util.UriComponentsBuilder; * @since 0.1.1 * @see OAuth2AuthorizationServerMetadata * @see AuthorizationServerSettings - * @see 3. Obtaining Authorization Server Metadata + * @see 3. + * Obtaining Authorization Server Metadata */ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OncePerRequestFilter { + /** - * The default endpoint {@code URI} for OAuth 2.0 Authorization Server Metadata requests. + * The default endpoint {@code URI} for OAuth 2.0 Authorization Server Metadata + * requests. */ private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server"; private final RequestMatcher requestMatcher = new AntPathRequestMatcher( - DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI, - HttpMethod.GET.name()); - private final OAuth2AuthorizationServerMetadataHttpMessageConverter authorizationServerMetadataHttpMessageConverter = - new OAuth2AuthorizationServerMetadataHttpMessageConverter(); - private Consumer authorizationServerMetadataCustomizer = (authorizationServerMetadata) -> {}; + DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI, HttpMethod.GET.name()); + + private final OAuth2AuthorizationServerMetadataHttpMessageConverter authorizationServerMetadataHttpMessageConverter = new OAuth2AuthorizationServerMetadataHttpMessageConverter(); + + private Consumer authorizationServerMetadataCustomizer = ( + authorizationServerMetadata) -> { + }; /** - * Sets the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder} - * allowing the ability to customize the claims of the Authorization Server's configuration. - * - * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access to the {@link OAuth2AuthorizationServerMetadata.Builder} + * Sets the {@code Consumer} providing access to the + * {@link OAuth2AuthorizationServerMetadata.Builder} allowing the ability to customize + * the claims of the Authorization Server's configuration. + * @param authorizationServerMetadataCustomizer the {@code Consumer} providing access + * to the {@link OAuth2AuthorizationServerMetadata.Builder} * @since 0.4.0 */ - public void setAuthorizationServerMetadataCustomizer(Consumer authorizationServerMetadataCustomizer) { + public void setAuthorizationServerMetadataCustomizer( + Consumer authorizationServerMetadataCustomizer) { Assert.notNull(authorizationServerMetadataCustomizer, "authorizationServerMetadataCustomizer cannot be null"); this.authorizationServerMetadataCustomizer = authorizationServerMetadataCustomizer; } @@ -87,31 +94,33 @@ public final class OAuth2AuthorizationServerMetadataEndpointFilter extends OnceP AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); String issuer = authorizationServerContext.getIssuer(); - AuthorizationServerSettings authorizationServerSettings = authorizationServerContext.getAuthorizationServerSettings(); + AuthorizationServerSettings authorizationServerSettings = authorizationServerContext + .getAuthorizationServerSettings(); - OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() - .issuer(issuer) - .authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint())) - .deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint())) - .tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint())) - .tokenEndpointAuthenticationMethods(clientAuthenticationMethods()) - .jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint())) - .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) - .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) - .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue()) - .grantType(AuthorizationGrantType.DEVICE_CODE.getValue()) - .tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint())) - .tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods()) - .tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint())) - .tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods()) - .codeChallengeMethod("S256"); + OAuth2AuthorizationServerMetadata.Builder authorizationServerMetadata = OAuth2AuthorizationServerMetadata + .builder() + .issuer(issuer) + .authorizationEndpoint(asUrl(issuer, authorizationServerSettings.getAuthorizationEndpoint())) + .deviceAuthorizationEndpoint(asUrl(issuer, authorizationServerSettings.getDeviceAuthorizationEndpoint())) + .tokenEndpoint(asUrl(issuer, authorizationServerSettings.getTokenEndpoint())) + .tokenEndpointAuthenticationMethods(clientAuthenticationMethods()) + .jwkSetUrl(asUrl(issuer, authorizationServerSettings.getJwkSetEndpoint())) + .responseType(OAuth2AuthorizationResponseType.CODE.getValue()) + .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) + .grantType(AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) + .grantType(AuthorizationGrantType.REFRESH_TOKEN.getValue()) + .grantType(AuthorizationGrantType.DEVICE_CODE.getValue()) + .tokenRevocationEndpoint(asUrl(issuer, authorizationServerSettings.getTokenRevocationEndpoint())) + .tokenRevocationEndpointAuthenticationMethods(clientAuthenticationMethods()) + .tokenIntrospectionEndpoint(asUrl(issuer, authorizationServerSettings.getTokenIntrospectionEndpoint())) + .tokenIntrospectionEndpointAuthenticationMethods(clientAuthenticationMethods()) + .codeChallengeMethod("S256"); this.authorizationServerMetadataCustomizer.accept(authorizationServerMetadata); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); - this.authorizationServerMetadataHttpMessageConverter.write( - authorizationServerMetadata.build(), MediaType.APPLICATION_JSON, httpResponse); + this.authorizationServerMetadataHttpMessageConverter.write(authorizationServerMetadata.build(), + MediaType.APPLICATION_JSON, httpResponse); } private static Consumer> clientAuthenticationMethods() { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilter.java index 6926314d..d88a119b 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilter.java @@ -69,24 +69,36 @@ import org.springframework.web.filter.OncePerRequestFilter; * @see ClientSecretAuthenticationProvider * @see PublicClientAuthenticationConverter * @see PublicClientAuthenticationProvider - * @see Section 2.3 Client Authentication - * @see Section 3.2.1 Token Endpoint Client Authentication + * @see Section 2.3 Client + * Authentication + * @see Section 3.2.1 Token + * Endpoint Client Authentication */ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter { + private final AuthenticationManager authenticationManager; + private final RequestMatcher requestMatcher; + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); - private final AuthenticationDetailsSource authenticationDetailsSource = - new WebAuthenticationDetailsSource(); + + private final AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::onAuthenticationSuccess; + private AuthenticationFailureHandler authenticationFailureHandler = this::onAuthenticationFailure; /** - * Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided parameters. - * - * @param authenticationManager the {@link AuthenticationManager} used for authenticating the client - * @param requestMatcher the {@link RequestMatcher} used for matching against the {@code HttpServletRequest} + * Constructs an {@code OAuth2ClientAuthenticationFilter} using the provided + * parameters. + * @param authenticationManager the {@link AuthenticationManager} used for + * authenticating the client + * @param requestMatcher the {@link RequestMatcher} used for matching against the + * {@code HttpServletRequest} */ public OAuth2ClientAuthenticationFilter(AuthenticationManager authenticationManager, RequestMatcher requestMatcher) { @@ -94,12 +106,14 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter Assert.notNull(requestMatcher, "requestMatcher cannot be null"); this.authenticationManager = authenticationManager; this.requestMatcher = requestMatcher; + // @formatter:off this.authenticationConverter = new DelegatingAuthenticationConverter( Arrays.asList( new JwtClientAssertionAuthenticationConverter(), new ClientSecretBasicAuthenticationConverter(), new ClientSecretPostAuthenticationConverter(), new PublicClientAuthenticationConverter())); + // @formatter:on } @Override @@ -114,8 +128,8 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter try { Authentication authenticationRequest = this.authenticationConverter.convert(request); if (authenticationRequest instanceof AbstractAuthenticationToken) { - ((AbstractAuthenticationToken) authenticationRequest).setDetails( - this.authenticationDetailsSource.buildDetails(request)); + ((AbstractAuthenticationToken) authenticationRequest) + .setDetails(this.authenticationDetailsSource.buildDetails(request)); } if (authenticationRequest != null) { validateClientIdentifier(authenticationRequest); @@ -124,7 +138,8 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter } filterChain.doFilter(request, response); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Client authentication failed: %s", ex.getError()), ex); } @@ -133,10 +148,11 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} - * to an instance of {@link OAuth2ClientAuthenticationToken} used for authenticating the client. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract client credentials from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract client + * credentials from {@link HttpServletRequest} to an instance of + * {@link OAuth2ClientAuthenticationToken} used for authenticating the client. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract client credentials from {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -144,10 +160,11 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client authentication - * and associating the {@link OAuth2ClientAuthenticationToken} to the {@link SecurityContext}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling a successful client authentication + * Sets the {@link AuthenticationSuccessHandler} used for handling a successful client + * authentication and associating the {@link OAuth2ClientAuthenticationToken} to the + * {@link SecurityContext}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling a successful client authentication */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -155,10 +172,10 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter } /** - * Sets the {@link AuthenticationFailureHandler} used for handling a failed client authentication - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling a failed client authentication + * Sets the {@link AuthenticationFailureHandler} used for handling a failed client + * authentication and returning the {@link OAuth2Error Error Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling a failed client authentication */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); @@ -185,8 +202,10 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter // TODO // The authorization server MAY return an HTTP 401 (Unauthorized) status code // to indicate which HTTP authentication schemes are supported. - // If the client attempted to authenticate via the "Authorization" request header field, - // the authorization server MUST respond with an HTTP 401 (Unauthorized) status code and + // If the client attempted to authenticate via the "Authorization" request header + // field, + // the authorization server MUST respond with an HTTP 401 (Unauthorized) status + // code and // include the "WWW-Authenticate" response header field // matching the authentication scheme used by the client. @@ -194,10 +213,12 @@ public final class OAuth2ClientAuthenticationFilter extends OncePerRequestFilter ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); if (OAuth2ErrorCodes.INVALID_CLIENT.equals(error.getErrorCode())) { httpResponse.setStatusCode(HttpStatus.UNAUTHORIZED); - } else { + } + else { httpResponse.setStatusCode(HttpStatus.BAD_REQUEST); } - // We don't want to reveal too much information to the caller so just return the error code + // We don't want to reveal too much information to the caller so just return the + // error code OAuth2Error errorResponse = new OAuth2Error(error.getErrorCode()); this.errorHttpResponseConverter.write(errorResponse, null, httpResponse); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java index b6b6db2f..46d94335 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilter.java @@ -54,36 +54,46 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; /** - * A {@code Filter} for the OAuth 2.0 Device Authorization endpoint, - * which handles the processing of the OAuth 2.0 Device Authorization Request. + * A {@code Filter} for the OAuth 2.0 Device Authorization endpoint, which handles the + * processing of the OAuth 2.0 Device Authorization Request. * * @author Steve Riesenberg * @since 1.1 * @see AuthenticationManager * @see OAuth2DeviceAuthorizationRequestAuthenticationConverter * @see OAuth2DeviceAuthorizationRequestAuthenticationProvider - * @see OAuth 2.0 Device Authorization Grant - * @see Section 3.1 Device Authorization Request - * @see Section 3.2 Device Authorization Response + * @see OAuth 2.0 + * Device Authorization Grant + * @see Section 3.1 Device + * Authorization Request + * @see Section 3.2 Device + * Authorization Response */ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerRequestFilter { private static final String DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI = "/oauth2/device_authorization"; private final AuthenticationManager authenticationManager; + private final RequestMatcher deviceAuthorizationEndpointMatcher; - private final HttpMessageConverter deviceAuthorizationHttpResponseConverter = - new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); - private AuthenticationDetailsSource authenticationDetailsSource = - new WebAuthenticationDetailsSource(); + + private final HttpMessageConverter deviceAuthorizationHttpResponseConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); + + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendDeviceAuthorizationResponse; + private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler(); + private String verificationUri = OAuth2DeviceVerificationEndpointFilter.DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI; /** - * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OAuth2DeviceAuthorizationEndpointFilter(AuthenticationManager authenticationManager) { @@ -91,12 +101,14 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques } /** - * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceAuthorizationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager - * @param deviceAuthorizationEndpointUri the endpoint {@code URI} for device authorization requests + * @param deviceAuthorizationEndpointUri the endpoint {@code URI} for device + * authorization requests */ - public OAuth2DeviceAuthorizationEndpointFilter(AuthenticationManager authenticationManager, String deviceAuthorizationEndpointUri) { + public OAuth2DeviceAuthorizationEndpointFilter(AuthenticationManager authenticationManager, + String deviceAuthorizationEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(deviceAuthorizationEndpointUri, "deviceAuthorizationEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; @@ -118,15 +130,16 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques Authentication deviceAuthorizationRequestAuthentication = this.authenticationConverter.convert(request); if (deviceAuthorizationRequestAuthentication instanceof AbstractAuthenticationToken) { ((AbstractAuthenticationToken) deviceAuthorizationRequestAuthentication) - .setDetails(this.authenticationDetailsSource.buildDetails(request)); + .setDetails(this.authenticationDetailsSource.buildDetails(request)); } - Authentication deviceAuthorizationRequestAuthenticationResult = - this.authenticationManager.authenticate(deviceAuthorizationRequestAuthentication); + Authentication deviceAuthorizationRequestAuthenticationResult = this.authenticationManager + .authenticate(deviceAuthorizationRequestAuthentication); this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, deviceAuthorizationRequestAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { SecurityContextHolder.clearContext(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Device authorization request failed: %s", ex.getError()), ex); @@ -136,20 +149,25 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques } /** - * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}. - * - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest} + * Sets the {@link AuthenticationDetailsSource} used for building an authentication + * details instance from {@link HttpServletRequest}. + * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for + * building an authentication details instance from {@link HttpServletRequest} */ - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); this.authenticationDetailsSource = authenticationDetailsSource; } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Device Authorization Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract a Device + * Authorization Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating + * the request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract a Device Authorization Request from + * {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -157,10 +175,11 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} - * and returning the {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} and returning the + * {@link OAuth2DeviceAuthorizationResponse Device Authorization Response}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -168,10 +187,11 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); @@ -180,9 +200,11 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques /** * Sets the end-user verification {@code URI} on the authorization server. - * - * @param verificationUri the end-user verification {@code URI} on the authorization server - * @see Section 3.2 Device Authorization Response + * @param verificationUri the end-user verification {@code URI} on the authorization + * server + * @see Section 3.2 Device + * Authorization Response */ public void setVerificationUri(String verificationUri) { Assert.hasText(verificationUri, "verificationUri cannot be empty"); @@ -192,8 +214,7 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques private void sendDeviceAuthorizationResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication; + OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) authentication; OAuth2DeviceCode deviceCode = deviceAuthorizationRequestAuthentication.getDeviceCode(); OAuth2UserCode userCode = deviceAuthorizationRequestAuthentication.getUserCode(); @@ -201,7 +222,7 @@ public final class OAuth2DeviceAuthorizationEndpointFilter extends OncePerReques // Generate the fully-qualified verification URI String issuerUri = AuthorizationServerContextHolder.getContext().getIssuer(); UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(issuerUri) - .path(this.verificationUri); + .path(this.verificationUri); String verificationUri = uriComponentsBuilder.build().toUriString(); // @formatter:off String verificationUriComplete = uriComponentsBuilder diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java index 5e0e5dc1..ca700d45 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilter.java @@ -63,9 +63,9 @@ import org.springframework.web.filter.OncePerRequestFilter; import org.springframework.web.util.UriComponentsBuilder; /** - * A {@code Filter} for the OAuth 2.0 Device Authorization Grant, - * which handles the processing of the Device Verification Request (submission of the user code) - * and the Device Authorization Consent. + * A {@code Filter} for the OAuth 2.0 Device Authorization Grant, which handles the + * processing of the Device Verification Request (submission of the user code) and the + * Device Authorization Consent. * * @author Steve Riesenberg * @since 1.1 @@ -74,27 +74,36 @@ import org.springframework.web.util.UriComponentsBuilder; * @see OAuth2DeviceVerificationAuthenticationProvider * @see OAuth2DeviceAuthorizationConsentAuthenticationConverter * @see OAuth2DeviceAuthorizationConsentAuthenticationProvider - * @see OAuth 2.0 Device Authorization Grant - * @see Section 3.3 User Interaction + * @see OAuth 2.0 + * Device Authorization Grant + * @see Section 3.3 User + * Interaction */ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequestFilter { static final String DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI = "/oauth2/device_verification"; private final AuthenticationManager authenticationManager; + private final RequestMatcher deviceVerificationEndpointMatcher; + private final RedirectStrategy redirectStrategy = new DefaultRedirectStrategy(); - private AuthenticationDetailsSource authenticationDetailsSource = - new WebAuthenticationDetailsSource(); + + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private AuthenticationConverter authenticationConverter; - private AuthenticationSuccessHandler authenticationSuccessHandler = - new SimpleUrlAuthenticationSuccessHandler("/?success"); + + private AuthenticationSuccessHandler authenticationSuccessHandler = new SimpleUrlAuthenticationSuccessHandler( + "/?success"); + private AuthenticationFailureHandler authenticationFailureHandler = this::sendErrorResponse; + private String consentPage; /** - * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager) { @@ -102,29 +111,33 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } /** - * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2DeviceVerificationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager - * @param deviceVerificationEndpointUri the endpoint {@code URI} for device verification requests + * @param deviceVerificationEndpointUri the endpoint {@code URI} for device + * verification requests */ - public OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager, String deviceVerificationEndpointUri) { + public OAuth2DeviceVerificationEndpointFilter(AuthenticationManager authenticationManager, + String deviceVerificationEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(deviceVerificationEndpointUri, "deviceVerificationEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; this.deviceVerificationEndpointMatcher = createDefaultRequestMatcher(deviceVerificationEndpointUri); + // @formatter:off this.authenticationConverter = new DelegatingAuthenticationConverter( Arrays.asList( new OAuth2DeviceVerificationAuthenticationConverter(), new OAuth2DeviceAuthorizationConsentAuthenticationConverter())); + // @formatter:on } private RequestMatcher createDefaultRequestMatcher(String deviceVerificationEndpointUri) { - RequestMatcher verificationRequestGetMatcher = new AntPathRequestMatcher( - deviceVerificationEndpointUri, HttpMethod.GET.name()); - RequestMatcher verificationRequestPostMatcher = new AntPathRequestMatcher( - deviceVerificationEndpointUri, HttpMethod.POST.name()); - RequestMatcher userCodeParameterMatcher = request -> - request.getParameter(OAuth2ParameterNames.USER_CODE) != null; + RequestMatcher verificationRequestGetMatcher = new AntPathRequestMatcher(deviceVerificationEndpointUri, + HttpMethod.GET.name()); + RequestMatcher verificationRequestPostMatcher = new AntPathRequestMatcher(deviceVerificationEndpointUri, + HttpMethod.POST.name()); + RequestMatcher userCodeParameterMatcher = request -> request + .getParameter(OAuth2ParameterNames.USER_CODE) != null; return new AndRequestMatcher( new OrRequestMatcher(verificationRequestGetMatcher, verificationRequestPostMatcher), @@ -144,14 +157,15 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest Authentication authentication = this.authenticationConverter.convert(request); if (authentication instanceof AbstractAuthenticationToken) { ((AbstractAuthenticationToken) authentication) - .setDetails(this.authenticationDetailsSource.buildDetails(request)); + .setDetails(this.authenticationDetailsSource.buildDetails(request)); } Authentication authenticationResult = this.authenticationManager.authenticate(authentication); if (!authenticationResult.isAuthenticated()) { - // If the Principal (Resource Owner) is not authenticated then - // pass through the chain with the expectation that the authentication process - // will commence via AuthenticationEntryPoint + // If the Principal (Resource Owner) is not authenticated then pass + // through the chain + // with the expectation that the authentication process will commence via + // AuthenticationEntryPoint filterChain.doFilter(request, response); return; } @@ -165,7 +179,8 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, authenticationResult); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Device verification request failed: %s", ex.getError()), ex); } @@ -174,21 +189,27 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}. - * - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest} + * Sets the {@link AuthenticationDetailsSource} used for building an authentication + * details instance from {@link HttpServletRequest}. + * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for + * building an authentication details instance from {@link HttpServletRequest} */ - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); this.authenticationDetailsSource = authenticationDetailsSource; } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest} - * to an instance of {@link OAuth2DeviceVerificationAuthenticationToken} or {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} - * used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Device Verification Request (or Device Authorization Consent) from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract a Device + * Verification Request (or Device Authorization Consent) from + * {@link HttpServletRequest} to an instance of + * {@link OAuth2DeviceVerificationAuthenticationToken} or + * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating + * the request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract a Device Verification Request (or Device Authorization + * Consent) from {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -196,10 +217,10 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken} - * and returning the response. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2DeviceVerificationAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2DeviceVerificationAuthenticationToken} and returning the response. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2DeviceVerificationAuthenticationToken} */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -207,10 +228,11 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); @@ -218,10 +240,10 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest } /** - * Specify the URI to redirect Resource Owners to if consent is required. A default consent - * page will be generated when this attribute is not specified. - * - * @param consentPage the URI of the custom consent page to redirect to if consent is required (e.g. "/oauth2/consent") + * Specify the URI to redirect Resource Owners to if consent is required. A default + * consent page will be generated when this attribute is not specified. + * @param consentPage the URI of the custom consent page to redirect to if consent is + * required (e.g. "/oauth2/consent") */ public void setConsentPage(String consentPage) { this.consentPage = consentPage; @@ -230,8 +252,7 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest private void sendAuthorizationConsent(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - OAuth2DeviceAuthorizationConsentAuthenticationToken authorizationConsentAuthentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication; + OAuth2DeviceAuthorizationConsentAuthenticationToken authorizationConsentAuthentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) authentication; String clientId = authorizationConsentAuthentication.getClientId(); Authentication principal = (Authentication) authorizationConsentAuthentication.getPrincipal(); @@ -242,19 +263,21 @@ public final class OAuth2DeviceVerificationEndpointFilter extends OncePerRequest if (hasConsentUri()) { String redirectUri = UriComponentsBuilder.fromUriString(resolveConsentUri(request)) - .queryParam(OAuth2ParameterNames.SCOPE, String.join(" ", requestedScopes)) - .queryParam(OAuth2ParameterNames.CLIENT_ID, clientId) - .queryParam(OAuth2ParameterNames.STATE, state) - .queryParam(OAuth2ParameterNames.USER_CODE, userCode) - .toUriString(); + .queryParam(OAuth2ParameterNames.SCOPE, String.join(" ", requestedScopes)) + .queryParam(OAuth2ParameterNames.CLIENT_ID, clientId) + .queryParam(OAuth2ParameterNames.STATE, state) + .queryParam(OAuth2ParameterNames.USER_CODE, userCode) + .toUriString(); this.redirectStrategy.sendRedirect(request, response, redirectUri); - } else { + } + else { if (this.logger.isTraceEnabled()) { this.logger.trace("Displaying generated consent screen"); } Map additionalParameters = new HashMap<>(); additionalParameters.put(OAuth2ParameterNames.USER_CODE, userCode); - DefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes, state, additionalParameters); + DefaultConsentPage.displayConsent(request, response, clientId, principal, requestedScopes, authorizedScopes, + state, additionalParameters); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java index 655162c3..780efe25 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilter.java @@ -65,23 +65,24 @@ import org.springframework.util.CollectionUtils; import org.springframework.web.filter.OncePerRequestFilter; /** - * A {@code Filter} for the OAuth 2.0 Token endpoint, - * which handles the processing of an OAuth 2.0 Authorization Grant. + * A {@code Filter} for the OAuth 2.0 Token endpoint, which handles the processing of an + * OAuth 2.0 Authorization Grant. * *

* It converts the OAuth 2.0 Authorization Grant request to an {@link Authentication}, - * which is then authenticated by the {@link AuthenticationManager}. - * If the authentication succeeds, the {@link AuthenticationManager} returns an - * {@link OAuth2AccessTokenAuthenticationToken}, which is returned in the OAuth 2.0 Access Token response. - * In case of any error, an {@link OAuth2Error} is returned in the OAuth 2.0 Error response. + * which is then authenticated by the {@link AuthenticationManager}. If the authentication + * succeeds, the {@link AuthenticationManager} returns an + * {@link OAuth2AccessTokenAuthenticationToken}, which is returned in the OAuth 2.0 Access + * Token response. In case of any error, an {@link OAuth2Error} is returned in the OAuth + * 2.0 Error response. * *

- * By default, this {@code Filter} responds to authorization grant requests - * at the {@code URI} {@code /oauth2/token} and {@code HttpMethod} {@code POST}. + * By default, this {@code Filter} responds to authorization grant requests at the + * {@code URI} {@code /oauth2/token} and {@code HttpMethod} {@code POST}. * *

- * The default endpoint {@code URI} {@code /oauth2/token} may be overridden - * via the constructor {@link #OAuth2TokenEndpointFilter(AuthenticationManager, String)}. + * The default endpoint {@code URI} {@code /oauth2/token} may be overridden via the + * constructor {@link #OAuth2TokenEndpointFilter(AuthenticationManager, String)}. * * @author Joe Grandja * @author Madhu Bhat @@ -92,28 +93,34 @@ import org.springframework.web.filter.OncePerRequestFilter; * @see OAuth2RefreshTokenAuthenticationProvider * @see OAuth2ClientCredentialsAuthenticationProvider * @see OAuth2DeviceCodeAuthenticationProvider - * @see Section 3.2 Token Endpoint + * @see Section + * 3.2 Token Endpoint */ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for access token requests. */ private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; + private final AuthenticationManager authenticationManager; + private final RequestMatcher tokenEndpointMatcher; - private final HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); - private AuthenticationDetailsSource authenticationDetailsSource = - new WebAuthenticationDetailsSource(); + + private final HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + + private AuthenticationDetailsSource authenticationDetailsSource = new WebAuthenticationDetailsSource(); + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAccessTokenResponse; + private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler(); /** * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager */ public OAuth2TokenEndpointFilter(AuthenticationManager authenticationManager) { @@ -122,7 +129,6 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { /** * Constructs an {@code OAuth2TokenEndpointFilter} using the provided parameters. - * * @param authenticationManager the authentication manager * @param tokenEndpointUri the endpoint {@code URI} for access token requests */ @@ -131,12 +137,14 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { Assert.hasText(tokenEndpointUri, "tokenEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; this.tokenEndpointMatcher = new AntPathRequestMatcher(tokenEndpointUri, HttpMethod.POST.name()); + // @formatter:off this.authenticationConverter = new DelegatingAuthenticationConverter( Arrays.asList( new OAuth2AuthorizationCodeAuthenticationConverter(), new OAuth2RefreshTokenAuthenticationConverter(), new OAuth2ClientCredentialsAuthenticationConverter(), new OAuth2DeviceCodeAuthenticationConverter())); + // @formatter:on } @Override @@ -160,13 +168,14 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } if (authorizationGrantAuthentication instanceof AbstractAuthenticationToken) { ((AbstractAuthenticationToken) authorizationGrantAuthentication) - .setDetails(this.authenticationDetailsSource.buildDetails(request)); + .setDetails(this.authenticationDetailsSource.buildDetails(request)); } - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationManager.authenticate(authorizationGrantAuthentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationManager + .authenticate(authorizationGrantAuthentication); this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, accessTokenAuthentication); - } catch (OAuth2AuthenticationException ex) { + } + catch (OAuth2AuthenticationException ex) { SecurityContextHolder.clearContext(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Token request failed: %s", ex.getError()), ex); @@ -176,20 +185,24 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest}. - * - * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for building an authentication details instance from {@link HttpServletRequest} + * Sets the {@link AuthenticationDetailsSource} used for building an authentication + * details instance from {@link HttpServletRequest}. + * @param authenticationDetailsSource the {@link AuthenticationDetailsSource} used for + * building an authentication details instance from {@link HttpServletRequest} */ - public void setAuthenticationDetailsSource(AuthenticationDetailsSource authenticationDetailsSource) { + public void setAuthenticationDetailsSource( + AuthenticationDetailsSource authenticationDetailsSource) { Assert.notNull(authenticationDetailsSource, "authenticationDetailsSource cannot be null"); this.authenticationDetailsSource = authenticationDetailsSource; } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the authorization grant. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an Access Token Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract an Access + * Token Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2AuthorizationGrantAuthenticationToken} used for authenticating the + * authorization grant. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract an Access Token Request from {@link HttpServletRequest} */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { Assert.notNull(authenticationConverter, "authenticationConverter cannot be null"); @@ -197,10 +210,11 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} - * and returning the {@link OAuth2AccessTokenResponse Access Token Response}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2AccessTokenAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2AccessTokenAuthenticationToken} and returning the + * {@link OAuth2AccessTokenResponse Access Token Response}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2AccessTokenAuthenticationToken} */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { Assert.notNull(authenticationSuccessHandler, "authenticationSuccessHandler cannot be null"); @@ -208,10 +222,11 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { Assert.notNull(authenticationFailureHandler, "authenticationFailureHandler cannot be null"); @@ -221,17 +236,15 @@ public final class OAuth2TokenEndpointFilter extends OncePerRequestFilter { private void sendAccessTokenResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) authentication; + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) authentication; OAuth2AccessToken accessToken = accessTokenAuthentication.getAccessToken(); OAuth2RefreshToken refreshToken = accessTokenAuthentication.getRefreshToken(); Map additionalParameters = accessTokenAuthentication.getAdditionalParameters(); - OAuth2AccessTokenResponse.Builder builder = - OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) - .tokenType(accessToken.getTokenType()) - .scopes(accessToken.getScopes()); + OAuth2AccessTokenResponse.Builder builder = OAuth2AccessTokenResponse.withToken(accessToken.getTokenValue()) + .tokenType(accessToken.getTokenType()) + .scopes(accessToken.getScopes()); if (accessToken.getIssuedAt() != null && accessToken.getExpiresAt() != null) { builder.expiresIn(ChronoUnit.SECONDS.between(accessToken.getIssuedAt(), accessToken.getExpiresAt())); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilter.java index 95cd5e03..3f55020d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilter.java @@ -52,27 +52,34 @@ import org.springframework.web.filter.OncePerRequestFilter; * @author Joe Grandja * @author Gaurav Tiwari * @see OAuth2TokenIntrospectionAuthenticationProvider - * @see Section 2 Introspection Endpoint - * @see Section 2.1 Introspection Request + * @see Section 2 + * Introspection Endpoint + * @see Section + * 2.1 Introspection Request * @since 0.1.1 */ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for token introspection requests. */ private static final String DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI = "/oauth2/introspect"; private final AuthenticationManager authenticationManager; + private final RequestMatcher tokenIntrospectionEndpointMatcher; + private AuthenticationConverter authenticationConverter; - private final HttpMessageConverter tokenIntrospectionHttpResponseConverter = - new OAuth2TokenIntrospectionHttpMessageConverter(); + + private final HttpMessageConverter tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter(); + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendIntrospectionResponse; + private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler(); /** - * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OAuth2TokenIntrospectionEndpointFilter(AuthenticationManager authenticationManager) { @@ -80,18 +87,19 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest } /** - * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2TokenIntrospectionEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager - * @param tokenIntrospectionEndpointUri the endpoint {@code URI} for token introspection requests + * @param tokenIntrospectionEndpointUri the endpoint {@code URI} for token + * introspection requests */ public OAuth2TokenIntrospectionEndpointFilter(AuthenticationManager authenticationManager, String tokenIntrospectionEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(tokenIntrospectionEndpointUri, "tokenIntrospectionEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; - this.tokenIntrospectionEndpointMatcher = new AntPathRequestMatcher( - tokenIntrospectionEndpointUri, HttpMethod.POST.name()); + this.tokenIntrospectionEndpointMatcher = new AntPathRequestMatcher(tokenIntrospectionEndpointUri, + HttpMethod.POST.name()); this.authenticationConverter = new OAuth2TokenIntrospectionAuthenticationConverter(); } @@ -106,10 +114,12 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest try { Authentication tokenIntrospectionAuthentication = this.authenticationConverter.convert(request); - Authentication tokenIntrospectionAuthenticationResult = - this.authenticationManager.authenticate(tokenIntrospectionAuthentication); - this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, tokenIntrospectionAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + Authentication tokenIntrospectionAuthenticationResult = this.authenticationManager + .authenticate(tokenIntrospectionAuthentication); + this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, + tokenIntrospectionAuthenticationResult); + } + catch (OAuth2AuthenticationException ex) { SecurityContextHolder.clearContext(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Token introspection request failed: %s", ex.getError()), ex); @@ -119,10 +129,12 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract an Introspection Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract an + * Introspection Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the + * request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract an Introspection Request from {@link HttpServletRequest} * @since 0.2.3 */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { @@ -131,9 +143,10 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenIntrospectionAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2TokenIntrospectionAuthenticationToken}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2TokenIntrospectionAuthenticationToken} * @since 0.2.3 */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { @@ -142,10 +155,11 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Resonse}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Resonse}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} * @since 0.2.3 */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { @@ -156,8 +170,7 @@ public final class OAuth2TokenIntrospectionEndpointFilter extends OncePerRequest private void sendIntrospectionResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException { - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = - (OAuth2TokenIntrospectionAuthenticationToken) authentication; + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = (OAuth2TokenIntrospectionAuthenticationToken) authentication; OAuth2TokenIntrospection tokenClaims = tokenIntrospectionAuthentication.getTokenClaims(); ServletServerHttpResponse httpResponse = new ServletServerHttpResponse(response); this.tokenIntrospectionHttpResponseConverter.write(tokenClaims, null, httpResponse); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java index 03d01dd4..f8f02d15 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilter.java @@ -49,25 +49,32 @@ import org.springframework.web.filter.OncePerRequestFilter; * @author Joe Grandja * @author Arfat Chaus * @see OAuth2TokenRevocationAuthenticationProvider - * @see Section 2 Token Revocation - * @see Section 2.1 Revocation Request + * @see Section 2 + * Token Revocation + * @see Section + * 2.1 Revocation Request * @since 0.0.3 */ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFilter { + /** * The default endpoint {@code URI} for token revocation requests. */ private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke"; private final AuthenticationManager authenticationManager; + private final RequestMatcher tokenRevocationEndpointMatcher; + private AuthenticationConverter authenticationConverter; + private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendRevocationSuccessResponse; + private AuthenticationFailureHandler authenticationFailureHandler = new OAuth2ErrorAuthenticationFailureHandler(); /** - * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager */ public OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager) { @@ -75,18 +82,19 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil } /** - * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided parameters. - * + * Constructs an {@code OAuth2TokenRevocationEndpointFilter} using the provided + * parameters. * @param authenticationManager the authentication manager - * @param tokenRevocationEndpointUri the endpoint {@code URI} for token revocation requests + * @param tokenRevocationEndpointUri the endpoint {@code URI} for token revocation + * requests */ public OAuth2TokenRevocationEndpointFilter(AuthenticationManager authenticationManager, String tokenRevocationEndpointUri) { Assert.notNull(authenticationManager, "authenticationManager cannot be null"); Assert.hasText(tokenRevocationEndpointUri, "tokenRevocationEndpointUri cannot be empty"); this.authenticationManager = authenticationManager; - this.tokenRevocationEndpointMatcher = new AntPathRequestMatcher( - tokenRevocationEndpointUri, HttpMethod.POST.name()); + this.tokenRevocationEndpointMatcher = new AntPathRequestMatcher(tokenRevocationEndpointUri, + HttpMethod.POST.name()); this.authenticationConverter = new OAuth2TokenRevocationAuthenticationConverter(); } @@ -101,10 +109,12 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil try { Authentication tokenRevocationAuthentication = this.authenticationConverter.convert(request); - Authentication tokenRevocationAuthenticationResult = - this.authenticationManager.authenticate(tokenRevocationAuthentication); - this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, tokenRevocationAuthenticationResult); - } catch (OAuth2AuthenticationException ex) { + Authentication tokenRevocationAuthenticationResult = this.authenticationManager + .authenticate(tokenRevocationAuthentication); + this.authenticationSuccessHandler.onAuthenticationSuccess(request, response, + tokenRevocationAuthenticationResult); + } + catch (OAuth2AuthenticationException ex) { SecurityContextHolder.clearContext(); if (this.logger.isTraceEnabled()) { this.logger.trace(LogMessage.format("Token revocation request failed: %s", ex.getError()), ex); @@ -114,10 +124,12 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil } /** - * Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} - * to an instance of {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request. - * - * @param authenticationConverter the {@link AuthenticationConverter} used when attempting to extract a Revoke Token Request from {@link HttpServletRequest} + * Sets the {@link AuthenticationConverter} used when attempting to extract a Revoke + * Token Request from {@link HttpServletRequest} to an instance of + * {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the + * request. + * @param authenticationConverter the {@link AuthenticationConverter} used when + * attempting to extract a Revoke Token Request from {@link HttpServletRequest} * @since 0.2.2 */ public void setAuthenticationConverter(AuthenticationConverter authenticationConverter) { @@ -126,9 +138,10 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil } /** - * Sets the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken}. - * - * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used for handling an {@link OAuth2TokenRevocationAuthenticationToken} + * Sets the {@link AuthenticationSuccessHandler} used for handling an + * {@link OAuth2TokenRevocationAuthenticationToken}. + * @param authenticationSuccessHandler the {@link AuthenticationSuccessHandler} used + * for handling an {@link OAuth2TokenRevocationAuthenticationToken} * @since 0.2.2 */ public void setAuthenticationSuccessHandler(AuthenticationSuccessHandler authenticationSuccessHandler) { @@ -137,10 +150,11 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil } /** - * Sets the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} - * and returning the {@link OAuth2Error Error Response}. - * - * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used for handling an {@link OAuth2AuthenticationException} + * Sets the {@link AuthenticationFailureHandler} used for handling an + * {@link OAuth2AuthenticationException} and returning the {@link OAuth2Error Error + * Response}. + * @param authenticationFailureHandler the {@link AuthenticationFailureHandler} used + * for handling an {@link OAuth2AuthenticationException} * @since 0.2.2 */ public void setAuthenticationFailureHandler(AuthenticationFailureHandler authenticationFailureHandler) { @@ -148,7 +162,8 @@ public final class OAuth2TokenRevocationEndpointFilter extends OncePerRequestFil this.authenticationFailureHandler = authenticationFailureHandler; } - private void sendRevocationSuccessResponse(HttpServletRequest request, HttpServletResponse response, Authentication authentication) { + private void sendRevocationSuccessResponse(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) { response.setStatus(HttpStatus.OK.value()); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java index f3ab210e..794a9776 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverter.java @@ -34,8 +34,9 @@ import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.StringUtils; /** - * Attempts to extract HTTP Basic credentials from {@link HttpServletRequest} - * and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the client. + * Attempts to extract HTTP Basic credentials from {@link HttpServletRequest} and then + * converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the + * client. * * @author Patryk Kostrzewa * @author Joe Grandja @@ -65,17 +66,15 @@ public final class ClientSecretBasicAuthenticationConverter implements Authentic byte[] decodedCredentials; try { - decodedCredentials = Base64.getDecoder().decode( - parts[1].getBytes(StandardCharsets.UTF_8)); - } catch (IllegalArgumentException ex) { + decodedCredentials = Base64.getDecoder().decode(parts[1].getBytes(StandardCharsets.UTF_8)); + } + catch (IllegalArgumentException ex) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST), ex); } String credentialsString = new String(decodedCredentials, StandardCharsets.UTF_8); String[] credentials = credentialsString.split(":", 2); - if (credentials.length != 2 || - !StringUtils.hasText(credentials[0]) || - !StringUtils.hasText(credentials[1])) { + if (credentials.length != 2 || !StringUtils.hasText(credentials[0]) || !StringUtils.hasText(credentials[1])) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } @@ -84,12 +83,13 @@ public final class ClientSecretBasicAuthenticationConverter implements Authentic try { clientID = URLDecoder.decode(credentials[0], StandardCharsets.UTF_8.name()); clientSecret = URLDecoder.decode(credentials[1], StandardCharsets.UTF_8.name()); - } catch (Exception ex) { + } + catch (Exception ex) { throw new OAuth2AuthenticationException(new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST), ex); } - return new OAuth2ClientAuthenticationToken(clientID, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, clientSecret, - OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request)); + return new OAuth2ClientAuthenticationToken(clientID, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + clientSecret, OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request)); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverter.java index 04207fb5..80c8bac8 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverter.java @@ -32,15 +32,17 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract client credentials from POST parameters of {@link HttpServletRequest} - * and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the client. + * Attempts to extract client credentials from POST parameters of + * {@link HttpServletRequest} and then converts to an + * {@link OAuth2ClientAuthenticationToken} used for authenticating the client. * * @author Anoop Garlapati * @since 0.1.0 * @see AuthenticationConverter * @see OAuth2ClientAuthenticationToken * @see OAuth2ClientAuthenticationFilter - * @see Section 2.3.1 Client Password + * @see Section 2.3.1 Client Password */ public final class ClientSecretPostAuthenticationConverter implements AuthenticationConverter { @@ -69,12 +71,12 @@ public final class ClientSecretPostAuthenticationConverter implements Authentica throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } - Map additionalParameters = OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request, - OAuth2ParameterNames.CLIENT_ID, - OAuth2ParameterNames.CLIENT_SECRET); + Map additionalParameters = OAuth2EndpointUtils + .getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ID, + OAuth2ParameterNames.CLIENT_SECRET); - return new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.CLIENT_SECRET_POST, clientSecret, - additionalParameters); + return new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.CLIENT_SECRET_POST, + clientSecret, additionalParameters); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/DelegatingAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/DelegatingAuthenticationConverter.java index 911beafb..f918a147 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/DelegatingAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/DelegatingAuthenticationConverter.java @@ -27,23 +27,24 @@ import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.Assert; /** - * An {@link AuthenticationConverter} that simply delegates to it's - * internal {@code List} of {@link AuthenticationConverter}(s). + * An {@link AuthenticationConverter} that simply delegates to it's internal {@code List} + * of {@link AuthenticationConverter}(s). *

* Each {@link AuthenticationConverter} is given a chance to - * {@link AuthenticationConverter#convert(HttpServletRequest)} - * with the first {@code non-null} {@link Authentication} being returned. + * {@link AuthenticationConverter#convert(HttpServletRequest)} with the first + * {@code non-null} {@link Authentication} being returned. * * @author Joe Grandja * @since 0.0.2 * @see AuthenticationConverter */ public final class DelegatingAuthenticationConverter implements AuthenticationConverter { + private final List converters; /** - * Constructs a {@code DelegatingAuthenticationConverter} using the provided parameters. - * + * Constructs a {@code DelegatingAuthenticationConverter} using the provided + * parameters. * @param converters a {@code List} of {@link AuthenticationConverter}(s) */ public DelegatingAuthenticationConverter(List converters) { @@ -63,4 +64,5 @@ public final class DelegatingAuthenticationConverter implements AuthenticationCo } return null; } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java index a29c663f..7a09625f 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverter.java @@ -33,7 +33,8 @@ import org.springframework.util.StringUtils; /** * Attempts to extract a JWT client assertion credential from {@link HttpServletRequest} - * and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating the client. + * and then converts to an {@link OAuth2ClientAuthenticationToken} used for authenticating + * the client. * * @author Rafal Lewczuk * @since 0.2.2 @@ -42,16 +43,17 @@ import org.springframework.util.StringUtils; * @see OAuth2ClientAuthenticationFilter */ public final class JwtClientAssertionAuthenticationConverter implements AuthenticationConverter { - private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = - new ClientAuthenticationMethod("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + + private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod( + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); @Nullable @Override public Authentication convert(HttpServletRequest request) { MultiValueMap parameters = OAuth2EndpointUtils.getFormParameters(request); - if (parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE) == null || - parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION) == null) { + if (parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE) == null + || parameters.getFirst(OAuth2ParameterNames.CLIENT_ASSERTION) == null) { return null; } @@ -72,18 +74,16 @@ public final class JwtClientAssertionAuthenticationConverter implements Authenti // client_id (OPTIONAL as per specification but REQUIRED by this implementation) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } - Map additionalParameters = OAuth2EndpointUtils.getParametersIfMatchesAuthorizationCodeGrantRequest(request, - OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, - OAuth2ParameterNames.CLIENT_ASSERTION, - OAuth2ParameterNames.CLIENT_ID); + Map additionalParameters = OAuth2EndpointUtils + .getParametersIfMatchesAuthorizationCodeGrantRequest(request, OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, + OAuth2ParameterNames.CLIENT_ASSERTION, OAuth2ParameterNames.CLIENT_ID); - return new OAuth2ClientAuthenticationToken(clientId, JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, - jwtAssertion, additionalParameters); + return new OAuth2ClientAuthenticationToken(clientId, JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, jwtAssertion, + additionalParameters); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java index 2dda4c0e..18440eac 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeAuthenticationConverter.java @@ -33,8 +33,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the OAuth 2.0 Authorization Code Grant - * and then converts it to an {@link OAuth2AuthorizationCodeAuthenticationToken} used for authenticating the authorization grant. + * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the + * OAuth 2.0 Authorization Code Grant and then converts it to an + * {@link OAuth2AuthorizationCodeAuthenticationToken} used for authenticating the + * authorization grant. * * @author Joe Grandja * @since 0.1.2 @@ -59,37 +61,29 @@ public final class OAuth2AuthorizationCodeAuthenticationConverter implements Aut // code (REQUIRED) String code = parameters.getFirst(OAuth2ParameterNames.CODE); - if (!StringUtils.hasText(code) || - parameters.get(OAuth2ParameterNames.CODE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.CODE, + if (!StringUtils.hasText(code) || parameters.get(OAuth2ParameterNames.CODE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CODE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } // redirect_uri (REQUIRED) - // Required only if the "redirect_uri" parameter was included in the authorization request + // Required only if the "redirect_uri" parameter was included in the authorization + // request String redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); - if (StringUtils.hasText(redirectUri) && - parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.REDIRECT_URI, + if (StringUtils.hasText(redirectUri) && parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && - !key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.CODE) && - !key.equals(OAuth2ParameterNames.REDIRECT_URI)) { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID) + && !key.equals(OAuth2ParameterNames.CODE) && !key.equals(OAuth2ParameterNames.REDIRECT_URI)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2AuthorizationCodeAuthenticationToken( - code, clientPrincipal, redirectUri, additionalParameters); + return new OAuth2AuthorizationCodeAuthenticationToken(code, clientPrincipal, redirectUri, additionalParameters); } } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java index 3d5ea80d..73832583 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationCodeRequestAuthenticationConverter.java @@ -43,9 +43,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Authorization Request from {@link HttpServletRequest} - * for the OAuth 2.0 Authorization Code Grant and then converts it to - * an {@link OAuth2AuthorizationCodeRequestAuthenticationToken} used for authenticating the request. + * Attempts to extract an Authorization Request from {@link HttpServletRequest} for the + * OAuth 2.0 Authorization Code Grant and then converts it to an + * {@link OAuth2AuthorizationCodeRequestAuthenticationToken} used for authenticating the + * request. * * @author Joe Grandja * @since 0.1.2 @@ -54,10 +55,14 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationEndpointFilter */ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter implements AuthenticationConverter { + private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; + private static final String PKCE_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1"; - private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + private static final RequestMatcher OIDC_REQUEST_MATCHER = createOidcRequestMatcher(); @Override @@ -66,17 +71,15 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme return null; } - MultiValueMap parameters = - "GET".equals(request.getMethod()) ? - OAuth2EndpointUtils.getQueryParameters(request) : - OAuth2EndpointUtils.getFormParameters(request); + MultiValueMap parameters = "GET".equals(request.getMethod()) + ? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request); // response_type (REQUIRED) String responseType = parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE); - if (!StringUtils.hasText(responseType) || - parameters.get(OAuth2ParameterNames.RESPONSE_TYPE).size() != 1) { + if (!StringUtils.hasText(responseType) || parameters.get(OAuth2ParameterNames.RESPONSE_TYPE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.RESPONSE_TYPE); - } else if (!responseType.equals(OAuth2AuthorizationResponseType.CODE.getValue())) { + } + else if (!responseType.equals(OAuth2AuthorizationResponseType.CODE.getValue())) { throwError(OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, OAuth2ParameterNames.RESPONSE_TYPE); } @@ -84,8 +87,7 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme // client_id (REQUIRED) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID); } @@ -96,69 +98,61 @@ public final class OAuth2AuthorizationCodeRequestAuthenticationConverter impleme // redirect_uri (OPTIONAL) String redirectUri = parameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); - if (StringUtils.hasText(redirectUri) && - parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) { + if (StringUtils.hasText(redirectUri) && parameters.get(OAuth2ParameterNames.REDIRECT_URI).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI); } // scope (OPTIONAL) Set scopes = null; String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); - if (StringUtils.hasText(scope) && - parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { + if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE); } if (StringUtils.hasText(scope)) { - scopes = new HashSet<>( - Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + scopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); } // state (RECOMMENDED) String state = parameters.getFirst(OAuth2ParameterNames.STATE); - if (StringUtils.hasText(state) && - parameters.get(OAuth2ParameterNames.STATE).size() != 1) { + if (StringUtils.hasText(state) && parameters.get(OAuth2ParameterNames.STATE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE); } // code_challenge (REQUIRED for public clients) - RFC 7636 (PKCE) String codeChallenge = parameters.getFirst(PkceParameterNames.CODE_CHALLENGE); - if (StringUtils.hasText(codeChallenge) && - parameters.get(PkceParameterNames.CODE_CHALLENGE).size() != 1) { + if (StringUtils.hasText(codeChallenge) && parameters.get(PkceParameterNames.CODE_CHALLENGE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, PKCE_ERROR_URI); } // code_challenge_method (OPTIONAL for public clients) - RFC 7636 (PKCE) String codeChallengeMethod = parameters.getFirst(PkceParameterNames.CODE_CHALLENGE_METHOD); - if (StringUtils.hasText(codeChallengeMethod) && - parameters.get(PkceParameterNames.CODE_CHALLENGE_METHOD).size() != 1) { + if (StringUtils.hasText(codeChallengeMethod) + && parameters.get(PkceParameterNames.CODE_CHALLENGE_METHOD).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, PKCE_ERROR_URI); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.RESPONSE_TYPE) && - !key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.REDIRECT_URI) && - !key.equals(OAuth2ParameterNames.SCOPE) && - !key.equals(OAuth2ParameterNames.STATE)) { + if (!key.equals(OAuth2ParameterNames.RESPONSE_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID) + && !key.equals(OAuth2ParameterNames.REDIRECT_URI) && !key.equals(OAuth2ParameterNames.SCOPE) + && !key.equals(OAuth2ParameterNames.STATE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationUri, clientId, principal, - redirectUri, state, scopes, additionalParameters); + return new OAuth2AuthorizationCodeRequestAuthenticationToken(authorizationUri, clientId, principal, redirectUri, + state, scopes, additionalParameters); } private static RequestMatcher createOidcRequestMatcher() { RequestMatcher postMethodMatcher = request -> "POST".equals(request.getMethod()); - RequestMatcher responseTypeParameterMatcher = request -> - request.getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; + RequestMatcher responseTypeParameterMatcher = request -> request + .getParameter(OAuth2ParameterNames.RESPONSE_TYPE) != null; RequestMatcher openidScopeMatcher = request -> { String scope = request.getParameter(OAuth2ParameterNames.SCOPE); return StringUtils.hasText(scope) && scope.contains(OidcScopes.OPENID); }; - return new AndRequestMatcher( - postMethodMatcher, responseTypeParameterMatcher, openidScopeMatcher); + return new AndRequestMatcher(postMethodMatcher, responseTypeParameterMatcher, openidScopeMatcher); } private static void throwError(String errorCode, String parameterName) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java index 7da9e9f1..c6350c11 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2AuthorizationConsentAuthenticationConverter.java @@ -37,9 +37,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Authorization Consent from {@link HttpServletRequest} - * for the OAuth 2.0 Authorization Code Grant and then converts it to - * an {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the request. + * Attempts to extract an Authorization Consent from {@link HttpServletRequest} for the + * OAuth 2.0 Authorization Code Grant and then converts it to an + * {@link OAuth2AuthorizationConsentAuthenticationToken} used for authenticating the + * request. * * @author Joe Grandja * @since 0.4.0 @@ -48,16 +49,17 @@ import org.springframework.util.StringUtils; * @see OAuth2AuthorizationEndpointFilter */ public final class OAuth2AuthorizationConsentAuthenticationConverter implements AuthenticationConverter { + private static final String DEFAULT_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-4.1.2.1"; - private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); @Override public Authentication convert(HttpServletRequest request) { MultiValueMap parameters = OAuth2EndpointUtils.getFormParameters(request); - if (!"POST".equals(request.getMethod()) || - parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE) != null) { + if (!"POST".equals(request.getMethod()) || parameters.getFirst(OAuth2ParameterNames.RESPONSE_TYPE) != null) { return null; } @@ -65,8 +67,7 @@ public final class OAuth2AuthorizationConsentAuthenticationConverter implements // client_id (REQUIRED) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID); } @@ -77,8 +78,7 @@ public final class OAuth2AuthorizationConsentAuthenticationConverter implements // state (REQUIRED) String state = parameters.getFirst(OAuth2ParameterNames.STATE); - if (!StringUtils.hasText(state) || - parameters.get(OAuth2ParameterNames.STATE).size() != 1) { + if (!StringUtils.hasText(state) || parameters.get(OAuth2ParameterNames.STATE).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE); } @@ -90,15 +90,14 @@ public final class OAuth2AuthorizationConsentAuthenticationConverter implements Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.STATE) && - !key.equals(OAuth2ParameterNames.SCOPE)) { + if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.STATE) + && !key.equals(OAuth2ParameterNames.SCOPE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2AuthorizationConsentAuthenticationToken(authorizationUri, clientId, principal, - state, scopes, additionalParameters); + return new OAuth2AuthorizationConsentAuthenticationToken(authorizationUri, clientId, principal, state, scopes, + additionalParameters); } private static void throwError(String errorCode, String parameterName) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java index 7c34754a..a4a0dea3 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2ClientCredentialsAuthenticationConverter.java @@ -36,8 +36,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the OAuth 2.0 Client Credentials Grant - * and then converts it to an {@link OAuth2ClientCredentialsAuthenticationToken} used for authenticating the authorization grant. + * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the + * OAuth 2.0 Client Credentials Grant and then converts it to an + * {@link OAuth2ClientCredentialsAuthenticationToken} used for authenticating the + * authorization grant. * * @author Joe Grandja * @since 0.1.2 @@ -62,28 +64,23 @@ public final class OAuth2ClientCredentialsAuthenticationConverter implements Aut // scope (OPTIONAL) String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); - if (StringUtils.hasText(scope) && - parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.SCOPE, + if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } Set requestedScopes = null; if (StringUtils.hasText(scope)) { - requestedScopes = new HashSet<>( - Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && - !key.equals(OAuth2ParameterNames.SCOPE)) { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.SCOPE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2ClientCredentialsAuthenticationToken( - clientPrincipal, requestedScopes, additionalParameters); + return new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScopes, additionalParameters); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java index 3c4ea0ed..a64bd157 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverter.java @@ -35,10 +35,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract a Device Authorization Consent from {@link HttpServletRequest} - * for the OAuth 2.0 Device Authorization Grant and then converts it to an - * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for - * authenticating the request. + * Attempts to extract a Device Authorization Consent from {@link HttpServletRequest} for + * the OAuth 2.0 Device Authorization Grant and then converts it to an + * {@link OAuth2DeviceAuthorizationConsentAuthenticationToken} used for authenticating the + * request. * * @author Steve Riesenberg * @since 1.1 @@ -49,13 +49,13 @@ import org.springframework.util.StringUtils; public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter implements AuthenticationConverter { private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; - private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); @Override public Authentication convert(HttpServletRequest request) { - if (!"POST".equals(request.getMethod()) || - request.getParameter(OAuth2ParameterNames.STATE) == null) { + if (!"POST".equals(request.getMethod()) || request.getParameter(OAuth2ParameterNames.STATE) == null) { return null; } @@ -65,12 +65,8 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple // client_id (REQUIRED) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.CLIENT_ID, - ERROR_URI); + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, ERROR_URI); } Authentication principal = SecurityContextHolder.getContext().getAuthentication(); @@ -80,22 +76,15 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple // user_code (REQUIRED) String userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE); - if (!OAuth2EndpointUtils.validateUserCode(userCode) || - parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.USER_CODE, - ERROR_URI); + if (!OAuth2EndpointUtils.validateUserCode(userCode) + || parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.USER_CODE, ERROR_URI); } // state (REQUIRED) String state = parameters.getFirst(OAuth2ParameterNames.STATE); - if (!StringUtils.hasText(state) || - parameters.get(OAuth2ParameterNames.STATE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.STATE, - ERROR_URI); + if (!StringUtils.hasText(state) || parameters.get(OAuth2ParameterNames.STATE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, ERROR_URI); } // scope (OPTIONAL) @@ -106,10 +95,8 @@ public final class OAuth2DeviceAuthorizationConsentAuthenticationConverter imple Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.USER_CODE) && - !key.equals(OAuth2ParameterNames.STATE) && - !key.equals(OAuth2ParameterNames.SCOPE)) { + if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.USER_CODE) + && !key.equals(OAuth2ParameterNames.STATE) && !key.equals(OAuth2ParameterNames.SCOPE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverter.java index 925dfed6..129c9c3a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverter.java @@ -34,10 +34,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract a Device Authorization Request from {@link HttpServletRequest} for the - * OAuth 2.0 Device Authorization Grant and then converts it to an - * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating - * the request. + * Attempts to extract a Device Authorization Request from {@link HttpServletRequest} for + * the OAuth 2.0 Device Authorization Grant and then converts it to an + * {@link OAuth2DeviceAuthorizationRequestAuthenticationToken} used for authenticating the + * request. * * @author Steve Riesenberg * @since 1.1 @@ -59,12 +59,8 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationConverter imple // scope (OPTIONAL) String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); - if (StringUtils.hasText(scope) && - parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.SCOPE, - ERROR_URI); + if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE, ERROR_URI); } Set requestedScopes = null; if (StringUtils.hasText(scope)) { @@ -73,8 +69,7 @@ public final class OAuth2DeviceAuthorizationRequestAuthenticationConverter imple Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.SCOPE)) { + if (!key.equals(OAuth2ParameterNames.CLIENT_ID) && !key.equals(OAuth2ParameterNames.SCOPE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java index 1405b76f..c61f804a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverter.java @@ -33,10 +33,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract a Device Access Token Request from {@link HttpServletRequest} for the - * OAuth 2.0 Device Authorization Grant and then converts it to an - * {@link OAuth2DeviceCodeAuthenticationToken} used for authenticating the - * authorization grant. + * Attempts to extract a Device Access Token Request from {@link HttpServletRequest} for + * the OAuth 2.0 Device Authorization Grant and then converts it to an + * {@link OAuth2DeviceCodeAuthenticationToken} used for authenticating the authorization + * grant. * * @author Steve Riesenberg * @since 1.1 @@ -61,19 +61,15 @@ public final class OAuth2DeviceCodeAuthenticationConverter implements Authentica // device_code (REQUIRED) String deviceCode = parameters.getFirst(OAuth2ParameterNames.DEVICE_CODE); - if (!StringUtils.hasText(deviceCode) || - parameters.get(OAuth2ParameterNames.DEVICE_CODE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.DEVICE_CODE, + if (!StringUtils.hasText(deviceCode) || parameters.get(OAuth2ParameterNames.DEVICE_CODE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.DEVICE_CODE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && - !key.equals(OAuth2ParameterNames.CLIENT_ID) && - !key.equals(OAuth2ParameterNames.DEVICE_CODE)) { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.CLIENT_ID) + && !key.equals(OAuth2ParameterNames.DEVICE_CODE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java index ad879ba8..1c311b49 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverter.java @@ -32,10 +32,10 @@ import org.springframework.security.web.authentication.AuthenticationConverter; import org.springframework.util.MultiValueMap; /** - * Attempts to extract a user code from {@link HttpServletRequest} for the - * OAuth 2.0 Device Authorization Grant and then converts it to an - * {@link OAuth2DeviceVerificationAuthenticationToken} used for authenticating - * the request. + * Attempts to extract a user code from {@link HttpServletRequest} for the OAuth 2.0 + * Device Authorization Grant and then converts it to an + * {@link OAuth2DeviceVerificationAuthenticationToken} used for authenticating the + * request. * * @author Steve Riesenberg * @since 1.1 @@ -46,8 +46,9 @@ import org.springframework.util.MultiValueMap; public final class OAuth2DeviceVerificationAuthenticationConverter implements AuthenticationConverter { private static final String ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; - private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken( - "anonymous", "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); + + private static final Authentication ANONYMOUS_AUTHENTICATION = new AnonymousAuthenticationToken("anonymous", + "anonymousUser", AuthorityUtils.createAuthorityList("ROLE_ANONYMOUS")); @Override public Authentication convert(HttpServletRequest request) { @@ -59,19 +60,14 @@ public final class OAuth2DeviceVerificationAuthenticationConverter implements Au return null; } - MultiValueMap parameters = - "GET".equals(request.getMethod()) ? - OAuth2EndpointUtils.getQueryParameters(request) : - OAuth2EndpointUtils.getFormParameters(request); + MultiValueMap parameters = "GET".equals(request.getMethod()) + ? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request); // user_code (REQUIRED) String userCode = parameters.getFirst(OAuth2ParameterNames.USER_CODE); - if (!OAuth2EndpointUtils.validateUserCode(userCode) || - parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.USER_CODE, - ERROR_URI); + if (!OAuth2EndpointUtils.validateUserCode(userCode) + || parameters.get(OAuth2ParameterNames.USER_CODE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.USER_CODE, ERROR_URI); } Authentication principal = SecurityContextHolder.getContext().getAuthentication(); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java index 972deaa0..f58f88d6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2EndpointUtils.java @@ -39,6 +39,7 @@ import org.springframework.util.StringUtils; * @since 0.1.2 */ final class OAuth2EndpointUtils { + static final String ACCESS_TOKEN_REQUEST_ERROR_URI = "https://datatracker.ietf.org/doc/html/rfc6749#section-5.2"; private OAuth2EndpointUtils() { @@ -73,34 +74,33 @@ final class OAuth2EndpointUtils { return parameters; } - static Map getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request, String... exclusions) { + static Map getParametersIfMatchesAuthorizationCodeGrantRequest(HttpServletRequest request, + String... exclusions) { if (!matchesAuthorizationCodeGrantRequest(request)) { return Collections.emptyMap(); } - MultiValueMap multiValueParameters = - "GET".equals(request.getMethod()) ? - getQueryParameters(request) : - getFormParameters(request); + MultiValueMap multiValueParameters = "GET".equals(request.getMethod()) + ? getQueryParameters(request) : getFormParameters(request); for (String exclusion : exclusions) { multiValueParameters.remove(exclusion); } Map parameters = new HashMap<>(); - multiValueParameters.forEach((key, value) -> - parameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]))); + multiValueParameters.forEach( + (key, value) -> parameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]))); return parameters; } static boolean matchesAuthorizationCodeGrantRequest(HttpServletRequest request) { - return AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals( - request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) && - request.getParameter(OAuth2ParameterNames.CODE) != null; + return AuthorizationGrantType.AUTHORIZATION_CODE.getValue() + .equals(request.getParameter(OAuth2ParameterNames.GRANT_TYPE)) + && request.getParameter(OAuth2ParameterNames.CODE) != null; } static boolean matchesPkceTokenRequest(HttpServletRequest request) { - return matchesAuthorizationCodeGrantRequest(request) && - request.getParameter(PkceParameterNames.CODE_VERIFIER) != null; + return matchesAuthorizationCodeGrantRequest(request) + && request.getParameter(PkceParameterNames.CODE_VERIFIER) != null; } static void throwError(String errorCode, String parameterName, String errorUri) { @@ -119,4 +119,5 @@ final class OAuth2EndpointUtils { static boolean validateUserCode(String userCode) { return (userCode != null && userCode.toUpperCase().replaceAll("[^A-Z\\d]+", "").length() == 8); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java index 1b34eef8..805f0d71 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2RefreshTokenAuthenticationConverter.java @@ -36,8 +36,10 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the OAuth 2.0 Refresh Token Grant - * and then converts it to an {@link OAuth2RefreshTokenAuthenticationToken} used for authenticating the authorization grant. + * Attempts to extract an Access Token Request from {@link HttpServletRequest} for the + * OAuth 2.0 Refresh Token Grant and then converts it to an + * {@link OAuth2RefreshTokenAuthenticationToken} used for authenticating the authorization + * grant. * * @author Joe Grandja * @since 0.1.2 @@ -62,39 +64,32 @@ public final class OAuth2RefreshTokenAuthenticationConverter implements Authenti // refresh_token (REQUIRED) String refreshToken = parameters.getFirst(OAuth2ParameterNames.REFRESH_TOKEN); - if (!StringUtils.hasText(refreshToken) || - parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.REFRESH_TOKEN, + if (!StringUtils.hasText(refreshToken) || parameters.get(OAuth2ParameterNames.REFRESH_TOKEN).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REFRESH_TOKEN, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } // scope (OPTIONAL) String scope = parameters.getFirst(OAuth2ParameterNames.SCOPE); - if (StringUtils.hasText(scope) && - parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { - OAuth2EndpointUtils.throwError( - OAuth2ErrorCodes.INVALID_REQUEST, - OAuth2ParameterNames.SCOPE, + if (StringUtils.hasText(scope) && parameters.get(OAuth2ParameterNames.SCOPE).size() != 1) { + OAuth2EndpointUtils.throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.SCOPE, OAuth2EndpointUtils.ACCESS_TOKEN_REQUEST_ERROR_URI); } Set requestedScopes = null; if (StringUtils.hasText(scope)) { - requestedScopes = new HashSet<>( - Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); + requestedScopes = new HashSet<>(Arrays.asList(StringUtils.delimitedListToStringArray(scope, " "))); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && - !key.equals(OAuth2ParameterNames.REFRESH_TOKEN) && - !key.equals(OAuth2ParameterNames.SCOPE)) { + if (!key.equals(OAuth2ParameterNames.GRANT_TYPE) && !key.equals(OAuth2ParameterNames.REFRESH_TOKEN) + && !key.equals(OAuth2ParameterNames.SCOPE)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2RefreshTokenAuthenticationToken( - refreshToken, clientPrincipal, requestedScopes, additionalParameters); + return new OAuth2RefreshTokenAuthenticationToken(refreshToken, clientPrincipal, requestedScopes, + additionalParameters); } + } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java index 28dbcb0c..0f8b2625 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenIntrospectionAuthenticationConverter.java @@ -33,8 +33,9 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract an Introspection Request from {@link HttpServletRequest} - * and then converts it to an {@link OAuth2TokenIntrospectionAuthenticationToken} used for authenticating the request. + * Attempts to extract an Introspection Request from {@link HttpServletRequest} and then + * converts it to an {@link OAuth2TokenIntrospectionAuthenticationToken} used for + * authenticating the request. * * @author Gerardo Roza * @author Joe Grandja @@ -53,28 +54,25 @@ public final class OAuth2TokenIntrospectionAuthenticationConverter implements Au // token (REQUIRED) String token = parameters.getFirst(OAuth2ParameterNames.TOKEN); - if (!StringUtils.hasText(token) || - parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) { + if (!StringUtils.hasText(token) || parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN); } // token_type_hint (OPTIONAL) String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT); - if (StringUtils.hasText(tokenTypeHint) && - parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) { + if (StringUtils.hasText(tokenTypeHint) && parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT); } Map additionalParameters = new HashMap<>(); parameters.forEach((key, value) -> { - if (!key.equals(OAuth2ParameterNames.TOKEN) && - !key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) { + if (!key.equals(OAuth2ParameterNames.TOKEN) && !key.equals(OAuth2ParameterNames.TOKEN_TYPE_HINT)) { additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0])); } }); - return new OAuth2TokenIntrospectionAuthenticationToken( - token, clientPrincipal, tokenTypeHint, additionalParameters); + return new OAuth2TokenIntrospectionAuthenticationToken(token, clientPrincipal, tokenTypeHint, + additionalParameters); } private static void throwError(String errorCode, String parameterName) { diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenRevocationAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenRevocationAuthenticationConverter.java index a8765ed2..1b357a3d 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenRevocationAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2TokenRevocationAuthenticationConverter.java @@ -30,8 +30,9 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract a Revoke Token Request from {@link HttpServletRequest} - * and then converts it to an {@link OAuth2TokenRevocationAuthenticationToken} used for authenticating the request. + * Attempts to extract a Revoke Token Request from {@link HttpServletRequest} and then + * converts it to an {@link OAuth2TokenRevocationAuthenticationToken} used for + * authenticating the request. * * @author Vivek Babu * @author Joe Grandja @@ -50,15 +51,13 @@ public final class OAuth2TokenRevocationAuthenticationConverter implements Authe // token (REQUIRED) String token = parameters.getFirst(OAuth2ParameterNames.TOKEN); - if (!StringUtils.hasText(token) || - parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) { + if (!StringUtils.hasText(token) || parameters.get(OAuth2ParameterNames.TOKEN).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN); } // token_type_hint (OPTIONAL) String tokenTypeHint = parameters.getFirst(OAuth2ParameterNames.TOKEN_TYPE_HINT); - if (StringUtils.hasText(tokenTypeHint) && - parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) { + if (StringUtils.hasText(tokenTypeHint) && parameters.get(OAuth2ParameterNames.TOKEN_TYPE_HINT).size() != 1) { throwError(OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.TOKEN_TYPE_HINT); } diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverter.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverter.java index 2fda1393..0fdc56c6 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverter.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverter.java @@ -34,15 +34,16 @@ import org.springframework.util.MultiValueMap; import org.springframework.util.StringUtils; /** - * Attempts to extract the parameters from {@link HttpServletRequest} - * used for authenticating public clients using Proof Key for Code Exchange (PKCE). + * Attempts to extract the parameters from {@link HttpServletRequest} used for + * authenticating public clients using Proof Key for Code Exchange (PKCE). * * @author Joe Grandja * @since 0.0.2 * @see AuthenticationConverter * @see OAuth2ClientAuthenticationToken * @see OAuth2ClientAuthenticationFilter - * @see Proof Key for Code Exchange by OAuth Public Clients + * @see Proof Key for Code + * Exchange by OAuth Public Clients */ public final class PublicClientAuthenticationConverter implements AuthenticationConverter { @@ -53,15 +54,12 @@ public final class PublicClientAuthenticationConverter implements Authentication return null; } - MultiValueMap parameters = - "GET".equals(request.getMethod()) ? - OAuth2EndpointUtils.getQueryParameters(request) : - OAuth2EndpointUtils.getFormParameters(request); + MultiValueMap parameters = "GET".equals(request.getMethod()) + ? OAuth2EndpointUtils.getQueryParameters(request) : OAuth2EndpointUtils.getFormParameters(request); // client_id (REQUIRED for public clients) String clientId = parameters.getFirst(OAuth2ParameterNames.CLIENT_ID); - if (!StringUtils.hasText(clientId) || - parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { + if (!StringUtils.hasText(clientId) || parameters.get(OAuth2ParameterNames.CLIENT_ID).size() != 1) { throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); } @@ -73,10 +71,11 @@ public final class PublicClientAuthenticationConverter implements Authentication parameters.remove(OAuth2ParameterNames.CLIENT_ID); Map additionalParameters = new HashMap<>(); - parameters.forEach((key, value) -> - additionalParameters.put(key, (value.size() == 1) ? value.get(0) : value.toArray(new String[0]))); + parameters.forEach((key, value) -> additionalParameters.put(key, + (value.size() == 1) ? value.get(0) : value.toArray(new String[0]))); return new OAuth2ClientAuthenticationToken(clientId, ClientAuthenticationMethod.NONE, null, additionalParameters); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java index 0a53411b..e75b6d45 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jose/TestJwks.java @@ -41,7 +41,8 @@ public final class TestJwks { try { rsaKeyPairGenerator = KeyPairGenerator.getInstance("RSA"); rsaKeyPairGenerator.initialize(2048); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalStateException(ex); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java index da0939b6..a41b0833 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwsHeaders.java @@ -56,4 +56,5 @@ public final class TestJwsHeaders { rsaJwk.put("e", "exponent"); return rsaJwk; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java index 6e09c3f9..2dc5a8a4 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/jwt/TestJwtClaimsSets.java @@ -44,4 +44,5 @@ public final class TestJwtClaimsSets { .claim("custom-claim-name", "custom-claim-value"); // @formatter:on } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentServiceTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentServiceTests.java index 732263d2..a9fefc70 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentServiceTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationConsentServiceTests.java @@ -31,12 +31,15 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Daniel Garnier-Moiroux */ public class InMemoryOAuth2AuthorizationConsentServiceTests { + private static final String REGISTERED_CLIENT_ID = "registered-client-id"; + private static final String PRINCIPAL_NAME = "principal-name"; - private static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = - OAuth2AuthorizationConsent.withId(REGISTERED_CLIENT_ID, PRINCIPAL_NAME) - .authority(new SimpleGrantedAuthority("some.authority")) - .build(); + + private static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = OAuth2AuthorizationConsent + .withId(REGISTERED_CLIENT_ID, PRINCIPAL_NAME) + .authority(new SimpleGrantedAuthority("some.authority")) + .build(); private InMemoryOAuth2AuthorizationConsentService authorizationConsentService; @@ -49,94 +52,92 @@ public class InMemoryOAuth2AuthorizationConsentServiceTests { @Test public void constructorVarargsWhenAuthorizationConsentNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((OAuth2AuthorizationConsent) null)) - .withMessage("authorizationConsent cannot be null"); + .isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((OAuth2AuthorizationConsent) null)) + .withMessage("authorizationConsent cannot be null"); } @Test public void constructorListWhenAuthorizationConsentsNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((List) null)) - .withMessage("authorizationConsents cannot be null"); + .isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService((List) null)) + .withMessage("authorizationConsents cannot be null"); } @Test public void constructorWhenDuplicateAuthorizationConsentsThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new InMemoryOAuth2AuthorizationConsentService(AUTHORIZATION_CONSENT, AUTHORIZATION_CONSENT)) - .withMessage("The authorizationConsent must be unique. Found duplicate, with registered client id: [registered-client-id] and principal name: [principal-name]"); + .isThrownBy( + () -> new InMemoryOAuth2AuthorizationConsentService(AUTHORIZATION_CONSENT, AUTHORIZATION_CONSENT)) + .withMessage( + "The authorizationConsent must be unique. Found duplicate, with registered client id: [registered-client-id] and principal name: [principal-name]"); } @Test public void saveWhenAuthorizationConsentNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.save(null)) - .withMessage("authorizationConsent cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.save(null)) + .withMessage("authorizationConsent cannot be null"); } @Test public void saveWhenAuthorizationConsentNewThenSaved() { - OAuth2AuthorizationConsent expectedAuthorizationConsent = - OAuth2AuthorizationConsent.withId("new-client", "new-principal") - .authority(new SimpleGrantedAuthority("new.authority")) - .build(); + OAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent + .withId("new-client", "new-principal") + .authority(new SimpleGrantedAuthority("new.authority")) + .build(); this.authorizationConsentService.save(expectedAuthorizationConsent); - OAuth2AuthorizationConsent authorizationConsent = - this.authorizationConsentService.findById("new-client", "new-principal"); + OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById("new-client", + "new-principal"); assertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent); } @Test public void saveWhenAuthorizationConsentExistsThenUpdated() { - OAuth2AuthorizationConsent expectedAuthorizationConsent = - OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT) - .authority(new SimpleGrantedAuthority("new.authority")) - .build(); + OAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT) + .authority(new SimpleGrantedAuthority("new.authority")) + .build(); this.authorizationConsentService.save(expectedAuthorizationConsent); - OAuth2AuthorizationConsent authorizationConsent = - this.authorizationConsentService.findById( - AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName()); + OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService + .findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName()); assertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent); assertThat(authorizationConsent).isNotEqualTo(AUTHORIZATION_CONSENT); } @Test public void removeWhenAuthorizationConsentNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.remove(null)) - .withMessage("authorizationConsent cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.remove(null)) + .withMessage("authorizationConsent cannot be null"); } @Test public void removeWhenAuthorizationConsentProvidedThenRemoved() { this.authorizationConsentService.remove(AUTHORIZATION_CONSENT); - assertThat(this.authorizationConsentService.findById( - AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName())) - .isNull(); + assertThat(this.authorizationConsentService.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), + AUTHORIZATION_CONSENT.getPrincipalName())) + .isNull(); } @Test public void findByIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.findById(null, "some-user")) - .withMessage("registeredClientId cannot be empty"); + .isThrownBy(() -> this.authorizationConsentService.findById(null, "some-user")) + .withMessage("registeredClientId cannot be empty"); } @Test public void findByIdWhenPrincipalNameNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.findById("some-client", null)) - .withMessage("principalName cannot be empty"); + .isThrownBy(() -> this.authorizationConsentService.findById("some-client", null)) + .withMessage("principalName cannot be empty"); } @Test public void findByIdWhenAuthorizationConsentExistsThenFound() { assertThat(this.authorizationConsentService.findById(REGISTERED_CLIENT_ID, PRINCIPAL_NAME)) - .isEqualTo(AUTHORIZATION_CONSENT); + .isEqualTo(AUTHORIZATION_CONSENT); } @Test @@ -145,4 +146,5 @@ public class InMemoryOAuth2AuthorizationConsentServiceTests { assertThat(this.authorizationConsentService.findById("unknown-client", PRINCIPAL_NAME)).isNull(); assertThat(this.authorizationConsentService.findById(REGISTERED_CLIENT_ID, "unknown-user")).isNull(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationServiceTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationServiceTests.java index 83c3049a..7ea97e89 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationServiceTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/InMemoryOAuth2AuthorizationServiceTests.java @@ -41,15 +41,24 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class InMemoryOAuth2AuthorizationServiceTests { + private static final String ID = "id"; + private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build(); + private static final String PRINCIPAL_NAME = "principal"; + private static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE; - private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES)); + + private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plus(5, ChronoUnit.MINUTES)); + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private InMemoryOAuth2AuthorizationService authorizationService; @BeforeEach @@ -60,50 +69,49 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void constructorVarargsWhenAuthorizationNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new InMemoryOAuth2AuthorizationService((OAuth2Authorization) null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorization cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorization cannot be null"); } @Test public void constructorListWhenAuthorizationsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new InMemoryOAuth2AuthorizationService((List) null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizations cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizations cannot be null"); } @Test public void constructorWhenDuplicateAuthorizationsThenThrowIllegalArgumentException() { OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .build(); assertThatThrownBy(() -> new InMemoryOAuth2AuthorizationService(authorization, authorization)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("The authorization must be unique. Found duplicate identifier: id"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("The authorization must be unique. Found duplicate identifier: id"); } @Test public void saveWhenAuthorizationNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> this.authorizationService.save(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorization cannot be null"); + assertThatThrownBy(() -> this.authorizationService.save(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorization cannot be null"); } @Test public void saveWhenAuthorizationNewThenSaved() { OAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .build(); this.authorizationService.save(expectedAuthorization); - OAuth2Authorization authorization = this.authorizationService.findByToken( - AUTHORIZATION_CODE.getTokenValue(), AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorization).isEqualTo(expectedAuthorization); } @@ -111,24 +119,22 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void saveWhenAuthorizationExistsThenUpdated() { OAuth2Authorization originalAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .build(); this.authorizationService.save(originalAuthorization); - OAuth2Authorization authorization = this.authorizationService.findById( - originalAuthorization.getId()); + OAuth2Authorization authorization = this.authorizationService.findById(originalAuthorization.getId()); assertThat(authorization).isEqualTo(originalAuthorization); OAuth2Authorization updatedAuthorization = OAuth2Authorization.from(authorization) - .attribute("custom-name-1", "custom-value-1") - .build(); + .attribute("custom-name-1", "custom-value-1") + .build(); this.authorizationService.save(updatedAuthorization); - authorization = this.authorizationService.findById( - updatedAuthorization.getId()); + authorization = this.authorizationService.findById(updatedAuthorization.getId()); assertThat(authorization).isEqualTo(updatedAuthorization); assertThat(authorization).isNotEqualTo(originalAuthorization); } @@ -136,94 +142,91 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void saveWhenInitializedAuthorizationsReachMaxThenOldestRemoved() { int maxInitializedAuthorizations = 5; - InMemoryOAuth2AuthorizationService authorizationService = - new InMemoryOAuth2AuthorizationService(maxInitializedAuthorizations); + InMemoryOAuth2AuthorizationService authorizationService = new InMemoryOAuth2AuthorizationService( + maxInitializedAuthorizations); OAuth2Authorization initialAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID + "-initial") - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .attribute(OAuth2ParameterNames.STATE, "state-initial") - .build(); + .id(ID + "-initial") + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .attribute(OAuth2ParameterNames.STATE, "state-initial") + .build(); authorizationService.save(initialAuthorization); OAuth2Authorization authorization = authorizationService.findById(initialAuthorization.getId()); assertThat(authorization).isEqualTo(initialAuthorization); - authorization = authorizationService.findByToken( - initialAuthorization.getAttribute(OAuth2ParameterNames.STATE), STATE_TOKEN_TYPE); + authorization = authorizationService.findByToken(initialAuthorization.getAttribute(OAuth2ParameterNames.STATE), + STATE_TOKEN_TYPE); assertThat(authorization).isEqualTo(initialAuthorization); for (int i = 0; i < maxInitializedAuthorizations; i++) { authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID + "-" + i) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .attribute(OAuth2ParameterNames.STATE, "state-" + i) - .build(); + .id(ID + "-" + i) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .attribute(OAuth2ParameterNames.STATE, "state-" + i) + .build(); authorizationService.save(authorization); } authorization = authorizationService.findById(initialAuthorization.getId()); assertThat(authorization).isNull(); - authorization = authorizationService.findByToken( - initialAuthorization.getAttribute(OAuth2ParameterNames.STATE), STATE_TOKEN_TYPE); + authorization = authorizationService.findByToken(initialAuthorization.getAttribute(OAuth2ParameterNames.STATE), + STATE_TOKEN_TYPE); assertThat(authorization).isNull(); } @Test public void removeWhenAuthorizationNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> this.authorizationService.remove(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorization cannot be null"); + assertThatThrownBy(() -> this.authorizationService.remove(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorization cannot be null"); } @Test public void removeWhenAuthorizationProvidedThenRemoved() { OAuth2Authorization expectedAuthorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .build(); this.authorizationService.save(expectedAuthorization); - OAuth2Authorization authorization = this.authorizationService.findByToken( - AUTHORIZATION_CODE.getTokenValue(), AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorization).isEqualTo(expectedAuthorization); this.authorizationService.remove(expectedAuthorization); - authorization = this.authorizationService.findByToken( - AUTHORIZATION_CODE.getTokenValue(), AUTHORIZATION_CODE_TOKEN_TYPE); + authorization = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorization).isNull(); } @Test public void findByIdWhenIdNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> this.authorizationService.findById(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("id cannot be empty"); + assertThatThrownBy(() -> this.authorizationService.findById(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("id cannot be empty"); } @Test public void findByTokenWhenTokenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authorizationService.findByToken(null, AUTHORIZATION_CODE_TOKEN_TYPE)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("token cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("token cannot be empty"); } @Test public void findByTokenWhenStateExistsThenFound() { String state = "state"; OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .attribute(OAuth2ParameterNames.STATE, state) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .attribute(OAuth2ParameterNames.STATE, state) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - state, STATE_TOKEN_TYPE); + OAuth2Authorization result = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE); assertThat(authorization).isEqualTo(result); result = this.authorizationService.findByToken(state, null); assertThat(authorization).isEqualTo(result); @@ -232,15 +235,15 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void findByTokenWhenAuthorizationCodeExistsThenFound() { OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - AUTHORIZATION_CODE.getTokenValue(), AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization result = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorization).isEqualTo(result); result = this.authorizationService.findByToken(AUTHORIZATION_CODE.getTokenValue(), null); assertThat(authorization).isEqualTo(result); @@ -248,19 +251,19 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void findByTokenWhenAccessTokenExistsThenFound() { - OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - "access-token", Instant.now().minusSeconds(60), Instant.now()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + Instant.now().minusSeconds(60), Instant.now()); OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .accessToken(accessToken) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .accessToken(accessToken) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken(accessToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN); assertThat(authorization).isEqualTo(result); result = this.authorizationService.findByToken(accessToken.getTokenValue(), null); assertThat(authorization).isEqualTo(result); @@ -268,22 +271,22 @@ public class InMemoryOAuth2AuthorizationServiceTests { @Test public void findByTokenWhenIdTokenExistsThenFound() { - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject("subject") - .issuedAt(Instant.now().minusSeconds(60)) - .expiresAt(Instant.now()) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject("subject") + .issuedAt(Instant.now().minusSeconds(60)) + .expiresAt(Instant.now()) + .build(); OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(idToken) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(idToken) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - idToken.getTokenValue(), ID_TOKEN_TOKEN_TYPE); + OAuth2Authorization result = this.authorizationService.findByToken(idToken.getTokenValue(), + ID_TOKEN_TOKEN_TYPE); assertThat(authorization).isEqualTo(result); result = this.authorizationService.findByToken(idToken.getTokenValue(), null); assertThat(authorization).isEqualTo(result); @@ -293,15 +296,15 @@ public class InMemoryOAuth2AuthorizationServiceTests { public void findByTokenWhenRefreshTokenExistsThenFound() { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now()); OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .refreshToken(refreshToken) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .refreshToken(refreshToken) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - refreshToken.getTokenValue(), OAuth2TokenType.REFRESH_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(), + OAuth2TokenType.REFRESH_TOKEN); assertThat(authorization).isEqualTo(result); result = this.authorizationService.findByToken(refreshToken.getTokenValue(), null); assertThat(authorization).isEqualTo(result); @@ -311,22 +314,23 @@ public class InMemoryOAuth2AuthorizationServiceTests { public void findByTokenWhenWrongTokenTypeThenNotFound() { OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now()); OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .refreshToken(refreshToken) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .refreshToken(refreshToken) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - refreshToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN); assertThat(result).isNull(); } @Test public void findByTokenWhenTokenDoesNotExistThenNull() { - OAuth2Authorization result = this.authorizationService.findByToken( - "access-token", OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken("access-token", + OAuth2TokenType.ACCESS_TOKEN); assertThat(result).isNull(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentServiceTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentServiceTests.java index d4776ab2..de6c4868 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentServiceTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationConsentServiceTests.java @@ -56,23 +56,30 @@ import static org.mockito.Mockito.when; * @author Ovidiu Popa */ public class JdbcOAuth2AuthorizationConsentServiceTests { + private static final String OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql"; + private static final String CUSTOM_OAUTH2_AUTHORIZATION_CONSENT_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-consent-schema.sql"; + private static final String PRINCIPAL_NAME = "principal-name"; + private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build(); - private static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = - OAuth2AuthorizationConsent.withId(REGISTERED_CLIENT.getId(), PRINCIPAL_NAME) - .authority(new SimpleGrantedAuthority("SCOPE_scope1")) - .authority(new SimpleGrantedAuthority("SCOPE_scope2")) - .authority(new SimpleGrantedAuthority("SCOPE_scope3")) - .authority(new SimpleGrantedAuthority("authority-a")) - .authority(new SimpleGrantedAuthority("authority-b")) - .build(); + private static final OAuth2AuthorizationConsent AUTHORIZATION_CONSENT = OAuth2AuthorizationConsent + .withId(REGISTERED_CLIENT.getId(), PRINCIPAL_NAME) + .authority(new SimpleGrantedAuthority("SCOPE_scope1")) + .authority(new SimpleGrantedAuthority("SCOPE_scope2")) + .authority(new SimpleGrantedAuthority("SCOPE_scope3")) + .authority(new SimpleGrantedAuthority("authority-a")) + .authority(new SimpleGrantedAuthority("authority-b")) + .build(); private EmbeddedDatabase db; + private JdbcOperations jdbcOperations; + private RegisteredClientRepository registeredClientRepository; + private JdbcOAuth2AuthorizationConsentService authorizationConsentService; @BeforeEach @@ -80,7 +87,8 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { this.db = createDb(); this.jdbcOperations = new JdbcTemplate(this.db); this.registeredClientRepository = mock(RegisteredClientRepository.class); - this.authorizationConsentService = new JdbcOAuth2AuthorizationConsentService(this.jdbcOperations, this.registeredClientRepository); + this.authorizationConsentService = new JdbcOAuth2AuthorizationConsentService(this.jdbcOperations, + this.registeredClientRepository); } @AfterEach @@ -135,38 +143,33 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { @Test public void saveWhenAuthorizationConsentNewThenSaved() { - OAuth2AuthorizationConsent expectedAuthorizationConsent = - OAuth2AuthorizationConsent.withId("new-client", "new-principal") - .authority(new SimpleGrantedAuthority("new.authority")) - .build(); + OAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent + .withId("new-client", "new-principal") + .authority(new SimpleGrantedAuthority("new.authority")) + .build(); - RegisteredClient newRegisteredClient = TestRegisteredClients.registeredClient() - .id("new-client").build(); + RegisteredClient newRegisteredClient = TestRegisteredClients.registeredClient().id("new-client").build(); - when(this.registeredClientRepository.findById(eq(newRegisteredClient.getId()))) - .thenReturn(newRegisteredClient); + when(this.registeredClientRepository.findById(eq(newRegisteredClient.getId()))).thenReturn(newRegisteredClient); this.authorizationConsentService.save(expectedAuthorizationConsent); - OAuth2AuthorizationConsent authorizationConsent = - this.authorizationConsentService.findById("new-client", "new-principal"); + OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById("new-client", + "new-principal"); assertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent); } @Test public void saveWhenAuthorizationConsentExistsThenUpdated() { - OAuth2AuthorizationConsent expectedAuthorizationConsent = - OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT) - .authority(new SimpleGrantedAuthority("new.authority")) - .build(); - when(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))) - .thenReturn(REGISTERED_CLIENT); + OAuth2AuthorizationConsent expectedAuthorizationConsent = OAuth2AuthorizationConsent.from(AUTHORIZATION_CONSENT) + .authority(new SimpleGrantedAuthority("new.authority")) + .build(); + when(this.registeredClientRepository.findById(eq(REGISTERED_CLIENT.getId()))).thenReturn(REGISTERED_CLIENT); this.authorizationConsentService.save(expectedAuthorizationConsent); - OAuth2AuthorizationConsent authorizationConsent = - this.authorizationConsentService.findById( - AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName()); + OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService + .findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName()); assertThat(authorizationConsent).isEqualTo(expectedAuthorizationConsent); assertThat(authorizationConsent).isNotEqualTo(AUTHORIZATION_CONSENT); } @@ -194,31 +197,30 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { @Test public void removeWhenAuthorizationConsentNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.remove(null)) - .withMessage("authorizationConsent cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.authorizationConsentService.remove(null)) + .withMessage("authorizationConsent cannot be null"); } @Test public void removeWhenAuthorizationConsentProvidedThenRemoved() { this.authorizationConsentService.remove(AUTHORIZATION_CONSENT); - assertThat(this.authorizationConsentService.findById( - AUTHORIZATION_CONSENT.getRegisteredClientId(), AUTHORIZATION_CONSENT.getPrincipalName())) - .isNull(); + assertThat(this.authorizationConsentService.findById(AUTHORIZATION_CONSENT.getRegisteredClientId(), + AUTHORIZATION_CONSENT.getPrincipalName())) + .isNull(); } @Test public void findByIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.findById(null, "some-user")) - .withMessage("registeredClientId cannot be empty"); + .isThrownBy(() -> this.authorizationConsentService.findById(null, "some-user")) + .withMessage("registeredClientId cannot be empty"); } @Test public void findByIdWhenPrincipalNameNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.authorizationConsentService.findById("some-client", null)) - .withMessage("principalName cannot be empty"); + .isThrownBy(() -> this.authorizationConsentService.findById("some-client", null)) + .withMessage("principalName cannot be empty"); } @Test @@ -273,7 +275,8 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { // @formatter:on } - private static final class CustomJdbcOAuth2AuthorizationConsentService extends JdbcOAuth2AuthorizationConsentService { + private static final class CustomJdbcOAuth2AuthorizationConsentService + extends JdbcOAuth2AuthorizationConsentService { // @formatter:off private static final String COLUMN_NAMES = "registeredClientId, " @@ -296,9 +299,11 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { + " (" + COLUMN_NAMES + ") VALUES (?, ?, ?)"; // @formatter:on - private static final String REMOVE_AUTHORIZATION_CONSENT_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + PK_FILTER; + private static final String REMOVE_AUTHORIZATION_CONSENT_SQL = "DELETE FROM " + TABLE_NAME + " WHERE " + + PK_FILTER; - private CustomJdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { + private CustomJdbcOAuth2AuthorizationConsentService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { super(jdbcOperations, registeredClientRepository); setAuthorizationConsentRowMapper(new CustomOAuth2AuthorizationConsentRowMapper(registeredClientRepository)); } @@ -314,8 +319,7 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { public void remove(OAuth2AuthorizationConsent authorizationConsent) { SqlParameterValue[] parameters = new SqlParameterValue[] { new SqlParameterValue(Types.VARCHAR, authorizationConsent.getRegisteredClientId()), - new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) - }; + new SqlParameterValue(Types.VARCHAR, authorizationConsent.getPrincipalName()) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); getJdbcOperations().update(REMOVE_AUTHORIZATION_CONSENT_SQL, pss); } @@ -324,14 +328,15 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { public OAuth2AuthorizationConsent findById(String registeredClientId, String principalName) { SqlParameterValue[] parameters = new SqlParameterValue[] { new SqlParameterValue(Types.VARCHAR, registeredClientId), - new SqlParameterValue(Types.VARCHAR, principalName)}; + new SqlParameterValue(Types.VARCHAR, principalName) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); List result = getJdbcOperations().query(LOAD_AUTHORIZATION_CONSENT_SQL, pss, getAuthorizationConsentRowMapper()); return !result.isEmpty() ? result.get(0) : null; } - private static final class CustomOAuth2AuthorizationConsentRowMapper extends JdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentRowMapper { + private static final class CustomOAuth2AuthorizationConsentRowMapper + extends JdbcOAuth2AuthorizationConsentService.OAuth2AuthorizationConsentRowMapper { private CustomOAuth2AuthorizationConsentRowMapper(RegisteredClientRepository registeredClientRepository) { super(registeredClientRepository); @@ -342,13 +347,14 @@ public class JdbcOAuth2AuthorizationConsentServiceTests { String registeredClientId = rs.getString("registeredClientId"); RegisteredClient registeredClient = getRegisteredClientRepository().findById(registeredClientId); if (registeredClient == null) { - throw new DataRetrievalFailureException( - "The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository."); + throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId + + "' was not found in the RegisteredClientRepository."); } String principalName = rs.getString("principalName"); - OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, principalName); + OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClientId, + principalName); String authorizationConsentAuthorities = rs.getString("authorities"); if (authorizationConsentAuthorities != null) { for (String authority : StringUtils.commaDelimitedListToSet(authorizationConsentAuthorities)) { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java index 4321aace..37052d06 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/JdbcOAuth2AuthorizationServiceTests.java @@ -75,24 +75,41 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class JdbcOAuth2AuthorizationServiceTests { + private static final String OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql"; + private static final String CUSTOM_OAUTH2_AUTHORIZATION_SCHEMA_SQL_RESOURCE = "org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema.sql"; + private static final String OAUTH2_AUTHORIZATION_SCHEMA_CLOB_DATA_TYPE_SQL_RESOURCE = "org/springframework/security/oauth2/server/authorization/custom-oauth2-authorization-schema-clob-data-type.sql"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private static final OAuth2TokenType USER_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.USER_CODE); + private static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE); + private static final String ID = "id"; + private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build(); + private static final String PRINCIPAL_NAME = "principal"; + private static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE; - private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode( - "code", Instant.now().truncatedTo(ChronoUnit.MILLIS), Instant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS)); + + private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode("code", + Instant.now().truncatedTo(ChronoUnit.MILLIS), + Instant.now().plus(5, ChronoUnit.MINUTES).truncatedTo(ChronoUnit.MILLIS)); private EmbeddedDatabase db; + private JdbcOperations jdbcOperations; + private RegisteredClientRepository registeredClientRepository; + private JdbcOAuth2AuthorizationService authorizationService; @BeforeEach @@ -100,7 +117,8 @@ public class JdbcOAuth2AuthorizationServiceTests { this.db = createDb(); this.jdbcOperations = new JdbcTemplate(this.db); this.registeredClientRepository = mock(RegisteredClientRepository.class); - this.authorizationService = new JdbcOAuth2AuthorizationService(this.jdbcOperations, this.registeredClientRepository); + this.authorizationService = new JdbcOAuth2AuthorizationService(this.jdbcOperations, + this.registeredClientRepository); } @AfterEach @@ -445,24 +463,25 @@ public class JdbcOAuth2AuthorizationServiceTests { @Test public void findByTokenWhenWrongTokenTypeThenNotFound() { - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now().truncatedTo(ChronoUnit.MILLIS)); + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", + Instant.now().truncatedTo(ChronoUnit.MILLIS)); OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .refreshToken(refreshToken) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .refreshToken(refreshToken) + .build(); this.authorizationService.save(authorization); - OAuth2Authorization result = this.authorizationService.findByToken( - refreshToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken(refreshToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN); assertThat(result).isNull(); } @Test public void findByTokenWhenTokenDoesNotExistThenNull() { - OAuth2Authorization result = this.authorizationService.findByToken( - "access-token", OAuth2TokenType.ACCESS_TOKEN); + OAuth2Authorization result = this.authorizationService.findByToken("access-token", + OAuth2TokenType.ACCESS_TOKEN); assertThat(result).isNull(); } @@ -578,9 +597,10 @@ public class JdbcOAuth2AuthorizationServiceTests { private static final String TABLE_NAME = "oauth2Authorization"; private static final String PK_FILTER = "id = ?"; - private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorizationCodeValue = ? OR " + - "accessTokenValue = ? OR oidcIdTokenValue = ? OR refreshTokenValue = ? OR userCodeValue = ? OR " + - "deviceCodeValue = ?"; + + private static final String UNKNOWN_TOKEN_TYPE_FILTER = "state = ? OR authorizationCodeValue = ? OR " + + "accessTokenValue = ? OR oidcIdTokenValue = ? OR refreshTokenValue = ? OR userCodeValue = ? OR " + + "deviceCodeValue = ?"; // @formatter:off private static final String LOAD_AUTHORIZATION_SQL = "SELECT " + COLUMN_NAMES @@ -612,8 +632,7 @@ public class JdbcOAuth2AuthorizationServiceTests { @Override public void remove(OAuth2Authorization authorization) { SqlParameterValue[] parameters = new SqlParameterValue[] { - new SqlParameterValue(Types.VARCHAR, authorization.getId()) - }; + new SqlParameterValue(Types.VARCHAR, authorization.getId()) }; PreparedStatementSetter pss = new ArgumentPreparedStatementSetter(parameters); getJdbcOperations().update(REMOVE_AUTHORIZATION_SQL, pss); } @@ -629,12 +648,13 @@ public class JdbcOAuth2AuthorizationServiceTests { } private OAuth2Authorization findBy(String filter, Object... args) { - List result = getJdbcOperations() - .query(LOAD_AUTHORIZATION_SQL + filter, getAuthorizationRowMapper(), args); + List result = getJdbcOperations().query(LOAD_AUTHORIZATION_SQL + filter, + getAuthorizationRowMapper(), args); return !result.isEmpty() ? result.get(0) : null; } - private static final class CustomOAuth2AuthorizationRowMapper extends JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper { + private static final class CustomOAuth2AuthorizationRowMapper + extends JdbcOAuth2AuthorizationService.OAuth2AuthorizationRowMapper { private CustomOAuth2AuthorizationRowMapper(RegisteredClientRepository registeredClientRepository) { super(registeredClientRepository); @@ -646,8 +666,8 @@ public class JdbcOAuth2AuthorizationServiceTests { String registeredClientId = rs.getString("registeredClientId"); RegisteredClient registeredClient = getRegisteredClientRepository().findById(registeredClientId); if (registeredClient == null) { - throw new DataRetrievalFailureException( - "The RegisteredClient with id '" + registeredClientId + "' was not found in the RegisteredClientRepository."); + throw new DataRetrievalFailureException("The RegisteredClient with id '" + registeredClientId + + "' was not found in the RegisteredClientRepository."); } OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient); @@ -662,10 +682,10 @@ public class JdbcOAuth2AuthorizationServiceTests { Map attributes = parseMap(rs.getString("attributes")); builder.id(id) - .principalName(principalName) - .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType)) - .authorizedScopes(authorizedScopes) - .attributes((attrs) -> attrs.putAll(attributes)); + .principalName(principalName) + .authorizationGrantType(new AuthorizationGrantType(authorizationGrantType)) + .authorizedScopes(authorizedScopes) + .attributes((attrs) -> attrs.putAll(attributes)); String state = rs.getString("state"); if (StringUtils.hasText(state)) { @@ -680,8 +700,8 @@ public class JdbcOAuth2AuthorizationServiceTests { tokenExpiresAt = rs.getTimestamp("authorizationCodeExpiresAt").toInstant(); Map authorizationCodeMetadata = parseMap(rs.getString("authorizationCodeMetadata")); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - tokenValue, tokenIssuedAt, tokenExpiresAt); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(tokenValue, tokenIssuedAt, + tokenExpiresAt); builder.token(authorizationCode, (metadata) -> metadata.putAll(authorizationCodeMetadata)); } @@ -691,7 +711,8 @@ public class JdbcOAuth2AuthorizationServiceTests { tokenExpiresAt = rs.getTimestamp("accessTokenExpiresAt").toInstant(); Map accessTokenMetadata = parseMap(rs.getString("accessTokenMetadata")); OAuth2AccessToken.TokenType tokenType = null; - if (OAuth2AccessToken.TokenType.BEARER.getValue().equalsIgnoreCase(rs.getString("accessTokenType"))) { + if (OAuth2AccessToken.TokenType.BEARER.getValue() + .equalsIgnoreCase(rs.getString("accessTokenType"))) { tokenType = OAuth2AccessToken.TokenType.BEARER; } @@ -700,7 +721,8 @@ public class JdbcOAuth2AuthorizationServiceTests { if (accessTokenScopes != null) { scopes = StringUtils.commaDelimitedListToSet(accessTokenScopes); } - OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, tokenIssuedAt, tokenExpiresAt, scopes); + OAuth2AccessToken accessToken = new OAuth2AccessToken(tokenType, tokenValue, tokenIssuedAt, + tokenExpiresAt, scopes); builder.token(accessToken, (metadata) -> metadata.putAll(accessTokenMetadata)); } @@ -710,8 +732,9 @@ public class JdbcOAuth2AuthorizationServiceTests { tokenExpiresAt = rs.getTimestamp("oidcIdTokenExpiresAt").toInstant(); Map oidcTokenMetadata = parseMap(rs.getString("oidcIdTokenMetadata")); - OidcIdToken oidcToken = new OidcIdToken( - tokenValue, tokenIssuedAt, tokenExpiresAt, (Map) oidcTokenMetadata.get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)); + OidcIdToken oidcToken = new OidcIdToken(tokenValue, tokenIssuedAt, tokenExpiresAt, + (Map) oidcTokenMetadata + .get(OAuth2Authorization.Token.CLAIMS_METADATA_NAME)); builder.token(oidcToken, (metadata) -> metadata.putAll(oidcTokenMetadata)); } @@ -725,8 +748,7 @@ public class JdbcOAuth2AuthorizationServiceTests { } Map refreshTokenMetadata = parseMap(rs.getString("refreshTokenMetadata")); - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - tokenValue, tokenIssuedAt, tokenExpiresAt); + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken(tokenValue, tokenIssuedAt, tokenExpiresAt); builder.token(refreshToken, (metadata) -> metadata.putAll(refreshTokenMetadata)); } @@ -755,15 +777,18 @@ public class JdbcOAuth2AuthorizationServiceTests { private Map parseMap(String data) { try { - return getObjectMapper().readValue(data, new TypeReference>() {}); - } catch (Exception ex) { + return getObjectMapper().readValue(data, new TypeReference>() { + }); + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } } - private static final class CustomOAuth2AuthorizationParametersMapper extends JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper { + private static final class CustomOAuth2AuthorizationParametersMapper + extends JdbcOAuth2AuthorizationService.OAuth2AuthorizationParametersMapper { @Override public List apply(OAuth2Authorization authorization) { @@ -771,11 +796,13 @@ public class JdbcOAuth2AuthorizationServiceTests { parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getId())); parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getRegisteredClientId())); parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getPrincipalName())); - parameters.add(new SqlParameterValue(Types.VARCHAR, authorization.getAuthorizationGrantType().getValue())); + parameters + .add(new SqlParameterValue(Types.VARCHAR, authorization.getAuthorizationGrantType().getValue())); String authorizedScopes = null; if (!CollectionUtils.isEmpty(authorization.getAuthorizedScopes())) { - authorizedScopes = StringUtils.collectionToDelimitedString(authorization.getAuthorizedScopes(), ","); + authorizedScopes = StringUtils.collectionToDelimitedString(authorization.getAuthorizedScopes(), + ","); } parameters.add(new SqlParameterValue(Types.VARCHAR, authorizedScopes)); @@ -789,13 +816,13 @@ public class JdbcOAuth2AuthorizationServiceTests { } parameters.add(new SqlParameterValue(Types.VARCHAR, state)); - OAuth2Authorization.Token authorizationCode = - authorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); List authorizationCodeSqlParameters = toSqlParameterList(authorizationCode); parameters.addAll(authorizationCodeSqlParameters); - OAuth2Authorization.Token accessToken = - authorization.getToken(OAuth2AccessToken.class); + OAuth2Authorization.Token accessToken = authorization + .getToken(OAuth2AccessToken.class); List accessTokenSqlParameters = toSqlParameterList(accessToken); parameters.addAll(accessTokenSqlParameters); String accessTokenType = null; @@ -803,7 +830,8 @@ public class JdbcOAuth2AuthorizationServiceTests { if (accessToken != null) { accessTokenType = accessToken.getToken().getTokenType().getValue(); if (!CollectionUtils.isEmpty(accessToken.getToken().getScopes())) { - accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), ","); + accessTokenScopes = StringUtils.collectionToDelimitedString(accessToken.getToken().getScopes(), + ","); } } parameters.add(new SqlParameterValue(Types.VARCHAR, accessTokenType)); @@ -828,7 +856,8 @@ public class JdbcOAuth2AuthorizationServiceTests { return parameters; } - private List toSqlParameterList(OAuth2Authorization.Token token) { + private List toSqlParameterList( + OAuth2Authorization.Token token) { List parameters = new ArrayList<>(); String tokenValue = null; Timestamp tokenIssuedAt = null; @@ -854,7 +883,8 @@ public class JdbcOAuth2AuthorizationServiceTests { private String writeMap(Map data) { try { return getObjectMapper().writeValueAsString(data); - } catch (Exception ex) { + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentTests.java index efe37961..53080165 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationConsentTests.java @@ -31,92 +31,75 @@ public class OAuth2AuthorizationConsentTests { @Test public void fromWhenAuthorizationConsentNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationConsent.from(null)) - .withMessage("authorizationConsent cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.from(null)) + .withMessage("authorizationConsent cannot be null"); } @Test public void withIdWhenRegisteredClientIdNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationConsent.withId(null, "some-user")) - .withMessage("registeredClientId cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.withId(null, "some-user")) + .withMessage("registeredClientId cannot be empty"); } @Test public void withIdWhenPrincipalNameNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationConsent.withId("some-client", null)) - .withMessage("principalName cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationConsent.withId("some-client", null)) + .withMessage("principalName cannot be empty"); } @Test public void buildWhenAuthoritiesEmptyThenThrowIllegalArgumentException() { OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId("some-client", "some-user"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("authorities cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("authorities cannot be empty"); } @Test public void buildWhenAllAttributesAreProvidedThenAllAttributesAreSet() { - OAuth2AuthorizationConsent authorizationConsent = - OAuth2AuthorizationConsent.withId("some-client", "some-user") - .scope("resource.read") - .scope("resource.write") - .authority(new SimpleGrantedAuthority("CLAIM_email")) - .build(); + OAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.withId("some-client", "some-user") + .scope("resource.read") + .scope("resource.write") + .authority(new SimpleGrantedAuthority("CLAIM_email")) + .build(); assertThat(authorizationConsent.getRegisteredClientId()).isEqualTo("some-client"); assertThat(authorizationConsent.getPrincipalName()).isEqualTo("some-user"); - assertThat(authorizationConsent.getScopes()) - .containsExactlyInAnyOrder( - "resource.read", - "resource.write" - ); - assertThat(authorizationConsent.getAuthorities()) - .containsExactlyInAnyOrder( - new SimpleGrantedAuthority("SCOPE_resource.read"), - new SimpleGrantedAuthority("SCOPE_resource.write"), - new SimpleGrantedAuthority("CLAIM_email") - ); + assertThat(authorizationConsent.getScopes()).containsExactlyInAnyOrder("resource.read", "resource.write"); + assertThat(authorizationConsent.getAuthorities()).containsExactlyInAnyOrder( + new SimpleGrantedAuthority("SCOPE_resource.read"), new SimpleGrantedAuthority("SCOPE_resource.write"), + new SimpleGrantedAuthority("CLAIM_email")); } @Test public void fromWhenAuthorizationConsentProvidedThenCopied() { - OAuth2AuthorizationConsent previousAuthorizationConsent = - OAuth2AuthorizationConsent.withId("some-client", "some-principal") - .scope("first.scope") - .scope("second.scope") - .authority(new SimpleGrantedAuthority("CLAIM_email")) - .build(); + OAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent + .withId("some-client", "some-principal") + .scope("first.scope") + .scope("second.scope") + .authority(new SimpleGrantedAuthority("CLAIM_email")) + .build(); - OAuth2AuthorizationConsent authorizationConsent = - OAuth2AuthorizationConsent.from(previousAuthorizationConsent) - .build(); + OAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.from(previousAuthorizationConsent) + .build(); assertThat(authorizationConsent.getRegisteredClientId()).isEqualTo("some-client"); assertThat(authorizationConsent.getPrincipalName()).isEqualTo("some-principal"); - assertThat(authorizationConsent.getAuthorities()) - .containsExactlyInAnyOrder( - new SimpleGrantedAuthority("SCOPE_first.scope"), - new SimpleGrantedAuthority("SCOPE_second.scope"), - new SimpleGrantedAuthority("CLAIM_email") - ); + assertThat(authorizationConsent.getAuthorities()).containsExactlyInAnyOrder( + new SimpleGrantedAuthority("SCOPE_first.scope"), new SimpleGrantedAuthority("SCOPE_second.scope"), + new SimpleGrantedAuthority("CLAIM_email")); } @Test public void authoritiesThenCustomizesAuthorities() { - OAuth2AuthorizationConsent authorizationConsent = - OAuth2AuthorizationConsent.withId("some-client", "some-user") - .authority(new SimpleGrantedAuthority("some.authority")) - .authorities(authorities -> { - authorities.clear(); - authorities.add(new SimpleGrantedAuthority("other.authority")); - }) - .build(); + OAuth2AuthorizationConsent authorizationConsent = OAuth2AuthorizationConsent.withId("some-client", "some-user") + .authority(new SimpleGrantedAuthority("some.authority")) + .authorities(authorities -> { + authorities.clear(); + authorities.add(new SimpleGrantedAuthority("other.authority")); + }) + .build(); - assertThat(authorizationConsent.getAuthorities()).containsExactly(new SimpleGrantedAuthority("other.authority")); + assertThat(authorizationConsent.getAuthorities()) + .containsExactly(new SimpleGrantedAuthority("other.authority")); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java index 2c772fb4..af59e98a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationServerMetadataTests.java @@ -35,6 +35,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationServerMetadataTests { + // @formatter:off private final Builder minimalBuilder = OAuth2AuthorizationServerMetadata.builder() @@ -47,35 +48,42 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void buildWhenAllClaimsProvidedThenCreated() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .jwkSetUrl("https://example.com/oauth2/jwks") - .scope("openid") - .responseType("code") - .grantType("authorization_code") - .grantType("client_credentials") - .tokenRevocationEndpoint("https://example.com/oauth2/revoke") - .tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .tokenIntrospectionEndpoint("https://example.com/oauth2/introspect") - .tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .codeChallengeMethod("S256") - .claim("a-claim", "a-value") - .build(); + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .jwkSetUrl("https://example.com/oauth2/jwks") + .scope("openid") + .responseType("code") + .grantType("authorization_code") + .grantType("client_credentials") + .tokenRevocationEndpoint("https://example.com/oauth2/revoke") + .tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .tokenIntrospectionEndpoint("https://example.com/oauth2/introspect") + .tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .codeChallengeMethod("S256") + .claim("a-claim", "a-value") + .build(); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); - assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(authorizationServerMetadata.getScopes()).containsExactly("openid"); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("code"); - assertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); - assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isEqualTo(url("https://example.com/oauth2/revoke")); - assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isEqualTo(url("https://example.com/oauth2/introspect")); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); + assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/revoke")); + assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()) + .isEqualTo(url("https://example.com/oauth2/introspect")); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("S256"); assertThat(authorizationServerMetadata.getClaimAsString("a-claim")).isEqualTo("a-value"); } @@ -83,14 +91,15 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void buildWhenOnlyRequiredClaimsProvidedThenCreated() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .responseType("code") - .build(); + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .responseType("code") + .build(); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getJwkSetUrl()).isNull(); @@ -108,28 +117,37 @@ public class OAuth2AuthorizationServerMetadataTests { public void withClaimsWhenClaimsProvidedThenCreated() { HashMap claims = new HashMap<>(); claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, "https://example.com"); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, "https://example.com/oauth2/authorize"); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, + "https://example.com/oauth2/authorize"); claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, "https://example.com/oauth2/token"); claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, "https://example.com/oauth2/jwks"); claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.singletonList("openid")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, "https://example.com/oauth2/revoke"); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, "https://example.com/oauth2/introspect"); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, + Collections.singletonList("code")); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, + "https://example.com/oauth2/revoke"); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, + "https://example.com/oauth2/introspect"); claims.put("some-claim", "some-value"); - OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.withClaims(claims).build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata + .withClaims(claims) + .build(); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(authorizationServerMetadata.getScopes()).containsExactly("openid"); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("code"); assertThat(authorizationServerMetadata.getGrantTypes()).isNull(); - assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isEqualTo(url("https://example.com/oauth2/revoke")); + assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/revoke")); assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull(); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isEqualTo(url("https://example.com/oauth2/introspect")); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()) + .isEqualTo(url("https://example.com/oauth2/introspect")); assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull(); assertThat(authorizationServerMetadata.getClaimAsString("some-claim")).isEqualTo("some-value"); @@ -139,27 +157,36 @@ public class OAuth2AuthorizationServerMetadataTests { public void withClaimsWhenClaimsWithUrlsProvidedThenCreated() { HashMap claims = new HashMap<>(); claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, url("https://example.com")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, url("https://example.com/oauth2/authorize")); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, + url("https://example.com/oauth2/authorize")); claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, url("https://example.com/oauth2/token")); claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, url("https://example.com/oauth2/jwks")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.singletonList("code")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, url("https://example.com/oauth2/revoke")); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, url("https://example.com/oauth2/introspect")); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, + Collections.singletonList("code")); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT, + url("https://example.com/oauth2/revoke")); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT, + url("https://example.com/oauth2/introspect")); claims.put("some-claim", "some-value"); - OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.withClaims(claims).build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata + .withClaims(claims) + .build(); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(authorizationServerMetadata.getScopes()).isNull(); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("code"); assertThat(authorizationServerMetadata.getGrantTypes()).isNull(); - assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isEqualTo(url("https://example.com/oauth2/revoke")); + assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/revoke")); assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).isNull(); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isEqualTo(url("https://example.com/oauth2/introspect")); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()) + .isEqualTo(url("https://example.com/oauth2/introspect")); assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getCodeChallengeMethods()).isNull(); assertThat(authorizationServerMetadata.getClaimAsString("some-claim")).isEqualTo("some-value"); @@ -167,34 +194,27 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void withClaimsWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(null)) - .withMessage("claims cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(null)) + .withMessage("claims cannot be empty"); } @Test public void withClaimsWhenMissingRequiredClaimsThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(Collections.emptyMap())) - .withMessage("claims cannot be empty"); + .isThrownBy(() -> OAuth2AuthorizationServerMetadata.withClaims(Collections.emptyMap())) + .withMessage("claims cannot be empty"); } @Test public void buildWhenCalledTwiceThenGeneratesTwoConfigurations() { - OAuth2AuthorizationServerMetadata first = this.minimalBuilder - .grantType("client_credentials") - .build(); + OAuth2AuthorizationServerMetadata first = this.minimalBuilder.grantType("client_credentials").build(); - OAuth2AuthorizationServerMetadata second = this.minimalBuilder - .claims((claims) -> - { - List newGrantTypes = new ArrayList<>(); - newGrantTypes.add("authorization_code"); - newGrantTypes.add("custom_grant"); - claims.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes); - } - ) - .build(); + OAuth2AuthorizationServerMetadata second = this.minimalBuilder.claims((claims) -> { + List newGrantTypes = new ArrayList<>(); + newGrantTypes.add("authorization_code"); + newGrantTypes.add("custom_grant"); + claims.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes); + }).build(); assertThat(first.getGrantTypes()).containsExactly("client_credentials"); assertThat(second.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "custom_grant"); @@ -203,135 +223,120 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void buildWhenMissingIssuerThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.ISSUER)); + .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.ISSUER)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("issuer cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("issuer cannot be null"); } @Test public void buildWhenIssuerNotUrlThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, "not an url")); + .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.ISSUER, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("issuer must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("issuer must be a valid URL"); } @Test public void buildWhenMissingAuthorizationEndpointThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT)); + .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("authorizationEndpoint cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("authorizationEndpoint cannot be null"); } @Test public void buildWhenAuthorizationEndpointNotUrlThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, "not an url")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.AUTHORIZATION_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("authorizationEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("authorizationEndpoint must be a valid URL"); } @Test public void buildWhenMissingTokenEndpointThenThrowsIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT)); + .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenEndpoint cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("tokenEndpoint cannot be null"); } @Test public void buildWhenTokenEndpointNotUrlThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenEndpoint must be a valid URL"); } @Test public void buildWhenTokenEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("tokenEndpointAuthenticationMethods must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("tokenEndpointAuthenticationMethods must be of type List"); } @Test public void buildWhenTokenEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims.put( + OAuth2AuthorizationServerMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHODS_SUPPORTED, + Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenEndpointAuthenticationMethods cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenEndpointAuthenticationMethods cannot be empty"); } @Test public void buildWhenTokenEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .tokenEndpointAuthenticationMethod("should-be-removed") - .tokenEndpointAuthenticationMethods(authMethods -> { - authMethods.clear(); - authMethods.add("some-authentication-method"); - }) - .build(); + .tokenEndpointAuthenticationMethod("should-be-removed") + .tokenEndpointAuthenticationMethods(authMethods -> { + authMethods.clear(); + authMethods.add("some-authentication-method"); + }) + .build(); - assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).containsExactly("some-authentication-method"); + assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()) + .containsExactly("some-authentication-method"); } @Test public void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, "not an url")); + .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.JWKS_URI, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("jwksUri must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("jwksUri must be a valid URL"); } @Test public void buildWhenScopesNotListThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, "not-a-list")); + .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("scopes must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("scopes must be of type List"); } @Test public void buildWhenScopesEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("scopes cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("scopes cannot be empty"); } @Test public void buildWhenScopesAddingOrRemovingThenCorrectValues() { - OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .scope("should-be-removed") - .scopes(scopes -> { - scopes.clear(); - scopes.add("some-scope"); - }) - .build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder.scope("should-be-removed") + .scopes(scopes -> { + scopes.clear(); + scopes.add("some-scope"); + }) + .build(); assertThat(authorizationServerMetadata.getScopes()).containsExactly("some-scope"); } @@ -339,42 +344,37 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void buildWhenMissingResponseTypesThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)); + .claims((claims) -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("responseTypes cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("responseTypes cannot be null"); } @Test public void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("responseTypes must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("responseTypes must be of type List"); } @Test public void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("responseTypes cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("responseTypes cannot be empty"); } @Test public void buildWhenResponseTypesAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .responseType("should-be-removed") - .responseTypes(responseTypes -> { - responseTypes.clear(); - responseTypes.add("some-response-type"); - }) - .build(); + .responseType("should-be-removed") + .responseTypes(responseTypes -> { + responseTypes.clear(); + responseTypes.add("some-response-type"); + }) + .build(); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("some-response-type"); } @@ -382,161 +382,154 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void buildWhenResponseTypesNotPresentAndAddingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .claims(claims -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)) - .responseTypes(responseTypes -> responseTypes.add("some-response-type")) - .build(); + .claims(claims -> claims.remove(OAuth2AuthorizationServerMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)) + .responseTypes(responseTypes -> responseTypes.add("some-response-type")) + .build(); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("some-response-type"); } @Test public void buildWhenGrantTypesNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("grantTypes must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("grantTypes must be of type List"); } @Test public void buildWhenGrantTypesEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.GRANT_TYPES_SUPPORTED, Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("grantTypes cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("grantTypes cannot be empty"); } @Test public void buildWhenGrantTypesAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .grantType("should-be-removed") - .grantTypes(grantTypes -> { - grantTypes.clear(); - grantTypes.add("some-grant-type"); - }) - .build(); + .grantType("should-be-removed") + .grantTypes(grantTypes -> { + grantTypes.clear(); + grantTypes.add("some-grant-type"); + }) + .build(); assertThat(authorizationServerMetadata.getGrantTypes()).containsExactly("some-grant-type"); } @Test public void buildWhenTokenRevocationEndpointNotUrlThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .tokenRevocationEndpoint("not a valid URL"); + Builder builder = this.minimalBuilder.tokenRevocationEndpoint("not a valid URL"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenRevocationEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenRevocationEndpoint must be a valid URL"); } @Test public void buildWhenTokenRevocationEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("tokenRevocationEndpointAuthenticationMethods must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("tokenRevocationEndpointAuthenticationMethods must be of type List"); } @Test public void buildWhenTokenRevocationEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims.put( + OAuth2AuthorizationServerMetadataClaimNames.REVOCATION_ENDPOINT_AUTH_METHODS_SUPPORTED, + Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenRevocationEndpointAuthenticationMethods cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenRevocationEndpointAuthenticationMethods cannot be empty"); } @Test public void buildWhenTokenRevocationEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .tokenRevocationEndpointAuthenticationMethod("should-be-removed") - .tokenRevocationEndpointAuthenticationMethods(authMethods -> { - authMethods.clear(); - authMethods.add("some-authentication-method"); - }) - .build(); + .tokenRevocationEndpointAuthenticationMethod("should-be-removed") + .tokenRevocationEndpointAuthenticationMethods(authMethods -> { + authMethods.clear(); + authMethods.add("some-authentication-method"); + }) + .build(); - assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).containsExactly("some-authentication-method"); + assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()) + .containsExactly("some-authentication-method"); } @Test public void buildWhenTokenIntrospectionEndpointNotUrlThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .tokenIntrospectionEndpoint("not a valid URL"); + Builder builder = this.minimalBuilder.tokenIntrospectionEndpoint("not a valid URL"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenIntrospectionEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenIntrospectionEndpoint must be a valid URL"); } @Test public void buildWhenTokenIntrospectionEndpointAuthenticationMethodsNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims.put( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, + "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("tokenIntrospectionEndpointAuthenticationMethods must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("tokenIntrospectionEndpointAuthenticationMethods must be of type List"); } @Test public void buildWhenTokenIntrospectionEndpointAuthenticationMethodsEmptyListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, Collections.emptyList())); + Builder builder = this.minimalBuilder.claims((claims) -> claims.put( + OAuth2AuthorizationServerMetadataClaimNames.INTROSPECTION_ENDPOINT_AUTH_METHODS_SUPPORTED, + Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenIntrospectionEndpointAuthenticationMethods cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("tokenIntrospectionEndpointAuthenticationMethods cannot be empty"); } @Test public void buildWhenTokenIntrospectionEndpointAuthenticationMethodsAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .tokenIntrospectionEndpointAuthenticationMethod("should-be-removed") - .tokenIntrospectionEndpointAuthenticationMethods(authMethods -> { - authMethods.clear(); - authMethods.add("some-authentication-method"); - }) - .build(); + .tokenIntrospectionEndpointAuthenticationMethod("should-be-removed") + .tokenIntrospectionEndpointAuthenticationMethods(authMethods -> { + authMethods.clear(); + authMethods.add("some-authentication-method"); + }) + .build(); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).containsExactly("some-authentication-method"); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()) + .containsExactly("some-authentication-method"); } @Test public void buildWhenCodeChallengeMethodsNotListThenThrowIllegalArgumentException() { - Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, "not-a-list")); + Builder builder = this.minimalBuilder.claims((claims) -> claims + .put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, "not-a-list")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("codeChallengeMethods must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("codeChallengeMethods must be of type List"); } @Test public void buildWhenCodeChallengeMethodsEmptyListThenThrowIllegalArgumentException() { Builder builder = this.minimalBuilder - .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, Collections.emptyList())); + .claims((claims) -> claims.put(OAuth2AuthorizationServerMetadataClaimNames.CODE_CHALLENGE_METHODS_SUPPORTED, + Collections.emptyList())); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("codeChallengeMethods cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("codeChallengeMethods cannot be empty"); } @Test public void buildWhenCodeChallengeMethodsAddingOrRemovingThenCorrectValues() { OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder - .codeChallengeMethod("should-be-removed") - .codeChallengeMethods(codeChallengeMethods -> { - codeChallengeMethods.clear(); - codeChallengeMethods.add("some-authentication-method"); - }) - .build(); + .codeChallengeMethod("should-be-removed") + .codeChallengeMethods(codeChallengeMethods -> { + codeChallengeMethods.clear(); + codeChallengeMethods.add("some-authentication-method"); + }) + .build(); assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("some-authentication-method"); } @@ -544,40 +537,39 @@ public class OAuth2AuthorizationServerMetadataTests { @Test public void claimWhenNameNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim(null, "claim-value")) - .withMessage("name cannot be empty"); + .isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim(null, "claim-value")) + .withMessage("name cannot be empty"); } @Test public void claimWhenValueNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim("claim-name", null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> OAuth2AuthorizationServerMetadata.builder().claim("claim-name", null)) + .withMessage("value cannot be null"); } @Test public void claimsWhenRemovingClaimThenNotPresent() { - OAuth2AuthorizationServerMetadata authorizationServerMetadata = - this.minimalBuilder - .claim("claim-name", "claim-value") - .claims((claims) -> claims.remove("claim-name")) - .build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder + .claim("claim-name", "claim-value") + .claims((claims) -> claims.remove("claim-name")) + .build(); assertThat(authorizationServerMetadata.hasClaim("claim-name")).isFalse(); } @Test public void claimsWhenAddingClaimThenPresent() { - OAuth2AuthorizationServerMetadata authorizationServerMetadata = - this.minimalBuilder - .claim("claim-name", "claim-value") - .build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.minimalBuilder + .claim("claim-name", "claim-value") + .build(); assertThat(authorizationServerMetadata.hasClaim("claim-name")).isTrue(); } private static URL url(String urlString) { try { return new URL(urlString); - } catch (Exception ex) { + } + 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/server/authorization/OAuth2AuthorizationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationTests.java index 8a9e3013..b42d6255 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/OAuth2AuthorizationTests.java @@ -36,93 +36,98 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2AuthorizationTests { + private static final String ID = "id"; + private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build(); + private static final String PRINCIPAL_NAME = "principal"; + private static final AuthorizationGrantType AUTHORIZATION_GRANT_TYPE = AuthorizationGrantType.AUTHORIZATION_CODE; - private static final OAuth2AccessToken ACCESS_TOKEN = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300)); + + private static final OAuth2AccessToken ACCESS_TOKEN = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, + "access-token", Instant.now(), Instant.now().plusSeconds(300)); + private static final OAuth2RefreshToken REFRESH_TOKEN = new OAuth2RefreshToken("refresh-token", Instant.now()); - private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES)); + + private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plus(5, ChronoUnit.MINUTES)); @Test public void withRegisteredClientWhenRegisteredClientNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OAuth2Authorization.withRegisteredClient(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClient cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClient cannot be null"); } @Test public void fromWhenAuthorizationNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> OAuth2Authorization.from(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorization cannot be null"); + assertThatThrownBy(() -> OAuth2Authorization.from(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorization cannot be null"); } @Test public void fromWhenAuthorizationProvidedThenCopied() { OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .accessToken(ACCESS_TOKEN) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .accessToken(ACCESS_TOKEN) + .build(); OAuth2Authorization authorizationResult = OAuth2Authorization.from(authorization).build(); assertThat(authorizationResult.getId()).isEqualTo(authorization.getId()); assertThat(authorizationResult.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId()); assertThat(authorizationResult.getPrincipalName()).isEqualTo(authorization.getPrincipalName()); - assertThat(authorizationResult.getAuthorizationGrantType()).isEqualTo(authorization.getAuthorizationGrantType()); + assertThat(authorizationResult.getAuthorizationGrantType()) + .isEqualTo(authorization.getAuthorizationGrantType()); assertThat(authorizationResult.getAccessToken()).isEqualTo(authorization.getAccessToken()); assertThat(authorizationResult.getToken(OAuth2AuthorizationCode.class)) - .isEqualTo(authorization.getToken(OAuth2AuthorizationCode.class)); + .isEqualTo(authorization.getToken(OAuth2AuthorizationCode.class)); assertThat(authorizationResult.getAttributes()).isEqualTo(authorization.getAttributes()); } @Test public void buildWhenPrincipalNameNotProvidedThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).build()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("principalName cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("principalName cannot be empty"); } @Test public void buildWhenAuthorizationGrantTypeNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).principalName(PRINCIPAL_NAME).build()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationGrantType cannot be null"); + assertThatThrownBy( + () -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).principalName(PRINCIPAL_NAME).build()) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationGrantType cannot be null"); } @Test public void attributeWhenNameNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .attribute(null, AUTHORIZATION_CODE)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("name cannot be empty"); + assertThatThrownBy( + () -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).attribute(null, AUTHORIZATION_CODE)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("name cannot be empty"); } @Test public void attributeWhenValueNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .attribute("name", null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("value cannot be null"); + assertThatThrownBy(() -> OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT).attribute("name", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("value cannot be null"); } @Test public void buildWhenAllAttributesAreProvidedThenAllAttributesAreSet() { OAuth2Authorization authorization = OAuth2Authorization.withRegisteredClient(REGISTERED_CLIENT) - .id(ID) - .principalName(PRINCIPAL_NAME) - .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) - .token(AUTHORIZATION_CODE) - .accessToken(ACCESS_TOKEN) - .refreshToken(REFRESH_TOKEN) - .build(); + .id(ID) + .principalName(PRINCIPAL_NAME) + .authorizationGrantType(AUTHORIZATION_GRANT_TYPE) + .token(AUTHORIZATION_CODE) + .accessToken(ACCESS_TOKEN) + .refreshToken(REFRESH_TOKEN) + .build(); assertThat(authorization.getId()).isEqualTo(ID); assertThat(authorization.getRegisteredClientId()).isEqualTo(REGISTERED_CLIENT.getId()); @@ -132,4 +137,5 @@ public class OAuth2AuthorizationTests { assertThat(authorization.getAccessToken().getToken()).isEqualTo(ACCESS_TOKEN); assertThat(authorization.getRefreshToken().getToken()).isEqualTo(REFRESH_TOKEN); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java index fce02bd8..eee09c21 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/TestOAuth2Authorizations.java @@ -48,11 +48,12 @@ public class TestOAuth2Authorizations { public static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient, Map authorizationRequestAdditionalParameters) { - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plusSeconds(120)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300)); - return authorization(registeredClient, authorizationCode, accessToken, Collections.emptyMap(), authorizationRequestAdditionalParameters); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plusSeconds(120)); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + Instant.now(), Instant.now().plusSeconds(300)); + return authorization(registeredClient, authorizationCode, accessToken, Collections.emptyMap(), + authorizationRequestAdditionalParameters); } public static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient, @@ -62,37 +63,37 @@ public class TestOAuth2Authorizations { public static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient, OAuth2AccessToken accessToken, Map accessTokenClaims) { - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plusSeconds(120)); - return authorization(registeredClient, authorizationCode, accessToken, accessTokenClaims, Collections.emptyMap()); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plusSeconds(120)); + return authorization(registeredClient, authorizationCode, accessToken, accessTokenClaims, + Collections.emptyMap()); } private static OAuth2Authorization.Builder authorization(RegisteredClient registeredClient, OAuth2AuthorizationCode authorizationCode, OAuth2AccessToken accessToken, Map accessTokenClaims, Map authorizationRequestAdditionalParameters) { OAuth2AuthorizationRequest authorizationRequest = OAuth2AuthorizationRequest.authorizationCode() - .authorizationUri("https://provider.com/oauth2/authorize") - .clientId(registeredClient.getClientId()) - .redirectUri(registeredClient.getRedirectUris().iterator().next()) - .scopes(registeredClient.getScopes()) - .additionalParameters(authorizationRequestAdditionalParameters) - .state("state") - .build(); + .authorizationUri("https://provider.com/oauth2/authorize") + .clientId(registeredClient.getClientId()) + .redirectUri(registeredClient.getRedirectUris().iterator().next()) + .scopes(registeredClient.getScopes()) + .additionalParameters(authorizationRequestAdditionalParameters) + .state("state") + .build(); OAuth2Authorization.Builder builder = OAuth2Authorization.withRegisteredClient(registeredClient) - .id("id") - .principalName("principal") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizedScopes(authorizationRequest.getScopes()) - .token(authorizationCode) - .attribute(OAuth2ParameterNames.STATE, "consent-state") - .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest) - .attribute(Principal.class.getName(), - new TestingAuthenticationToken("principal", null, "ROLE_A", "ROLE_B")); + .id("id") + .principalName("principal") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizedScopes(authorizationRequest.getScopes()) + .token(authorizationCode) + .attribute(OAuth2ParameterNames.STATE, "consent-state") + .attribute(OAuth2AuthorizationRequest.class.getName(), authorizationRequest) + .attribute(Principal.class.getName(), + new TestingAuthenticationToken("principal", null, "ROLE_A", "ROLE_B")); if (accessToken != null) { - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - "refresh-token", Instant.now(), Instant.now().plus(1, ChronoUnit.HOURS)); - builder - .token(accessToken, (metadata) -> metadata.putAll(tokenMetadata(accessTokenClaims))) + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now(), + Instant.now().plus(1, ChronoUnit.HOURS)); + builder.token(accessToken, (metadata) -> metadata.putAll(tokenMetadata(accessTokenClaims))) .refreshToken(refreshToken); } @@ -116,4 +117,5 @@ public class TestOAuth2Authorizations { claims.put("claim3", "value3"); return claims; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProviderTests.java index bd773f38..bfd3506d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/ClientSecretAuthenticationProviderTests.java @@ -56,25 +56,31 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class ClientSecretAuthenticationProviderTests { - // See RFC 7636: Appendix B. Example for the S256 code_challenge_method + + // See RFC 7636: Appendix B. Example for the S256 code_challenge_method // https://tools.ietf.org/html/rfc7636#appendix-B private static final String S256_CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String S256_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; private static final String AUTHORIZATION_CODE = "code"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private ClientSecretAuthenticationProvider authenticationProvider; + private PasswordEncoder passwordEncoder; @BeforeEach public void setUp() { this.registeredClientRepository = mock(RegisteredClientRepository.class); this.authorizationService = mock(OAuth2AuthorizationService.class); - this.authenticationProvider = new ClientSecretAuthenticationProvider( - this.registeredClientRepository, this.authorizationService); + this.authenticationProvider = new ClientSecretAuthenticationProvider(this.registeredClientRepository, + this.authorizationService); this.passwordEncoder = spy(new PasswordEncoder() { @Override public String encode(CharSequence rawPassword) { @@ -97,22 +103,22 @@ public class ClientSecretAuthenticationProviderTests { @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new ClientSecretAuthenticationProvider(null, this.authorizationService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new ClientSecretAuthenticationProvider(this.registeredClientRepository, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("passwordEncoder cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("passwordEncoder cannot be null"); } @Test @@ -124,88 +130,92 @@ public class ClientSecretAuthenticationProviderTests { public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId() + "-invalid", ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); + registeredClient.getClientId() + "-invalid", ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); + }); } @Test public void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_POST, registeredClient.getClientSecret(), null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_POST, + registeredClient.getClientSecret(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("authentication_method"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("authentication_method"); + }); } @Test public void authenticateWhenClientSecretNotProvidedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("credentials"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("credentials"); + }); } @Test public void authenticateWhenInvalidClientSecretThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret() + "-invalid", null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret() + "-invalid", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_SECRET); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_SECRET); + }); verify(this.passwordEncoder).matches(any(), any()); } @Test public void authenticateWhenExpiredClientSecretThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSecretExpiresAt(Instant.now().minus(1, ChronoUnit.HOURS).truncatedTo(ChronoUnit.SECONDS)) - .build(); + .clientSecretExpiresAt(Instant.now().minus(1, ChronoUnit.HOURS).truncatedTo(ChronoUnit.SECONDS)) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("client_secret_expires_at"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("client_secret_expires_at"); + }); verify(this.passwordEncoder).matches(any(), any()); } @@ -213,12 +223,13 @@ public class ClientSecretAuthenticationProviderTests { public void authenticateWhenValidCredentialsThenAuthenticated() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.passwordEncoder).matches(any(), any()); assertThat(authenticationResult.isAuthenticated()).isTrue(); @@ -231,12 +242,13 @@ public class ClientSecretAuthenticationProviderTests { public void authenticateWhenValidCredentialsAndRequiresUpgradingThenClientSecretUpgraded() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.passwordEncoder).matches(any(), any()); verify(this.passwordEncoder).upgradeEncoding(any()); @@ -252,14 +264,15 @@ public class ClientSecretAuthenticationProviderTests { public void authenticateWhenAuthorizationCodeGrantAndValidCredentialsThenAuthenticated() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(TestOAuth2Authorizations.authorization().build()); + .thenReturn(TestOAuth2Authorizations.authorization().build()); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), createAuthorizationCodeTokenParameters()); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), createAuthorizationCodeTokenParameters()); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.passwordEncoder).matches(any(), any()); verify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)); @@ -273,73 +286,76 @@ public class ClientSecretAuthenticationProviderTests { public void authenticateWhenPkceAndInvalidCodeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); parameters.put(OAuth2ParameterNames.CODE, "invalid-code"); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), parameters); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE); + }); } @Test public void authenticateWhenPkceAndMissingCodeVerifierThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createAuthorizationCodeTokenParameters(); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); + }); } @Test public void authenticateWhenPkceAndValidCodeVerifierThenAuthenticated() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), parameters); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), parameters); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.passwordEncoder).matches(any(), any()); verify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProviderTests.java index 936eca98..d2179944 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionAuthenticationProviderTests.java @@ -76,51 +76,60 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class JwtClientAssertionAuthenticationProviderTests { - // See RFC 7636: Appendix B. Example for the S256 code_challenge_method + + // See RFC 7636: Appendix B. Example for the S256 code_challenge_method // https://tools.ietf.org/html/rfc7636#appendix-B private static final String S256_CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String S256_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; private static final String AUTHORIZATION_CODE = "code"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); - private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = - new ClientAuthenticationMethod("urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); + private static final ClientAuthenticationMethod JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD = new ClientAuthenticationMethod( + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"); private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private JwtClientAssertionAuthenticationProvider authenticationProvider; + private AuthorizationServerSettings authorizationServerSettings; @BeforeEach public void setUp() { this.registeredClientRepository = mock(RegisteredClientRepository.class); this.authorizationService = mock(OAuth2AuthorizationService.class); - this.authenticationProvider = new JwtClientAssertionAuthenticationProvider( - this.registeredClientRepository, this.authorizationService); - this.authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://auth-server.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); + this.authenticationProvider = new JwtClientAssertionAuthenticationProvider(this.registeredClientRepository, + this.authorizationService); + this.authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://auth-server.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); } @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new JwtClientAssertionAuthenticationProvider(null, this.authorizationService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new JwtClientAssertionAuthenticationProvider(this.registeredClientRepository, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void setJwtDecoderFactoryWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setJwtDecoderFactory(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwtDecoderFactory cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwtDecoderFactory cannot be null"); } @Test @@ -136,34 +145,35 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId() + "-invalid", JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, "jwt-assertion", null); + registeredClient.getClientId() + "-invalid", JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, + "jwt-assertion", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); + }); } @Test public void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, "jwt-assertion", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("authentication_method"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("authentication_method"); + }); } @Test @@ -174,17 +184,17 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("credentials"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("credentials"); + }); } @Test @@ -201,18 +211,19 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, "invalid-jwt-assertion", null); + registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, "invalid-jwt-assertion", + null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .hasCauseInstanceOf(BadJwtException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .hasCauseInstanceOf(BadJwtException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION); + }); } @Test @@ -229,7 +240,7 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); // @formatter:off JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256) @@ -245,17 +256,19 @@ public class JwtClientAssertionAuthenticationProviderTests { Jwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, jwtAssertion.getTokenValue(), null); + registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, + jwtAssertion.getTokenValue(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .hasCauseInstanceOf(JwtValidationException.class) - .extracting(ex -> (OAuth2AuthenticationException) ex) - .satisfies(ex -> { - assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(ex.getError().getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION); - JwtValidationException jwtValidationException = (JwtValidationException) ex.getCause(); - assertThat(jwtValidationException.getErrors()).hasSize(4); // iss, sub, aud, exp - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .hasCauseInstanceOf(JwtValidationException.class) + .extracting(ex -> (OAuth2AuthenticationException) ex) + .satisfies(ex -> { + assertThat(ex.getError().getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(ex.getError().getDescription()).contains(OAuth2ParameterNames.CLIENT_ASSERTION); + JwtValidationException jwtValidationException = (JwtValidationException) ex.getCause(); + assertThat(jwtValidationException.getErrors()).hasSize(4); // iss, sub, + // aud, exp + }); } @Test @@ -272,7 +285,7 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); // @formatter:off JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256) @@ -285,15 +298,17 @@ public class JwtClientAssertionAuthenticationProviderTests { Jwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, jwtAssertion.getTokenValue(), null); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, + jwtAssertion.getTokenValue(), null); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getCredentials()).isInstanceOf(Jwt.class); assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(authenticationResult.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT); + assertThat(authenticationResult.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT); } @Test @@ -310,13 +325,13 @@ public class JwtClientAssertionAuthenticationProviderTests { .build(); // @formatter:on when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); @@ -331,34 +346,37 @@ public class JwtClientAssertionAuthenticationProviderTests { Jwt jwtAssertion = jwsEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, jwtAssertion.getTokenValue(), parameters); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + registeredClient.getClientId(), JWT_CLIENT_ASSERTION_AUTHENTICATION_METHOD, + jwtAssertion.getTokenValue(), parameters); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE)); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getCredentials()).isInstanceOf(Jwt.class); assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(authenticationResult.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT); + assertThat(authenticationResult.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT); } private JwtClaimsSet.Builder jwtClientAssertionClaims(RegisteredClient registeredClient) { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); return JwtClaimsSet.builder() - .issuer(registeredClient.getClientId()) - .subject(registeredClient.getClientId()) - .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), this.authorizationServerSettings.getTokenEndpoint()))) - .issuedAt(issuedAt) - .expiresAt(expiresAt); + .issuer(registeredClient.getClientId()) + .subject(registeredClient.getClientId()) + .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), + this.authorizationServerSettings.getTokenEndpoint()))) + .issuedAt(issuedAt) + .expiresAt(expiresAt); } private static JwtEncoder createEncoder(String secret, String algorithm) { SecretKey secretKey = new SecretKeySpec(secret.getBytes(StandardCharsets.UTF_8), algorithm); OctetSequenceKey secretKeyJwk = TestJwks.jwk(secretKey).build(); - JWKSource jwkSource = (jwkSelector, securityContext) -> - jwkSelector.select(new JWKSet(secretKeyJwk)); + JWKSource jwkSource = (jwkSelector, securityContext) -> jwkSelector + .select(new JWKSet(secretKeyJwk)); return new NimbusJwtEncoder(jwkSource); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactoryTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactoryTests.java index 68d7ce2a..d44777da 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactoryTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/JwtClientAssertionDecoderFactoryTests.java @@ -35,13 +35,14 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class JwtClientAssertionDecoderFactoryTests { + private JwtClientAssertionDecoderFactory jwtDecoderFactory = new JwtClientAssertionDecoderFactory(); @Test public void setJwtValidatorFactoryWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.jwtDecoderFactory.setJwtValidatorFactory(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwtValidatorFactory cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwtValidatorFactory cannot be null"); } @Test @@ -58,13 +59,13 @@ public class JwtClientAssertionDecoderFactoryTests { // @formatter:on assertThatThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).isEqualTo("Failed to find a Signature Verifier for Client: '" + - registeredClient.getId() + "'. Check to ensure you have configured the JWK Set URL."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).isEqualTo("Failed to find a Signature Verifier for Client: '" + + registeredClient.getId() + "'. Check to ensure you have configured the JWK Set URL."); + }); } @Test @@ -82,13 +83,13 @@ public class JwtClientAssertionDecoderFactoryTests { // @formatter:on assertThatThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).isEqualTo("Failed to find a Signature Verifier for Client: '" + - registeredClient.getId() + "'. Check to ensure you have configured the client secret."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).isEqualTo("Failed to find a Signature Verifier for Client: '" + + registeredClient.getId() + "'. Check to ensure you have configured the client secret."); + }); } @Test @@ -100,13 +101,14 @@ public class JwtClientAssertionDecoderFactoryTests { // @formatter:on assertThatThrownBy(() -> this.jwtDecoderFactory.createDecoder(registeredClient)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).isEqualTo("Failed to find a Signature Verifier for Client: '" + - registeredClient.getId() + "'. Check to ensure you have configured a valid JWS Algorithm: 'null'."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()) + .isEqualTo("Failed to find a Signature Verifier for Client: '" + registeredClient.getId() + + "'. Check to ensure you have configured a valid JWS Algorithm: 'null'."); + }); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationTokenTests.java index 7ea3ddee..ee6be4cd 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AccessTokenAuthenticationTokenTests.java @@ -37,48 +37,56 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2AccessTokenAuthenticationTokenTests { + private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); - private OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - "access-token", Instant.now(), Instant.now().plusSeconds(300)); - private OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - "refresh-token", Instant.now(), Instant.now().plus(1, ChronoUnit.DAYS)); + + private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + + private OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + Instant.now(), Instant.now().plusSeconds(300)); + + private OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now(), + Instant.now().plus(1, ChronoUnit.DAYS)); + private Map additionalParameters = Collections.singletonMap("custom-param", "custom-value"); @Test public void constructorWhenRegisteredClientNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(null, this.clientPrincipal, this.accessToken)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClient cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClient cannot be null"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, null, this.accessToken)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + assertThatThrownBy( + () -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, null, this.accessToken)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test public void constructorWhenAccessTokenNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("accessToken cannot be null"); + assertThatThrownBy( + () -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("accessToken cannot be null"); } @Test public void constructorWhenAdditionalParametersNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AccessTokenAuthenticationToken( - this.registeredClient, this.clientPrincipal, this.accessToken, this.refreshToken, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("additionalParameters cannot be null"); + assertThatThrownBy(() -> new OAuth2AccessTokenAuthenticationToken(this.registeredClient, this.clientPrincipal, + this.accessToken, this.refreshToken, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("additionalParameters cannot be null"); } @Test public void constructorWhenAllValuesProvidedThenCreated() { OAuth2AccessTokenAuthenticationToken authentication = new OAuth2AccessTokenAuthenticationToken( - this.registeredClient, this.clientPrincipal, this.accessToken, this.refreshToken, this.additionalParameters); + this.registeredClient, this.clientPrincipal, this.accessToken, this.refreshToken, + this.additionalParameters); assertThat(authentication.getPrincipal()).isEqualTo(this.clientPrincipal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getRegisteredClient()).isEqualTo(this.registeredClient); @@ -86,4 +94,5 @@ public class OAuth2AccessTokenAuthenticationTokenTests { assertThat(authentication.getRefreshToken()).isEqualTo(this.refreshToken); assertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java index 1c775dfc..f8aad7f9 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationProviderTests.java @@ -99,14 +99,23 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationCodeAuthenticationProviderTests { + private static final String AUTHORIZATION_CODE = "code"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private OAuth2AuthorizationService authorizationService; + private JwtEncoder jwtEncoder; + private OAuth2TokenCustomizer jwtCustomizer; + private OAuth2TokenCustomizer accessTokenCustomizer; + private OAuth2TokenGenerator tokenGenerator; + private SessionRegistry sessionRegistry; + private OAuth2AuthorizationCodeAuthenticationProvider authenticationProvider; @BeforeEach @@ -120,8 +129,8 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); accessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); - OAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator); + OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, + accessTokenGenerator, refreshTokenGenerator); this.tokenGenerator = spy(new OAuth2TokenGenerator() { @Override public OAuth2Token generate(OAuth2TokenContext context) { @@ -129,11 +138,14 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { } }); this.sessionRegistry = mock(SessionRegistry.class); - this.authenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider( - this.authorizationService, this.tokenGenerator); + this.authenticationProvider = new OAuth2AuthorizationCodeAuthenticationProvider(this.authorizationService, + this.tokenGenerator); this.authenticationProvider.setSessionRegistry(this.sessionRegistry); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); } @AfterEach @@ -144,15 +156,15 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationProvider(null, this.tokenGenerator)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationProvider(this.authorizationService, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenGenerator cannot be null"); } @Test @@ -163,74 +175,75 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { @Test public void setSessionRegistryWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setSessionRegistry(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("sessionRegistry cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("sessionRegistry cannot be null"); } @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( - registeredClient.getClientId(), registeredClient.getClientSecret()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(), + registeredClient.getClientSecret()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenInvalidCodeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenCodeIssuedToAnotherClientThenThrowOAuth2AuthenticationException() { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - OAuth2Authorization.Token authorizationCode = - updatedAuthorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = updatedAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCode.isInvalidated()).isTrue(); } @@ -239,44 +252,45 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri() + "-invalid", null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri() + "-invalid", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenInvalidatedCodeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - AUTHORIZATION_CODE, Instant.now(), Instant.now().plusSeconds(120)); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE, Instant.now(), + Instant.now().plusSeconds(120)); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(authorizationCode, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .build(); + .token(authorizationCode, + (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); @@ -289,26 +303,27 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { @Test public void authenticateWhenInvalidatedCodeAndAccessTokenNullThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - AUTHORIZATION_CODE, Instant.now(), Instant.now().plusSeconds(120)); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE, Instant.now(), + Instant.now().plusSeconds(120)); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient, authorizationCode) - .token(authorizationCode, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .build(); + .token(authorizationCode, + (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); verify(this.authorizationService, never()).save(any()); } @@ -317,26 +332,26 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { @Test public void authenticateWhenExpiredCodeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - AUTHORIZATION_CODE, Instant.now().minusSeconds(300), Instant.now().minusSeconds(60)); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode(AUTHORIZATION_CODE, + Instant.now().minusSeconds(300), Instant.now().minusSeconds(60)); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(authorizationCode) - .build(); + .token(authorizationCode) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test @@ -344,31 +359,32 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); doAnswer(answer -> { OAuth2TokenContext context = answer.getArgument(0); if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { return null; - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); + }); } @Test @@ -376,14 +392,14 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); @@ -392,7 +408,8 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { if (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) { return new OAuth2AccessToken( OAuth2AccessToken.TokenType.BEARER, "access-token", Instant.now(), Instant.now().plusSeconds(300)); - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); @@ -411,14 +428,14 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); @@ -426,18 +443,19 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { OAuth2TokenContext context = answer.getArgument(0); if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { return null; - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the ID token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()).contains("The token generator failed to generate the ID token."); + }); } @Test @@ -445,34 +463,37 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture()); JwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue(); assertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(jwtEncodingContext.getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(jwtEncodingContext.getPrincipal()) + .isEqualTo(authorization.getAttribute(Principal.class.getName())); assertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization); assertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(jwtEncodingContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(jwtEncodingContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(jwtEncodingContext.getJwsHeader()).isNotNull(); assertThat(jwtEncodingContext.getClaims()).isNotNull(); - ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor.forClass(JwtEncoderParameters.class); + ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor + .forClass(JwtEncoderParameters.class); verify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture()); JwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims(); @@ -484,49 +505,53 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId()); + assertThat(accessTokenAuthentication.getRegisteredClient().getId()) + .isEqualTo(updatedAuthorization.getRegisteredClientId()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); - assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(authorization.getAuthorizedScopes()); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); + assertThat(accessTokenAuthentication.getAccessToken().getScopes()) + .isEqualTo(authorization.getAuthorizedScopes()); assertThat(accessTokenAuthentication.getRefreshToken()).isNotNull(); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); - OAuth2Authorization.Token authorizationCode = updatedAuthorization.getToken(OAuth2AuthorizationCode.class); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + OAuth2Authorization.Token authorizationCode = updatedAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCode.isInvalidated()).isTrue(); } @Test public void authenticateWhenValidCodeAndAuthenticationRequestThenReturnIdToken() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plusSeconds(120)); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient, authorizationCode).build(); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plusSeconds(120)); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient, authorizationCode) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); Authentication principal = authorization.getAttribute(Principal.class.getName()); List sessions = new ArrayList<>(); - sessions.add(new SessionInformation(principal.getPrincipal(), - "session3", Date.from(Instant.now()))); - sessions.add(new SessionInformation(principal.getPrincipal(), - "session2", Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))); - sessions.add(new SessionInformation(principal.getPrincipal(), - "session1", Date.from(Instant.now().minus(2, ChronoUnit.HOURS)))); - SessionInformation expectedSession = sessions.get(0); // Most recent - when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(false))) - .thenReturn(sessions); + sessions.add(new SessionInformation(principal.getPrincipal(), "session3", Date.from(Instant.now()))); + sessions.add(new SessionInformation(principal.getPrincipal(), "session2", + Date.from(Instant.now().minus(1, ChronoUnit.HOURS)))); + sessions.add(new SessionInformation(principal.getPrincipal(), "session1", + Date.from(Instant.now().minus(2, ChronoUnit.HOURS)))); + SessionInformation expectedSession = sessions.get(0); // Most recent + when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(false))).thenReturn(sessions); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer, times(2)).customize(jwtEncodingContextCaptor.capture()); @@ -539,13 +564,14 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { assertThat(accessTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(accessTokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(accessTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(accessTokenContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(accessTokenContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(accessTokenContext.getJwsHeader()).isNotNull(); assertThat(accessTokenContext.getClaims()).isNotNull(); Map claims = new HashMap<>(); accessTokenContext.getClaims().claims(claims::putAll); assertThat(claims).flatExtracting(OAuth2ParameterNames.SCOPE) - .containsExactlyInAnyOrder(OidcScopes.OPENID, "scope1"); + .containsExactlyInAnyOrder(OidcScopes.OPENID, "scope1"); // ID Token context JwtEncodingContext idTokenContext = jwtEncodingContextCaptor.getAllValues().get(1); assertThat(idTokenContext.getRegisteredClient()).isEqualTo(registeredClient); @@ -555,70 +581,78 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { assertThat(idTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(idTokenContext.getTokenType().getValue()).isEqualTo(OidcParameterNames.ID_TOKEN); assertThat(idTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(idTokenContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(idTokenContext.getAuthorizationGrant()) + .isEqualTo(authentication); SessionInformation sessionInformation = idTokenContext.get(SessionInformation.class); assertThat(sessionInformation).isNotNull(); assertThat(sessionInformation.getSessionId()).isEqualTo(createHash(expectedSession.getSessionId())); assertThat(idTokenContext.getJwsHeader()).isNotNull(); assertThat(idTokenContext.getClaims()).isNotNull(); - verify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token + verify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId()); + assertThat(accessTokenAuthentication.getRegisteredClient().getId()) + .isEqualTo(updatedAuthorization.getRegisteredClientId()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); Set accessTokenScopes = new HashSet<>(updatedAuthorization.getAuthorizedScopes()); assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(accessTokenScopes); assertThat(accessTokenAuthentication.getRefreshToken()).isNotNull(); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); - OAuth2Authorization.Token authorizationCodeToken = updatedAuthorization.getToken(OAuth2AuthorizationCode.class); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + OAuth2Authorization.Token authorizationCodeToken = updatedAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCodeToken.isInvalidated()).isTrue(); OAuth2Authorization.Token idToken = updatedAuthorization.getToken(OidcIdToken.class); assertThat(idToken).isNotNull(); assertThat(accessTokenAuthentication.getAdditionalParameters()) - .containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue())); + .containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue())); } // gh-296 @Test public void authenticateWhenPublicClientThenRefreshTokenNotIssued() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient() - .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - .build(); + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.NONE, null); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.NONE, null); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture()); JwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue(); assertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(jwtEncodingContext.getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(jwtEncodingContext.getPrincipal()) + .isEqualTo(authorization.getAttribute(Principal.class.getName())); assertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization); assertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(jwtEncodingContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(jwtEncodingContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(jwtEncodingContext.getJwsHeader()).isNotNull(); assertThat(jwtEncodingContext.getClaims()).isNotNull(); - ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor.forClass(JwtEncoderParameters.class); + ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor + .forClass(JwtEncoderParameters.class); verify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture()); JwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims(); @@ -630,12 +664,16 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId()); + assertThat(accessTokenAuthentication.getRegisteredClient().getId()) + .isEqualTo(updatedAuthorization.getRegisteredClientId()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); - assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(authorization.getAuthorizedScopes()); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); + assertThat(accessTokenAuthentication.getAccessToken().getScopes()) + .isEqualTo(authorization.getAuthorizedScopes()); assertThat(accessTokenAuthentication.getRefreshToken()).isNull(); - OAuth2Authorization.Token authorizationCode = updatedAuthorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = updatedAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCode.isInvalidated()).isTrue(); } @@ -644,66 +682,72 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { Duration accessTokenTTL = Duration.ofHours(2); Duration refreshTokenTTL = Duration.ofDays(1); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .tokenSettings(TokenSettings.builder() - .accessTokenTimeToLive(accessTokenTTL) - .refreshTokenTimeToLive(refreshTokenTTL) - .build()) - .build(); + .tokenSettings(TokenSettings.builder() + .accessTokenTimeToLive(accessTokenTTL) + .refreshTokenTimeToLive(refreshTokenTTL) + .build()) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); Instant accessTokenIssuedAt = Instant.now(); Instant accessTokenExpiresAt = accessTokenIssuedAt.plus(accessTokenTTL); when(this.jwtEncoder.encode(any())).thenReturn(createJwt(accessTokenIssuedAt, accessTokenExpiresAt)); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); - Instant expectedAccessTokenExpiresAt = accessTokenAuthentication.getAccessToken().getIssuedAt().plus(accessTokenTTL); - assertThat(accessTokenAuthentication.getAccessToken().getExpiresAt()).isBetween( - expectedAccessTokenExpiresAt.minusSeconds(1), expectedAccessTokenExpiresAt.plusSeconds(1)); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); + Instant expectedAccessTokenExpiresAt = accessTokenAuthentication.getAccessToken() + .getIssuedAt() + .plus(accessTokenTTL); + assertThat(accessTokenAuthentication.getAccessToken().getExpiresAt()) + .isBetween(expectedAccessTokenExpiresAt.minusSeconds(1), expectedAccessTokenExpiresAt.plusSeconds(1)); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); - Instant expectedRefreshTokenExpiresAt = accessTokenAuthentication.getRefreshToken().getIssuedAt().plus(refreshTokenTTL); - assertThat(accessTokenAuthentication.getRefreshToken().getExpiresAt()).isBetween( - expectedRefreshTokenExpiresAt.minusSeconds(1), expectedRefreshTokenExpiresAt.plusSeconds(1)); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + Instant expectedRefreshTokenExpiresAt = accessTokenAuthentication.getRefreshToken() + .getIssuedAt() + .plus(refreshTokenTTL); + assertThat(accessTokenAuthentication.getRefreshToken().getExpiresAt()) + .isBetween(expectedRefreshTokenExpiresAt.minusSeconds(1), expectedRefreshTokenExpiresAt.plusSeconds(1)); } @Test public void authenticateWhenRefreshTokenGrantNotConfiguredThenRefreshTokenNotIssued() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN)) - .build(); + .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt()); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(accessTokenAuthentication.getRefreshToken()).isNull(); } @@ -719,14 +763,14 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { // @formatter:on OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken(AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + AUTHORIZATION_CODE, clientPrincipal, authorizationRequest.getRedirectUri(), null); this.authenticationProvider.authenticate(authentication); @@ -741,10 +785,10 @@ public class OAuth2AuthorizationCodeAuthenticationProviderTests { private static Jwt createJwt(Instant issuedAt, Instant expiresAt) { return Jwt.withTokenValue("token") - .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) - .issuedAt(issuedAt) - .expiresAt(expiresAt) - .build(); + .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) + .issuedAt(issuedAt) + .expiresAt(expiresAt) + .build(); } private static String createHash(String value) throws NoSuchAlgorithmException { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java index 676ac9f5..69bfe5d1 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeAuthenticationTokenTests.java @@ -35,25 +35,32 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationCodeAuthenticationTokenTests { + private String code = "code"; + private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + + private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + private String redirectUri = "redirectUri"; + private Map additionalParameters = Collections.singletonMap("param1", "value1"); @Test public void constructorWhenCodeNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.clientPrincipal, this.redirectUri, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("code cannot be empty"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(null, this.clientPrincipal, + this.redirectUri, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("code cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeAuthenticationToken(this.code, null, this.redirectUri, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + assertThatThrownBy( + () -> new OAuth2AuthorizationCodeAuthenticationToken(this.code, null, this.redirectUri, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test @@ -73,10 +80,11 @@ public class OAuth2AuthorizationCodeAuthenticationTokenTests { OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( this.code, this.clientPrincipal, this.redirectUri, this.additionalParameters); assertThatThrownBy(() -> authentication.getAdditionalParameters().put("another_key", 1)) - .isInstanceOf(UnsupportedOperationException.class); + .isInstanceOf(UnsupportedOperationException.class); assertThatThrownBy(() -> authentication.getAdditionalParameters().remove("some_key")) - .isInstanceOf(UnsupportedOperationException.class); + .isInstanceOf(UnsupportedOperationException.class); assertThatThrownBy(() -> authentication.getAdditionalParameters().clear()) - .isInstanceOf(UnsupportedOperationException.class); + .isInstanceOf(UnsupportedOperationException.class); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java index b9e96da4..4fac15c6 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationProviderTests.java @@ -65,12 +65,19 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { + private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize"; + private static final String STATE = "state"; + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2AuthorizationCodeRequestAuthenticationProvider authenticationProvider; + private TestingAuthenticationToken principal; @BeforeEach @@ -82,67 +89,70 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { this.registeredClientRepository, this.authorizationService, this.authorizationConsentService); this.principal = new TestingAuthenticationToken("principalName", "password"); this.principal.setAuthenticated(true); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); } @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider( - null, this.authorizationService, this.authorizationConsentService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(null, + this.authorizationService, this.authorizationConsentService)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider( - this.registeredClientRepository, null, this.authorizationConsentService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + assertThatThrownBy( + () -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(this.registeredClientRepository, null, + this.authorizationConsentService)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void constructorWhenAuthorizationConsentServiceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationProvider( - this.registeredClientRepository, this.authorizationService, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationConsentService cannot be null"); + assertThatThrownBy( + () -> new OAuth2AuthorizationCodeRequestAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationConsentService cannot be null"); } @Test public void supportsWhenTypeOAuth2AuthorizationCodeRequestAuthenticationTokenThenReturnTrue() { - assertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeRequestAuthenticationToken.class)).isTrue(); + assertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeRequestAuthenticationToken.class)) + .isTrue(); } @Test public void setAuthorizationCodeGeneratorWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationCodeGenerator(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationCodeGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationCodeGenerator cannot be null"); } @Test public void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationValidator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationValidator cannot be null"); } @Test public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)); } // gh-243 @@ -150,17 +160,14 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { public void authenticateWhenInvalidRedirectUriHostThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - "https:///invalid", STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, "https:///invalid", STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)); } // gh-243 @@ -168,191 +175,168 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { public void authenticateWhenInvalidRedirectUriFragmentThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - "https://example.com#fragment", STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, "https://example.com#fragment", STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)); } @Test public void authenticateWhenUnregisteredRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - "https://invalid-example.com", STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, "https://invalid-example.com", STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)); } // gh-243 @Test public void authenticateWhenRedirectUriIPv4LoopbackAndDifferentPortThenReturnAuthorizationCode() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUri("https://127.0.0.1:8080") - .build(); + .redirectUri("https://127.0.0.1:8080") + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - "https://127.0.0.1:5000", STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, "https://127.0.0.1:5000", STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); } // gh-243 @Test public void authenticateWhenRedirectUriIPv6LoopbackAndDifferentPortThenReturnAuthorizationCode() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUri("https://[::1]:8080") - .build(); + .redirectUri("https://[::1]:8080") + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - "https://[::1]:5000", STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, "https://[::1]:5000", STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); } @Test public void authenticateWhenMissingRedirectUriAndMultipleRegisteredThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUri("https://example2.com").build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient() + .redirectUri("https://example2.com") + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - null, STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, null, STATE, registeredClient.getScopes(), + null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)); } @Test public void authenticateWhenAuthenticationRequestMissingRedirectUriThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { // redirect_uri is REQUIRED for OpenID Connect requests - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scope(OidcScopes.OPENID) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - null, STATE, registeredClient.getScopes(), null); + .thenReturn(registeredClient); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, null, STATE, registeredClient.getScopes(), + null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.REDIRECT_URI, null)); } @Test public void authenticateWhenClientNotAuthorizedToRequestCodeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantTypes(Set::clear) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .build(); + .authorizationGrantTypes(Set::clear) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID, authentication.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.UNAUTHORIZED_CLIENT, OAuth2ParameterNames.CLIENT_ID, + authentication.getRedirectUri())); } @Test public void authenticateWhenInvalidScopeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, - Collections.singleton("invalid-scope"), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + Collections.singleton("invalid-scope"), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authentication.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authentication.getRedirectUri())); } @Test public void authenticateWhenPkceRequiredAndMissingCodeChallengeThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder().requireProofKey(true).build()) - .build(); + .clientSettings(ClientSettings.builder().requireProofKey(true).build()) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, authentication.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE, + authentication.getRedirectUri())); } @Test public void authenticateWhenPkceUnsupportedCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0]; Map additionalParameters = new HashMap<>(); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge"); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "unsupported"); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), additionalParameters); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), additionalParameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, + authentication.getRedirectUri())); } // gh-770 @@ -360,37 +344,34 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { public void authenticateWhenPkceMissingCodeChallengeMethodThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2]; Map additionalParameters = new HashMap<>(); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge"); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), additionalParameters); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), additionalParameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, authentication.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, PkceParameterNames.CODE_CHALLENGE_METHOD, + authentication.getRedirectUri())); } @Test public void authenticateWhenPrincipalNotAuthenticatedThenReturnAuthorizationCodeRequest() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); this.principal.setAuthenticated(false); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult).isSameAs(authentication); assertThat(authenticationResult.isAuthenticated()).isFalse(); @@ -399,25 +380,25 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { @Test public void authenticateWhenRequireAuthorizationConsentThenReturnAuthorizationConsent() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .build(); + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationConsentAuthenticationToken authenticationResult = - (OAuth2AuthorizationConsentAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationConsentAuthenticationToken authenticationResult = (OAuth2AuthorizationConsentAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization authorization = authorizationCaptor.getValue(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); assertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); assertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE); assertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(authentication.getAuthorizationUri()); @@ -446,126 +427,125 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { @Test public void authenticateWhenRequireAuthorizationConsentAndOnlyOpenidScopeRequestedThenAuthorizationConsentNotRequired() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .scopes(scopes -> { - scopes.clear(); - scopes.add(OidcScopes.OPENID); - }) - .build(); + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .scopes(scopes -> { + scopes.clear(); + scopes.add(OidcScopes.OPENID); + }) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); } @Test public void authenticateWhenRequireAuthorizationConsentAndAllPreviouslyApprovedThenAuthorizationConsentNotRequired() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .build(); + .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) + .build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OAuth2AuthorizationConsent.Builder builder = - OAuth2AuthorizationConsent.withId(registeredClient.getId(), this.principal.getName()); + OAuth2AuthorizationConsent.Builder builder = OAuth2AuthorizationConsent.withId(registeredClient.getId(), + this.principal.getName()); registeredClient.getScopes().forEach(builder::scope); OAuth2AuthorizationConsent previousAuthorizationConsent = builder.build(); when(this.authorizationConsentService.findById(eq(registeredClient.getId()), eq(this.principal.getName()))) - .thenReturn(previousAuthorizationConsent); + .thenReturn(previousAuthorizationConsent); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); } @Test public void authenticateWhenAuthorizationCodeRequestValidThenReturnAuthorizationCode() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[0]; Map additionalParameters = new HashMap<>(); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE, "code-challenge"); additionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), additionalParameters); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), additionalParameters); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); } @Test public void authenticateWhenAuthorizationCodeNotGeneratedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); @SuppressWarnings("unchecked") OAuth2TokenGenerator authorizationCodeGenerator = mock(OAuth2TokenGenerator.class); this.authenticationProvider.setAuthorizationCodeGenerator(authorizationCodeGenerator); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[1]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .extracting(ex -> ((OAuth2AuthorizationCodeRequestAuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the authorization code."); - }); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .extracting(ex -> ((OAuth2AuthorizationCodeRequestAuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()) + .contains("The token generator failed to generate the authorization code."); + }); } @Test public void authenticateWhenCustomAuthenticationValidatorThenUsed() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); @SuppressWarnings("unchecked") Consumer authenticationValidator = mock(Consumer.class); this.authenticationProvider.setAuthenticationValidator(authenticationValidator); String redirectUri = registeredClient.getRedirectUris().toArray(new String[0])[2]; - OAuth2AuthorizationCodeRequestAuthenticationToken authentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - redirectUri, STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, redirectUri, STATE, + registeredClient.getScopes(), null); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, authenticationResult); + assertAuthorizationCodeRequestWithAuthorizationCodeResult(registeredClient, authentication, + authenticationResult); verify(authenticationValidator).accept(any()); } - private void assertAuthorizationCodeRequestWithAuthorizationCodeResult( - RegisteredClient registeredClient, + private void assertAuthorizationCodeRequestWithAuthorizationCodeResult(RegisteredClient registeredClient, OAuth2AuthorizationCodeRequestAuthenticationToken authentication, OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) { @@ -573,7 +553,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization authorization = authorizationCaptor.getValue(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); assertThat(authorizationRequest.getGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); assertThat(authorizationRequest.getResponseType()).isEqualTo(OAuth2AuthorizationResponseType.CODE); assertThat(authorizationRequest.getAuthorizationUri()).isEqualTo(authentication.getAuthorizationUri()); @@ -588,7 +569,8 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { assertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); assertThat(authorization.getAttribute(Principal.class.getName())).isEqualTo(this.principal); - OAuth2Authorization.Token authorizationCode = authorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCode = authorization + .getToken(OAuth2AuthorizationCode.class); Set authorizedScopes = authorization.getAuthorizedScopes(); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); @@ -601,15 +583,16 @@ public class OAuth2AuthorizationCodeRequestAuthenticationProviderTests { assertThat(authenticationResult.isAuthenticated()).isTrue(); } - private static void assertAuthenticationException(OAuth2AuthorizationCodeRequestAuthenticationException authenticationException, - String errorCode, String parameterName, String redirectUri) { + private static void assertAuthenticationException( + OAuth2AuthorizationCodeRequestAuthenticationException authenticationException, String errorCode, + String parameterName, String redirectUri) { OAuth2Error error = authenticationException.getError(); assertThat(error.getErrorCode()).isEqualTo(errorCode); assertThat(error.getDescription()).contains(parameterName); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authenticationException.getAuthorizationCodeRequestAuthentication(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationException + .getAuthorizationCodeRequestAuthentication(); assertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationTokenTests.java index 0e4cc6b8..da01589a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationCodeRequestAuthenticationTokenTests.java @@ -37,46 +37,47 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2AuthorizationCodeRequestAuthenticationTokenTests { + private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize"; + private static final RegisteredClient REGISTERED_CLIENT = TestRegisteredClients.registeredClient().build(); - private static final TestingAuthenticationToken PRINCIPAL = new TestingAuthenticationToken("principalName", "password"); - private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = - new OAuth2AuthorizationCode("code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES)); + + private static final TestingAuthenticationToken PRINCIPAL = new TestingAuthenticationToken("principalName", + "password"); + + private static final OAuth2AuthorizationCode AUTHORIZATION_CODE = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plus(5, ChronoUnit.MINUTES)); @Test public void constructorWhenAuthorizationUriNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - new OAuth2AuthorizationCodeRequestAuthenticationToken(null, REGISTERED_CLIENT.getClientId(), PRINCIPAL, - null, null, (Set) null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationUri cannot be empty"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(null, + REGISTERED_CLIENT.getClientId(), PRINCIPAL, null, null, (Set) null, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationUri cannot be empty"); } @Test public void constructorWhenClientIdNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, null, PRINCIPAL, - null, null, (Set) null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientId cannot be empty"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, null, + PRINCIPAL, null, null, (Set) null, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientId cannot be empty"); } @Test public void constructorWhenPrincipalNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, REGISTERED_CLIENT.getClientId(), null, - null, null, (Set) null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("principal cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, + REGISTERED_CLIENT.getClientId(), null, null, null, (Set) null, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("principal cannot be null"); } @Test public void constructorWhenAuthorizationCodeNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, REGISTERED_CLIENT.getClientId(), PRINCIPAL, - null, null, null, (Set) null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationCode cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationCodeRequestAuthenticationToken(AUTHORIZATION_URI, + REGISTERED_CLIENT.getClientId(), PRINCIPAL, null, null, null, (Set) null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationCode cannot be null"); } @Test diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContextTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContextTests.java index 21ccfa65..43887dd4 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContextTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationContextTests.java @@ -37,65 +37,62 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2AuthorizationConsentAuthenticationContextTests { + private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); + + private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient) + .build(); + private final Authentication principal = this.authorization.getAttribute(Principal.class.getName()); - private final OAuth2AuthorizationRequest authorizationRequest = this.authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - private final OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = - new OAuth2AuthorizationConsentAuthenticationToken( - this.authorizationRequest.getAuthorizationUri(), this.registeredClient.getClientId(), - this.principal, "state", null, null); - private final OAuth2AuthorizationConsent.Builder authorizationConsentBuilder = - OAuth2AuthorizationConsent.withId(this.authorization.getRegisteredClientId(), this.authorization.getPrincipalName()); + + private final OAuth2AuthorizationRequest authorizationRequest = this.authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + + private final OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = new OAuth2AuthorizationConsentAuthenticationToken( + this.authorizationRequest.getAuthorizationUri(), this.registeredClient.getClientId(), this.principal, + "state", null, null); + + private final OAuth2AuthorizationConsent.Builder authorizationConsentBuilder = OAuth2AuthorizationConsent + .withId(this.authorization.getRegisteredClientId(), this.authorization.getPrincipalName()); @Test public void withWhenAuthenticationNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OAuth2AuthorizationConsentAuthenticationContext.with(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authentication cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authentication cannot be null"); } @Test public void setWhenValueNullThenThrowIllegalArgumentException() { - OAuth2AuthorizationConsentAuthenticationContext.Builder builder = - OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication); + OAuth2AuthorizationConsentAuthenticationContext.Builder builder = OAuth2AuthorizationConsentAuthenticationContext + .with(this.authorizationConsentAuthentication); - assertThatThrownBy(() -> builder.authorizationConsent(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.registeredClient(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.authorization(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.authorizationRequest(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.put(null, "")) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorizationConsent(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.registeredClient(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorization(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorizationRequest(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.put(null, "")).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRequiredValueNullThenThrowIllegalArgumentException() { - OAuth2AuthorizationConsentAuthenticationContext.Builder builder = - OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication); + OAuth2AuthorizationConsentAuthenticationContext.Builder builder = OAuth2AuthorizationConsentAuthenticationContext + .with(this.authorizationConsentAuthentication); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationConsentBuilder cannot be null"); + assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationConsentBuilder cannot be null"); builder.authorizationConsent(this.authorizationConsentBuilder); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClient cannot be null"); + assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClient cannot be null"); builder.registeredClient(this.registeredClient); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorization cannot be null"); + assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorization cannot be null"); builder.authorization(this.authorization); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationRequest cannot be null"); + assertThatThrownBy(builder::build).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationRequest cannot be null"); builder.authorizationRequest(this.authorizationRequest); builder.build(); @@ -103,15 +100,15 @@ public class OAuth2AuthorizationConsentAuthenticationContextTests { @Test public void buildWhenAllValuesProvidedThenAllValuesAreSet() { - OAuth2AuthorizationConsentAuthenticationContext context = - OAuth2AuthorizationConsentAuthenticationContext.with(this.authorizationConsentAuthentication) - .authorizationConsent(this.authorizationConsentBuilder) - .registeredClient(this.registeredClient) - .authorization(this.authorization) - .authorizationRequest(this.authorizationRequest) - .put("custom-key-1", "custom-value-1") - .context(ctx -> ctx.put("custom-key-2", "custom-value-2")) - .build(); + OAuth2AuthorizationConsentAuthenticationContext context = OAuth2AuthorizationConsentAuthenticationContext + .with(this.authorizationConsentAuthentication) + .authorizationConsent(this.authorizationConsentBuilder) + .registeredClient(this.registeredClient) + .authorization(this.authorization) + .authorizationRequest(this.authorizationRequest) + .put("custom-key-1", "custom-value-1") + .context(ctx -> ctx.put("custom-key-2", "custom-value-2")) + .build(); assertThat(context.getAuthentication()).isEqualTo(this.authorizationConsentAuthentication); assertThat(context.getAuthorizationConsent()).isEqualTo(this.authorizationConsentBuilder); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProviderTests.java index ccc4fd4d..a2687abf 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2AuthorizationConsentAuthenticationProviderTests.java @@ -61,13 +61,21 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OAuth2AuthorizationConsentAuthenticationProviderTests { + private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize"; + private static final String STATE = "state"; + private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2AuthorizationConsentAuthenticationProvider authenticationProvider; + private TestingAuthenticationToken principal; @BeforeEach @@ -79,32 +87,35 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { this.registeredClientRepository, this.authorizationService, this.authorizationConsentService); this.principal = new TestingAuthenticationToken("principalName", "password"); this.principal.setAuthenticated(true); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); } @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider( - null, this.authorizationService, this.authorizationConsentService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(null, this.authorizationService, + this.authorizationConsentService)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider( - this.registeredClientRepository, null, this.authorizationConsentService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(this.registeredClientRepository, + null, this.authorizationConsentService)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void constructorWhenAuthorizationConsentServiceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider( - this.registeredClientRepository, this.authorizationService, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationConsentService cannot be null"); + assertThatThrownBy(() -> new OAuth2AuthorizationConsentAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationConsentService cannot be null"); } @Test @@ -115,239 +126,212 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { @Test public void setAuthorizationCodeGeneratorWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationCodeGenerator(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationCodeGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationCodeGenerator cannot be null"); } @Test public void setAuthorizationConsentCustomizerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setAuthorizationConsentCustomizer(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationConsentCustomizer cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationConsentCustomizer cannot be null"); } @Test public void authenticateWhenInvalidStateThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, registeredClient.getScopes(), null); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, registeredClient.getScopes(), + null); when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(null); + .thenReturn(null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)); } @Test public void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, registeredClient.getScopes(), null); + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, registeredClient.getScopes(), + null); when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); this.principal.setAuthenticated(false); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)); } @Test public void authenticateWhenInvalidPrincipalThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName().concat("-other")) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, registeredClient.getScopes(), null); + .principalName(this.principal.getName().concat("-other")) + .build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, registeredClient.getScopes(), + null); when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.STATE, null)); } @Test public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); - RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2() - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, otherRegisteredClient.getClientId(), principal, - STATE, registeredClient.getScopes(), null); + .principalName(this.principal.getName()) + .build(); + when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE))).thenReturn(authorization); + RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2().build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, otherRegisteredClient.getClientId(), principal, STATE, registeredClient.getScopes(), + null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)); } @Test public void authenticateWhenDoesNotMatchClientThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); - RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2() - .build(); + .thenReturn(registeredClient); + RegisteredClient otherRegisteredClient = TestRegisteredClients.registeredClient2().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(otherRegisteredClient) - .principalName(this.principal.getName()) - .build(); - when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, registeredClient.getScopes(), null); + .principalName(this.principal.getName()) + .build(); + when(this.authorizationService.findByToken(eq("state"), eq(STATE_TOKEN_TYPE))).thenReturn(authorization); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, registeredClient.getScopes(), + null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_REQUEST, OAuth2ParameterNames.CLIENT_ID, null)); } @Test public void authenticateWhenScopeNotRequestedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set authorizedScopes = new HashSet<>(authorizationRequest.getScopes()); authorizedScopes.add("scope-not-requested"); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, authorizedScopes, null); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, authorizedScopes, null); when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationRequest.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, authorizationRequest.getRedirectUri())); } @Test public void authenticateWhenNotApprovedThenThrowOAuth2AuthorizationCodeRequestAuthenticationException() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, new HashSet<>(), null); // No scopes approved + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, new HashSet<>(), null); // No + // scopes + // approved when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, + authorizationRequest.getRedirectUri())); verify(this.authorizationService).remove(eq(authorization)); } @Test public void authenticateWhenApproveAllThenReturnAuthorizationCode() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set authorizedScopes = authorizationRequest.getScopes(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, authorizedScopes, null); // Approve all scopes + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, authorizedScopes, null); // Approve + // all + // scopes when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult); + assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, + authenticationResult); } @Test public void authenticateWhenCustomAuthorizationConsentCustomizerThenUsed() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set authorizedScopes = authorizationRequest.getScopes(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, authorizedScopes, null); // Approve all scopes + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, authorizedScopes, null); // Approve + // all + // scopes when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); @SuppressWarnings("unchecked") Consumer authorizationConsentCustomizer = mock(Consumer.class); this.authenticationProvider.setAuthorizationConsentCustomizer(authorizationConsentCustomizer); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, authenticationResult); + assertAuthorizationConsentRequestWithAuthorizationCodeResult(registeredClient, authorization, + authenticationResult); - ArgumentCaptor authenticationContextCaptor = - ArgumentCaptor.forClass(OAuth2AuthorizationConsentAuthenticationContext.class); + ArgumentCaptor authenticationContextCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationConsentAuthenticationContext.class); verify(authorizationConsentCustomizer).accept(authenticationContextCaptor.capture()); OAuth2AuthorizationConsentAuthenticationContext authenticationContext = authenticationContextCaptor.getValue(); @@ -358,14 +342,14 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { assertThat(authenticationContext.getAuthorizationRequest()).isEqualTo(authorizationRequest); } - private void assertAuthorizationConsentRequestWithAuthorizationCodeResult( - RegisteredClient registeredClient, - OAuth2Authorization authorization, - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) { - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + private void assertAuthorizationConsentRequestWithAuthorizationCodeResult(RegisteredClient registeredClient, + OAuth2Authorization authorization, OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult) { + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set authorizedScopes = authorizationRequest.getScopes(); - ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class); + ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationConsent.class); verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture()); OAuth2AuthorizationConsent authorizationConsent = authorizationConsentCaptor.getValue(); @@ -380,12 +364,15 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { assertThat(updatedAuthorization.getRegisteredClientId()).isEqualTo(authorization.getRegisteredClientId()); assertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authorization.getPrincipalName()); - assertThat(updatedAuthorization.getAuthorizationGrantType()).isEqualTo(authorization.getAuthorizationGrantType()); + assertThat(updatedAuthorization.getAuthorizationGrantType()) + .isEqualTo(authorization.getAuthorizationGrantType()); assertThat(updatedAuthorization.getAttribute(Principal.class.getName())) - .isEqualTo(authorization.getAttribute(Principal.class.getName())); - assertThat(updatedAuthorization.getAttribute(OAuth2AuthorizationRequest.class.getName())) - .isEqualTo(authorizationRequest); - OAuth2Authorization.Token authorizationCode = updatedAuthorization.getToken(OAuth2AuthorizationCode.class); + .isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(updatedAuthorization + .getAttribute(OAuth2AuthorizationRequest.class.getName())) + .isEqualTo(authorizationRequest); + OAuth2Authorization.Token authorizationCode = updatedAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCode).isNotNull(); assertThat(updatedAuthorization.getAttribute(OAuth2ParameterNames.STATE)).isNull(); assertThat(updatedAuthorization.getAuthorizedScopes()).isEqualTo(authorizedScopes); @@ -404,42 +391,42 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { public void authenticateWhenApproveNoneAndRevokePreviouslyApprovedThenAuthorizationConsentRemoved() { String previouslyApprovedScope = "message.read"; String requestedScope = "message.write"; - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add(previouslyApprovedScope); - scopes.add(requestedScope); - }) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add(previouslyApprovedScope); + scopes.add(requestedScope); + }).build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, new HashSet<>(), null); // No scopes approved + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, new HashSet<>(), null); // No + // scopes + // approved when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); - OAuth2AuthorizationConsent previousAuthorizationConsent = - OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) - .scope(previouslyApprovedScope) - .build(); - when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName()))) - .thenReturn(previousAuthorizationConsent); + .thenReturn(authorization); + OAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent + .withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) + .scope(previouslyApprovedScope) + .build(); + when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), + eq(authorization.getPrincipalName()))) + .thenReturn(previousAuthorizationConsent); // Revoke all (including previously approved) - this.authenticationProvider.setAuthorizationConsentCustomizer((authorizationConsentContext) -> - authorizationConsentContext.getAuthorizationConsent().authorities(Set::clear)); + this.authenticationProvider.setAuthorizationConsentCustomizer( + (authorizationConsentContext) -> authorizationConsentContext.getAuthorizationConsent() + .authorities(Set::clear)); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) - .satisfies(ex -> - assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, - OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, authorizationRequest.getRedirectUri()) - ); + .isInstanceOf(OAuth2AuthorizationCodeRequestAuthenticationException.class) + .satisfies(ex -> assertAuthenticationException((OAuth2AuthorizationCodeRequestAuthenticationException) ex, + OAuth2ErrorCodes.ACCESS_DENIED, OAuth2ParameterNames.CLIENT_ID, + authorizationRequest.getRedirectUri())); verify(this.authorizationConsentService).remove(eq(previousAuthorizationConsent)); verify(this.authorizationService).remove(eq(authorization)); @@ -450,45 +437,46 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { String previouslyApprovedScope = "message.read"; String requestedScope = "message.write"; String otherPreviouslyApprovedScope = "other.scope"; - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add(previouslyApprovedScope); - scopes.add(requestedScope); - }) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add(previouslyApprovedScope); + scopes.add(requestedScope); + }).build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); Set requestedScopes = authorizationRequest.getScopes(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, requestedScopes, null); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, requestedScopes, null); when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); - OAuth2AuthorizationConsent previousAuthorizationConsent = - OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) - .scope(previouslyApprovedScope) - .scope(otherPreviouslyApprovedScope) - .build(); - when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName()))) - .thenReturn(previousAuthorizationConsent); + .thenReturn(authorization); + OAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent + .withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) + .scope(previouslyApprovedScope) + .scope(otherPreviouslyApprovedScope) + .build(); + when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), + eq(authorization.getPrincipalName()))) + .thenReturn(previousAuthorizationConsent); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor.forClass(OAuth2AuthorizationConsent.class); + ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationConsent.class); verify(this.authorizationConsentService).save(authorizationConsentCaptor.capture()); OAuth2AuthorizationConsent updatedAuthorizationConsent = authorizationConsentCaptor.getValue(); - assertThat(updatedAuthorizationConsent.getRegisteredClientId()).isEqualTo(previousAuthorizationConsent.getRegisteredClientId()); - assertThat(updatedAuthorizationConsent.getPrincipalName()).isEqualTo(previousAuthorizationConsent.getPrincipalName()); - assertThat(updatedAuthorizationConsent.getScopes()).containsExactlyInAnyOrder( - previouslyApprovedScope, otherPreviouslyApprovedScope, requestedScope); + assertThat(updatedAuthorizationConsent.getRegisteredClientId()) + .isEqualTo(previousAuthorizationConsent.getRegisteredClientId()); + assertThat(updatedAuthorizationConsent.getPrincipalName()) + .isEqualTo(previousAuthorizationConsent.getPrincipalName()); + assertThat(updatedAuthorizationConsent.getScopes()).containsExactlyInAnyOrder(previouslyApprovedScope, + otherPreviouslyApprovedScope, requestedScope); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); @@ -501,47 +489,47 @@ public class OAuth2AuthorizationConsentAuthenticationProviderTests { public void authenticateWhenApproveNoneAndPreviouslyApprovedThenAuthorizationConsentNotUpdated() { String previouslyApprovedScope = "message.read"; String requestedScope = "message.write"; - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add(previouslyApprovedScope); - scopes.add(requestedScope); - }) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add(previouslyApprovedScope); + scopes.add(requestedScope); + }).build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(this.principal.getName()) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authentication = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, new HashSet<>(), null); // No scopes approved + .principalName(this.principal.getName()) + .build(); + OAuth2AuthorizationConsentAuthenticationToken authentication = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, new HashSet<>(), null); // No + // scopes + // approved when(this.authorizationService.findByToken(eq(authentication.getState()), eq(STATE_TOKEN_TYPE))) - .thenReturn(authorization); - OAuth2AuthorizationConsent previousAuthorizationConsent = - OAuth2AuthorizationConsent.withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) - .scope(previouslyApprovedScope) - .build(); - when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), eq(authorization.getPrincipalName()))) - .thenReturn(previousAuthorizationConsent); + .thenReturn(authorization); + OAuth2AuthorizationConsent previousAuthorizationConsent = OAuth2AuthorizationConsent + .withId(authorization.getRegisteredClientId(), authorization.getPrincipalName()) + .scope(previouslyApprovedScope) + .build(); + when(this.authorizationConsentService.findById(eq(authorization.getRegisteredClientId()), + eq(authorization.getPrincipalName()))) + .thenReturn(previousAuthorizationConsent); - OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = - (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AuthorizationCodeRequestAuthenticationToken authenticationResult = (OAuth2AuthorizationCodeRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationConsentService, never()).save(any()); assertThat(authenticationResult.getScopes()).isEqualTo(Collections.singleton(previouslyApprovedScope)); } - private static void assertAuthenticationException(OAuth2AuthorizationCodeRequestAuthenticationException authenticationException, - String errorCode, String parameterName, String redirectUri) { + private static void assertAuthenticationException( + OAuth2AuthorizationCodeRequestAuthenticationException authenticationException, String errorCode, + String parameterName, String redirectUri) { OAuth2Error error = authenticationException.getError(); assertThat(error.getErrorCode()).isEqualTo(errorCode); assertThat(error.getDescription()).contains(parameterName); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - authenticationException.getAuthorizationCodeRequestAuthentication(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = authenticationException + .getAuthorizationCodeRequestAuthentication(); assertThat(authorizationCodeRequestAuthentication.getRedirectUri()).isEqualTo(redirectUri); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationTokenTests.java index 07d59d50..d2949582 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientAuthenticationTokenTests.java @@ -34,23 +34,25 @@ public class OAuth2ClientAuthenticationTokenTests { @Test public void constructorWhenClientIdNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2ClientAuthenticationToken(null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, "secret", null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientId cannot be empty"); + assertThatThrownBy(() -> new OAuth2ClientAuthenticationToken(null, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, "secret", null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientId cannot be empty"); } @Test public void constructorWhenClientAuthenticationMethodNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2ClientAuthenticationToken("clientId", null, "clientSecret", null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientAuthenticationMethod cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientAuthenticationMethod cannot be null"); } @Test public void constructorWhenRegisteredClientNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2ClientAuthenticationToken(null, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, "clientSecret")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClient cannot be null"); + assertThatThrownBy(() -> new OAuth2ClientAuthenticationToken(null, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, "clientSecret")) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClient cannot be null"); } @Test @@ -61,18 +63,21 @@ public class OAuth2ClientAuthenticationTokenTests { assertThat(authentication.getPrincipal().toString()).isEqualTo("clientId"); assertThat(authentication.getCredentials()).isEqualTo("secret"); assertThat(authentication.getRegisteredClient()).isNull(); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } @Test public void constructorWhenRegisteredClientProvidedThenCreated() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); assertThat(authentication.isAuthenticated()).isTrue(); assertThat(authentication.getPrincipal().toString()).isEqualTo(registeredClient.getClientId()); assertThat(authentication.getCredentials().toString()).isEqualTo(registeredClient.getClientSecret()); assertThat(authentication.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java index 8f66092b..076d541c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java @@ -72,11 +72,17 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2ClientCredentialsAuthenticationProviderTests { + private OAuth2AuthorizationService authorizationService; + private JwtEncoder jwtEncoder; + private OAuth2TokenCustomizer jwtCustomizer; + private OAuth2TokenCustomizer accessTokenCustomizer; + private OAuth2TokenGenerator tokenGenerator; + private OAuth2ClientCredentialsAuthenticationProvider authenticationProvider; @BeforeEach @@ -89,18 +95,21 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { this.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); accessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer); - OAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator); + OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, + accessTokenGenerator); this.tokenGenerator = spy(new OAuth2TokenGenerator() { @Override public OAuth2Token generate(OAuth2TokenContext context) { return delegatingTokenGenerator.generate(context); } }); - this.authenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider( - this.authorizationService, this.tokenGenerator); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + this.authenticationProvider = new OAuth2ClientCredentialsAuthenticationProvider(this.authorizationService, + this.tokenGenerator); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); } @AfterEach @@ -111,15 +120,15 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationProvider(null, this.tokenGenerator)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test public void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationProvider(this.authorizationService, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenGenerator cannot be null"); } @Test @@ -135,129 +144,128 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( - registeredClient.getClientId(), registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(), + registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientNotAuthorizedToRequestTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2() - .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS)) - .build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.CLIENT_CREDENTIALS)) + .build(); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); } @Test public void authenticateWhenInvalidScopeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( clientPrincipal, Collections.singleton("invalid-scope"), null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE); } @Test public void authenticateWhenScopeRequestedThenAccessTokenContainsScope() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); Set requestedScope = Collections.singleton("scope1"); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, requestedScope, null); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, requestedScope, null); - when(this.jwtEncoder.encode(any())) - .thenReturn(createJwt(Collections.singleton("mapped-scoped"))); + when(this.jwtEncoder.encode(any())).thenReturn(createJwt(Collections.singleton("mapped-scoped"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScope); } @Test public void authenticateWhenNoScopeRequestedThenAccessTokenDoesNotContainScope() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); - when(this.jwtEncoder.encode(any())) - .thenReturn(createJwt(Collections.singleton("mapped-scoped"))); + when(this.jwtEncoder.encode(any())).thenReturn(createJwt(Collections.singleton("mapped-scoped"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEmpty(); } @Test public void authenticateWhenAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); doReturn(null).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); + }); } @Test public void authenticateWhenValidAuthenticationThenReturnAccessToken() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); when(this.jwtEncoder.encode(any())).thenReturn(createJwt(registeredClient.getScopes())); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture()); @@ -267,7 +275,8 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { assertThat(jwtEncodingContext.getAuthorization()).isNull(); assertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); - assertThat(jwtEncodingContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(jwtEncodingContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(jwtEncodingContext.getJwsHeader()).isNotNull(); assertThat(jwtEncodingContext.getClaims()).isNotNull(); @@ -280,7 +289,8 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { assertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.CLIENT_CREDENTIALS); assertThat(authorization.getAccessToken()).isNotNull(); assertThat(authorization.getAuthorizedScopes()).isNotNull(); - assertThat(authorization.getAccessToken().getToken().getScopes()).isEqualTo(authorization.getAuthorizedScopes()); + assertThat(authorization.getAccessToken().getToken().getScopes()) + .isEqualTo(authorization.getAuthorizedScopes()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(authorization.getAccessToken().getToken()); } @@ -294,10 +304,10 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { .build()) .build(); // @formatter:on - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken authentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); this.authenticationProvider.authenticate(authentication); @@ -308,10 +318,11 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); return Jwt.withTokenValue("token") - .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) - .issuedAt(issuedAt) - .expiresAt(expiresAt) - .claim(OAuth2ParameterNames.SCOPE, scope) - .build(); + .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) + .issuedAt(issuedAt) + .expiresAt(expiresAt) + .claim(OAuth2ParameterNames.SCOPE, scope) + .build(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java index 34e56617..4a12286e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationTokenTests.java @@ -35,17 +35,23 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Alexey Nesterov */ public class OAuth2ClientCredentialsAuthenticationTokenTests { + private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); + private final OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + this.registeredClient.getClientSecret()); + private Set scopes = Collections.singleton("scope1"); + private Map additionalParameters = Collections.singletonMap("param1", "value1"); @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2ClientCredentialsAuthenticationToken(null, this.scopes, this.additionalParameters)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + assertThatThrownBy( + () -> new OAuth2ClientCredentialsAuthenticationToken(null, this.scopes, this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test @@ -73,4 +79,5 @@ public class OAuth2ClientCredentialsAuthenticationTokenTests { assertThat(authentication.getScopes()).isEqualTo(expectedScopes); assertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java index 19352a1c..8f279d9d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationConsentAuthenticationProviderTests.java @@ -65,14 +65,21 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @author Steve Riesenberg */ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { + private static final String AUTHORIZATION_URI = "/oauth2/device_authorization"; + private static final String DEVICE_CODE = "EfYu_0jEL"; + private static final String USER_CODE = "BCDF-GHJK"; + private static final String STATE = "abc123"; private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2DeviceAuthorizationConsentAuthenticationProvider authenticationProvider; @BeforeEach @@ -125,7 +132,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { @Test public void supportsWhenTypeOAuth2DeviceAuthorizationConsentAuthenticationTokenThenReturnTrue() { - assertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationConsentAuthenticationToken.class)).isTrue(); + assertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationConsentAuthenticationToken.class)) + .isTrue(); } @Test @@ -234,8 +242,10 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { @Test public void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient().scopes(Set::clear) - .scope("invalid").build(); + RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() + .scopes(Set::clear) + .scope("invalid") + .build(); OAuth2Authorization authorization = createAuthorization(registeredClient); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); when(this.registeredClientRepository.findByClientId(anyString())).thenReturn(registeredClient); @@ -299,8 +309,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { when(this.registeredClientRepository.findByClientId(anyString())).thenReturn(registeredClient); Authentication authentication = createAuthentication(registeredClient); - OAuth2DeviceVerificationAuthenticationToken authenticationResult = - (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal()); @@ -333,8 +343,10 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { @Test public void authenticateWhenExistingAuthorizationConsentThenUpdated() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope("additional").build(); - RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient().scopes(Set::clear) - .scope("additional").build(); + RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() + .scopes(Set::clear) + .scope("additional") + .build(); OAuth2Authorization authorization = createAuthorization(registeredClient2); Authentication authentication = createAuthentication(registeredClient2); // @formatter:off @@ -346,15 +358,15 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { when(this.registeredClientRepository.findByClientId(anyString())).thenReturn(registeredClient); when(this.authorizationConsentService.findById(anyString(), anyString())).thenReturn(authorizationConsent); - OAuth2DeviceVerificationAuthenticationToken authenticationResult = - (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal()); assertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE); - ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor.forClass( - OAuth2AuthorizationConsent.class); + ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationConsent.class); verify(this.authorizationService).findByToken(STATE, STATE_TOKEN_TYPE); verify(this.registeredClientRepository).findByClientId(registeredClient.getClientId()); verify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName()); @@ -372,8 +384,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { @Test public void authenticateWhenAuthorizationConsentCustomizerSetThenUsed() { SimpleGrantedAuthority customAuthority = new SimpleGrantedAuthority("test"); - this.authenticationProvider.setAuthorizationConsentCustomizer((context) -> context.getAuthorizationConsent() - .authority(customAuthority)); + this.authenticationProvider.setAuthorizationConsentCustomizer( + (context) -> context.getAuthorizationConsent().authority(customAuthority)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(Set::clear).build(); OAuth2Authorization authorization = createAuthorization(registeredClient); @@ -382,15 +394,15 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { when(this.registeredClientRepository.findByClientId(anyString())).thenReturn(registeredClient); when(this.authorizationConsentService.findById(anyString(), anyString())).thenReturn(null); - OAuth2DeviceVerificationAuthenticationToken authenticationResult = - (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getPrincipal()).isSameAs(authentication.getPrincipal()); assertThat(authenticationResult.getUserCode()).isEqualTo(USER_CODE); - ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor.forClass( - OAuth2AuthorizationConsent.class); + ArgumentCaptor authorizationConsentCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationConsent.class); verify(this.authorizationService).findByToken(STATE, STATE_TOKEN_TYPE); verify(this.registeredClientRepository).findByClientId(registeredClient.getClientId()); verify(this.authorizationConsentService).findById(registeredClient.getId(), authentication.getName()); @@ -417,8 +429,10 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { // @formatter:on } - private static OAuth2DeviceAuthorizationConsentAuthenticationToken createAuthentication(RegisteredClient registeredClient) { - TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", null, Collections.emptyList()); + private static OAuth2DeviceAuthorizationConsentAuthenticationToken createAuthentication( + RegisteredClient registeredClient) { + TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", null, + Collections.emptyList()); Set authorizedScopes = registeredClient.getScopes(); if (authorizedScopes.isEmpty()) { authorizedScopes = null; @@ -441,4 +455,5 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationProviderTests { private static Function, Boolean> isInvalidated() { return (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java index d94fe9e6..3719da77 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceAuthorizationRequestAuthenticationProviderTests.java @@ -62,11 +62,15 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @author Steve Riesenberg */ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { + private static final String AUTHORIZATION_URI = "/oauth2/device_authorization"; + private static final String DEVICE_CODE = "EfYu_0jEL"; + private static final String USER_CODE = "BCDF-GHJK"; private OAuth2AuthorizationService authorizationService; + private OAuth2DeviceAuthorizationRequestAuthenticationProvider authenticationProvider; @BeforeEach @@ -111,15 +115,16 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { @Test public void supportsWhenTypeOAuth2DeviceAuthorizationRequestAuthenticationTokenThenReturnTrue() { - assertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationRequestAuthenticationToken.class)).isTrue(); + assertThat(this.authenticationProvider.supports(OAuth2DeviceAuthorizationRequestAuthenticationToken.class)) + .isTrue(); } @Test public void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() { - OAuth2ClientAuthenticationToken clientPrincipal = - new OAuth2ClientAuthenticationToken("client-1", ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); - OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = - new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, AUTHORIZATION_URI, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken("client-1", + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); + OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = new OAuth2DeviceAuthorizationRequestAuthenticationToken( + clientPrincipal, AUTHORIZATION_URI, null, null); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) .isThrownBy(() -> this.authenticationProvider.authenticate(authentication)) @@ -146,7 +151,8 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { @Test public void authenticateWhenInvalidScopesThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null); Authentication authentication = new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, @@ -169,7 +175,8 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { this.authenticationProvider.setDeviceCodeGenerator(deviceCodeGenerator); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -192,7 +199,8 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { when(userCodeGenerator.generate(any(OAuth2TokenContext.class))).thenReturn(null); this.authenticationProvider.setUserCodeGenerator(userCodeGenerator); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -211,14 +219,16 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { @Test public void authenticateWhenScopesRequestedThenReturnDeviceCodeAndUserCode() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); - OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); assertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes()); assertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128); - assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); // 8 chars + 1 dash + // 8 chars + 1 dash + assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); @@ -231,20 +241,23 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { assertThat(authorization.getToken(OAuth2DeviceCode.class)).isNotNull(); assertThat(authorization.getToken(OAuth2UserCode.class)).isNotNull(); assertThat(authorization.>getAttribute(OAuth2ParameterNames.SCOPE)) - .hasSameElementsAs(registeredClient.getScopes()); + .hasSameElementsAs(registeredClient.getScopes()); } @Test public void authenticateWhenNoScopesRequestedThenReturnDeviceCodeAndUserCode() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(Set::clear) - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient() + .scopes(Set::clear) + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); - OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); assertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes()); assertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128); - assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); // 8 chars + 1 dash + // 8 chars + 1 dash + assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); @@ -257,7 +270,7 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { assertThat(authorization.getToken(OAuth2DeviceCode.class)).isNotNull(); assertThat(authorization.getToken(OAuth2UserCode.class)).isNotNull(); assertThat(authorization.>getAttribute(OAuth2ParameterNames.SCOPE)) - .hasSameElementsAs(registeredClient.getScopes()); + .hasSameElementsAs(registeredClient.getScopes()); } @Test @@ -268,14 +281,16 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { this.authenticationProvider.setDeviceCodeGenerator(deviceCodeGenerator); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); - OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); assertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes()); assertThat(authenticationResult.getDeviceCode().getTokenValue()).isEqualTo(DEVICE_CODE); - assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); // 8 chars + 1 dash + // 8 chars + 1 dash + assertThat(authenticationResult.getUserCode().getTokenValue()).hasSize(9); ArgumentCaptor tokenContextCaptor = ArgumentCaptor.forClass(OAuth2TokenContext.class); verify(deviceCodeGenerator).generate(tokenContextCaptor.capture()); @@ -299,10 +314,11 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { this.authenticationProvider.setUserCodeGenerator(userCodeGenerator); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE).build(); + .authorizationGrantType(AuthorizationGrantType.DEVICE_CODE) + .build(); Authentication authentication = createAuthentication(registeredClient); - OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); assertThat(authenticationResult.getScopes()).hasSameElementsAs(registeredClient.getScopes()); assertThat(authenticationResult.getDeviceCode().getTokenValue()).hasSize(128); @@ -329,14 +345,16 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { AuthorizationServerContextHolder.setContext(authorizationServerContext); } - private static OAuth2DeviceAuthorizationRequestAuthenticationToken createAuthentication(RegisteredClient registeredClient) { + private static OAuth2DeviceAuthorizationRequestAuthenticationToken createAuthentication( + RegisteredClient registeredClient) { OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null); Set requestedScopes = registeredClient.getScopes(); if (requestedScopes.isEmpty()) { requestedScopes = null; } - return new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, AUTHORIZATION_URI, requestedScopes, null); + return new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, AUTHORIZATION_URI, + requestedScopes, null); } private static OAuth2DeviceCode createDeviceCode() { @@ -348,4 +366,5 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationProviderTests { Instant issuedAt = Instant.now(); return new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES)); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java index 7d2720a8..ac445ff5 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceCodeAuthenticationProviderTests.java @@ -71,13 +71,19 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @author Steve Riesenberg */ public class OAuth2DeviceCodeAuthenticationProviderTests { + private static final String DEVICE_CODE = "EfYu_0jEL"; + private static final String USER_CODE = "BCDF-GHJK"; + private static final String ACCESS_TOKEN = "abc123"; + private static final String REFRESH_TOKEN = "xyz456"; private OAuth2AuthorizationService authorizationService; + private OAuth2TokenGenerator tokenGenerator; + private OAuth2DeviceCodeAuthenticationProvider authenticationProvider; @BeforeEach @@ -120,8 +126,8 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { @Test public void authenticateWhenClientNotAuthenticatedThenThrowOAuth2AuthenticationException() { - OAuth2ClientAuthenticationToken clientPrincipal = - new OAuth2ClientAuthenticationToken("client-1", ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken("client-1", + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, null, null); Authentication authentication = new OAuth2DeviceCodeAuthenticationToken(DEVICE_CODE, clientPrincipal, null); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -156,7 +162,8 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().build(); Authentication authentication = createAuthentication(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient2) - .token(createDeviceCode()).build(); + .token(createDeviceCode()) + .build(); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -185,7 +192,8 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); Authentication authentication = createAuthentication(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(createUserCode()).build(); + .token(createUserCode()) + .build(); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -205,7 +213,9 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); Authentication authentication = createAuthentication(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(createDeviceCode(), withInvalidated()).token(createUserCode(), withInvalidated()).build(); + .token(createDeviceCode(), withInvalidated()) + .token(createUserCode(), withInvalidated()) + .build(); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -225,7 +235,9 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); Authentication authentication = createAuthentication(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(createExpiredDeviceCode()).token(createUserCode(), withInvalidated()).build(); + .token(createExpiredDeviceCode()) + .token(createUserCode(), withInvalidated()) + .build(); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); // @formatter:off assertThatExceptionOfType(OAuth2AuthenticationException.class) @@ -347,8 +359,8 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { OAuth2AccessToken accessToken = createAccessToken(); OAuth2RefreshToken refreshToken = createRefreshToken(); when(this.tokenGenerator.generate(any(OAuth2TokenContext.class))).thenReturn(accessToken, refreshToken); - OAuth2AccessTokenAuthenticationToken authenticationResult = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken authenticationResult = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getRegisteredClient()).isEqualTo(registeredClient); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); assertThat(authenticationResult.getAccessToken()).isEqualTo(accessToken); @@ -413,7 +425,8 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { private static OAuth2AccessToken createAccessToken() { Instant issuedAt = Instant.now(); - return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, ACCESS_TOKEN, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES)); + return new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, ACCESS_TOKEN, issuedAt, + issuedAt.plus(30, ChronoUnit.MINUTES)); } private static OAuth2RefreshToken createRefreshToken() { @@ -428,4 +441,5 @@ public class OAuth2DeviceCodeAuthenticationProviderTests { public static Function, Boolean> isInvalidated() { return (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java index c340af7f..3e97029d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2DeviceVerificationAuthenticationProviderTests.java @@ -68,13 +68,19 @@ import static org.springframework.security.oauth2.server.authorization.authentic * @author Steve Riesenberg */ public class OAuth2DeviceVerificationAuthenticationProviderTests { + private static final String AUTHORIZATION_URI = "/oauth2/device_verification"; + private static final String DEVICE_CODE = "EfYu_0jEL"; + private static final String USER_CODE = "BCDF-GHJK"; private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private OAuth2AuthorizationConsentService authorizationConsentService; + private OAuth2DeviceVerificationAuthenticationProvider authenticationProvider; @BeforeEach @@ -144,11 +150,12 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); TestingAuthenticationToken principal = new TestingAuthenticationToken("user", null); - Authentication authentication = new OAuth2DeviceVerificationAuthenticationToken(principal, USER_CODE, Collections.emptyMap()); + Authentication authentication = new OAuth2DeviceVerificationAuthenticationToken(principal, USER_CODE, + Collections.emptyMap()); when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); - OAuth2DeviceVerificationAuthenticationToken authenticationResult = - (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult).isEqualTo(authentication); assertThat(authenticationResult.isAuthenticated()).isFalse(); @@ -172,8 +179,8 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); when(this.authorizationConsentService.findById(anyString(), anyString())).thenReturn(null); - OAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); @@ -193,7 +200,7 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); assertThat(updatedAuthorization.getAttribute(OAuth2ParameterNames.STATE)) - .isEqualTo(authenticationResult.getState()); + .isEqualTo(authenticationResult.getState()); } @Test @@ -219,8 +226,8 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); when(this.authorizationConsentService.findById(anyString(), anyString())).thenReturn(authorizationConsent); - OAuth2DeviceVerificationAuthenticationToken authenticationResult = - (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceVerificationAuthenticationToken authenticationResult = (OAuth2DeviceVerificationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getPrincipal()).isEqualTo(authentication.getPrincipal()); @@ -238,7 +245,7 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { assertThat(updatedAuthorization.getPrincipalName()).isEqualTo(authentication.getName()); assertThat(updatedAuthorization.getAuthorizedScopes()).hasSameElementsAs(registeredClient.getScopes()); assertThat(updatedAuthorization.getAttribute(Principal.class.getName())) - .isEqualTo(authentication.getPrincipal()); + .isEqualTo(authentication.getPrincipal()); assertThat(updatedAuthorization.getAttribute(OAuth2ParameterNames.STATE)).isNull(); // @formatter:off assertThat(updatedAuthorization.getToken(OAuth2DeviceCode.class)) @@ -273,8 +280,8 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { when(this.authorizationService.findByToken(anyString(), any(OAuth2TokenType.class))).thenReturn(authorization); when(this.authorizationConsentService.findById(anyString(), anyString())).thenReturn(authorizationConsent); - OAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2DeviceAuthorizationConsentAuthenticationToken authenticationResult = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getAuthorizationUri()).isEqualTo(AUTHORIZATION_URI); assertThat(authenticationResult.getClientId()).isEqualTo(registeredClient.getClientId()); @@ -294,7 +301,7 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); assertThat(updatedAuthorization.getAttribute(OAuth2ParameterNames.STATE)) - .isEqualTo(authenticationResult.getState()); + .isEqualTo(authenticationResult.getState()); } private static void mockAuthorizationServerContext() { @@ -323,4 +330,5 @@ public class OAuth2DeviceVerificationAuthenticationProviderTests { private static Function, Boolean> isInvalidated() { return (token) -> token.getMetadata(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java index 61f0452d..311f78ab 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationProviderTests.java @@ -89,11 +89,17 @@ import static org.mockito.Mockito.when; * @since 0.0.3 */ public class OAuth2RefreshTokenAuthenticationProviderTests { + private OAuth2AuthorizationService authorizationService; + private JwtEncoder jwtEncoder; + private OAuth2TokenCustomizer jwtCustomizer; + private OAuth2TokenCustomizer accessTokenCustomizer; + private OAuth2TokenGenerator tokenGenerator; + private OAuth2RefreshTokenAuthenticationProvider authenticationProvider; @BeforeEach @@ -108,18 +114,21 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2AccessTokenGenerator accessTokenGenerator = new OAuth2AccessTokenGenerator(); accessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); - OAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(jwtGenerator, accessTokenGenerator, refreshTokenGenerator); + OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(jwtGenerator, + accessTokenGenerator, refreshTokenGenerator); this.tokenGenerator = spy(new OAuth2TokenGenerator() { @Override public OAuth2Token generate(OAuth2TokenContext context) { return delegatingTokenGenerator.generate(context); } }); - this.authenticationProvider = new OAuth2RefreshTokenAuthenticationProvider( - this.authorizationService, this.tokenGenerator); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + this.authenticationProvider = new OAuth2RefreshTokenAuthenticationProvider(this.authorizationService, + this.tokenGenerator); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); } @AfterEach @@ -130,16 +139,16 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(null, this.tokenGenerator)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("authorizationService cannot be null"); } @Test public void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationProvider(this.authorizationService, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenGenerator cannot be null"); } @Test @@ -156,29 +165,30 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { public void authenticateWhenValidRefreshTokenThenReturnAccessToken() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer).customize(jwtEncodingContextCaptor.capture()); JwtEncodingContext jwtEncodingContext = jwtEncodingContextCaptor.getValue(); assertThat(jwtEncodingContext.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(jwtEncodingContext.getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(jwtEncodingContext.getPrincipal()) + .isEqualTo(authorization.getAttribute(Principal.class.getName())); assertThat(jwtEncodingContext.getAuthorization()).isEqualTo(authorization); assertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(jwtEncodingContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); - assertThat(jwtEncodingContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(jwtEncodingContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(jwtEncodingContext.getJwsHeader()).isNotNull(); assertThat(jwtEncodingContext.getClaims()).isNotNull(); @@ -186,11 +196,14 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId()); + assertThat(accessTokenAuthentication.getRegisteredClient().getId()) + .isEqualTo(updatedAuthorization.getRegisteredClientId()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); assertThat(updatedAuthorization.getAccessToken()).isNotEqualTo(authorization.getAccessToken()); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); // By default, refresh token is reused assertThat(updatedAuthorization.getRefreshToken()).isEqualTo(authorization.getRefreshToken()); } @@ -198,73 +211,81 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenValidRefreshTokenThenReturnIdToken() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); - OidcIdToken authorizedIdToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject("subject") - .issuedAt(Instant.now()) - .expiresAt(Instant.now().plusSeconds(60)) - .claim("sid", "sessionId-1234") - .claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now())) - .build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).token(authorizedIdToken).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + OidcIdToken authorizedIdToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject("subject") + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(60)) + .claim("sid", "sessionId-1234") + .claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now())) + .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) + .token(authorizedIdToken) + .build(); + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor jwtEncodingContextCaptor = ArgumentCaptor.forClass(JwtEncodingContext.class); verify(this.jwtCustomizer, times(2)).customize(jwtEncodingContextCaptor.capture()); // Access Token context JwtEncodingContext accessTokenContext = jwtEncodingContextCaptor.getAllValues().get(0); assertThat(accessTokenContext.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(accessTokenContext.getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(accessTokenContext.getPrincipal()) + .isEqualTo(authorization.getAttribute(Principal.class.getName())); assertThat(accessTokenContext.getAuthorization()).isEqualTo(authorization); assertThat(accessTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(accessTokenContext.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(accessTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); - assertThat(accessTokenContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(accessTokenContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(accessTokenContext.getJwsHeader()).isNotNull(); assertThat(accessTokenContext.getClaims()).isNotNull(); Map claims = new HashMap<>(); accessTokenContext.getClaims().claims(claims::putAll); assertThat(claims).flatExtracting(OAuth2ParameterNames.SCOPE) - .containsExactlyInAnyOrder(OidcScopes.OPENID, "scope1"); + .containsExactlyInAnyOrder(OidcScopes.OPENID, "scope1"); // ID Token context JwtEncodingContext idTokenContext = jwtEncodingContextCaptor.getAllValues().get(1); assertThat(idTokenContext.getRegisteredClient()).isEqualTo(registeredClient); - assertThat(idTokenContext.getPrincipal()).isEqualTo(authorization.getAttribute(Principal.class.getName())); + assertThat(idTokenContext.getPrincipal()) + .isEqualTo(authorization.getAttribute(Principal.class.getName())); assertThat(idTokenContext.getAuthorization()).isNotEqualTo(authorization); assertThat(idTokenContext.getAuthorization().getAccessToken()).isNotEqualTo(authorization.getAccessToken()); assertThat(idTokenContext.getAuthorizedScopes()).isEqualTo(authorization.getAuthorizedScopes()); assertThat(idTokenContext.getTokenType().getValue()).isEqualTo(OidcParameterNames.ID_TOKEN); assertThat(idTokenContext.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.REFRESH_TOKEN); - assertThat(idTokenContext.getAuthorizationGrant()).isEqualTo(authentication); + assertThat(idTokenContext.getAuthorizationGrant()) + .isEqualTo(authentication); assertThat(idTokenContext.getJwsHeader()).isNotNull(); assertThat(idTokenContext.getClaims()).isNotNull(); - verify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token + verify(this.jwtEncoder, times(2)).encode(any()); // Access token and ID Token ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRegisteredClient().getId()).isEqualTo(updatedAuthorization.getRegisteredClientId()); + assertThat(accessTokenAuthentication.getRegisteredClient().getId()) + .isEqualTo(updatedAuthorization.getRegisteredClientId()); assertThat(accessTokenAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(accessTokenAuthentication.getAccessToken()).isEqualTo(updatedAuthorization.getAccessToken().getToken()); + assertThat(accessTokenAuthentication.getAccessToken()) + .isEqualTo(updatedAuthorization.getAccessToken().getToken()); assertThat(updatedAuthorization.getAccessToken()).isNotEqualTo(authorization.getAccessToken()); OAuth2Authorization.Token idToken = updatedAuthorization.getToken(OidcIdToken.class); assertThat(idToken).isNotNull(); assertThat(accessTokenAuthentication.getAdditionalParameters()) - .containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue())); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + .containsExactly(entry(OidcParameterNames.ID_TOKEN, idToken.getToken().getTokenValue())); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); // By default, refresh token is reused assertThat(updatedAuthorization.getRefreshToken()).isEqualTo(authorization.getRefreshToken()); } @@ -272,52 +293,51 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { @Test public void authenticateWhenReuseRefreshTokensFalseThenReturnNewRefreshToken() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) - .build(); + .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); verify(this.authorizationService).save(authorizationCaptor.capture()); OAuth2Authorization updatedAuthorization = authorizationCaptor.getValue(); - assertThat(accessTokenAuthentication.getRefreshToken()).isEqualTo(updatedAuthorization.getRefreshToken().getToken()); + assertThat(accessTokenAuthentication.getRefreshToken()) + .isEqualTo(updatedAuthorization.getRefreshToken().getToken()); assertThat(updatedAuthorization.getRefreshToken()).isNotEqualTo(authorization.getRefreshToken()); } @Test public void authenticateWhenRequestedScopesAuthorizedThenAccessTokenIncludesScopes() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scope("scope2") - .scope("scope3") - .build(); + .scope("scope2") + .scope("scope3") + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); Set authorizedScopes = authorization.getAuthorizedScopes(); Set requestedScopes = new HashSet<>(authorizedScopes); requestedScopes.remove("scope1"); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = (OAuth2AccessTokenAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScopes); } @@ -326,13 +346,12 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { public void authenticateWhenRequestedScopesNotAuthorizedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); Set authorizedScopes = authorization.getAuthorizedScopes(); Set requestedScopes = new HashSet<>(authorizedScopes); requestedScopes.add("unauthorized"); @@ -340,162 +359,158 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, requestedScopes, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_SCOPE); } @Test public void authenticateWhenInvalidRefreshTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( - "invalid", clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken("invalid", + clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( - registeredClient.getClientId(), registeredClient.getClientSecret()); + TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(), + registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( "refresh-token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( "refresh-token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenRefreshTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient2, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient2.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient2, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient2.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenClientNotAuthorizedToRefreshTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN)) - .build(); + .authorizationGrantTypes(grantTypes -> grantTypes.remove(AuthorizationGrantType.REFRESH_TOKEN)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT); } @Test public void authenticateWhenExpiredRefreshTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - OAuth2RefreshToken expiredRefreshToken = new OAuth2RefreshToken( - "expired-refresh-token", Instant.now().minusSeconds(120), Instant.now().minusSeconds(60)); + OAuth2RefreshToken expiredRefreshToken = new OAuth2RefreshToken("expired-refresh-token", + Instant.now().minusSeconds(120), Instant.now().minusSeconds(60)); authorization = OAuth2Authorization.from(authorization).token(expiredRefreshToken).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenRevokedRefreshTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - "refresh-token", Instant.now().minusSeconds(120), Instant.now().plusSeconds(1000)); + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now().minusSeconds(120), + Instant.now().plusSeconds(1000)); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(refreshToken, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) - .build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + .token(refreshToken, (metadata) -> metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true)) + .build(); + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } @Test public void authenticateWhenAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); @@ -503,33 +518,33 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2TokenContext context = answer.getArgument(0); if (OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { return null; - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()).contains("The token generator failed to generate the access token."); + }); } @Test public void authenticateWhenRefreshTokenNotGeneratedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) - .build(); + .tokenSettings(TokenSettings.builder().reuseRefreshTokens(false).build()) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); @@ -537,31 +552,32 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2TokenContext context = answer.getArgument(0); if (OAuth2TokenType.REFRESH_TOKEN.equals(context.getTokenType())) { return null; - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the refresh token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()) + .contains("The token generator failed to generate the refresh token."); + }); } @Test public void authenticateWhenIdTokenNotGeneratedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); @@ -569,18 +585,19 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { OAuth2TokenContext context = answer.getArgument(0); if (OidcParameterNames.ID_TOKEN.equals(context.getTokenType().getValue())) { return null; - } else { + } + else { return answer.callRealMethod(); } }).when(this.tokenGenerator).generate(any()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the ID token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()).contains("The token generator failed to generate the ID token."); + }); } @Test @@ -593,13 +610,12 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { .build(); // @formatter:on OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), eq(OAuth2TokenType.REFRESH_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, null, null); @@ -612,10 +628,11 @@ public class OAuth2RefreshTokenAuthenticationProviderTests { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); return Jwt.withTokenValue("refreshed-access-token") - .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) - .issuedAt(issuedAt) - .expiresAt(expiresAt) - .claim(OAuth2ParameterNames.SCOPE, scope) - .build(); + .header(JoseHeaderNames.ALG, SignatureAlgorithm.RS256.getName()) + .issuedAt(issuedAt) + .expiresAt(expiresAt) + .claim(OAuth2ParameterNames.SCOPE, scope) + .build(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java index fa014879..99c89f9f 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2RefreshTokenAuthenticationTokenTests.java @@ -36,27 +36,34 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @since 0.0.3 */ public class OAuth2RefreshTokenAuthenticationTokenTests { + private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + + private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + private Set scopes = Collections.singleton("scope1"); + private Map additionalParameters = Collections.singletonMap("param1", "value1"); @Test public void constructorWhenRefreshTokenNullOrEmptyThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, this.clientPrincipal, this.scopes, this.additionalParameters)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("refreshToken cannot be empty"); - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", this.clientPrincipal, this.scopes, this.additionalParameters)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("refreshToken cannot be empty"); + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken(null, this.clientPrincipal, this.scopes, + this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("refreshToken cannot be empty"); + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("", this.clientPrincipal, this.scopes, + this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("refreshToken cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("refresh-token", null, this.scopes, this.additionalParameters)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + assertThatThrownBy(() -> new OAuth2RefreshTokenAuthenticationToken("refresh-token", null, this.scopes, + this.additionalParameters)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test @@ -70,4 +77,5 @@ public class OAuth2RefreshTokenAuthenticationTokenTests { assertThat(authentication.getScopes()).isEqualTo(this.scopes); assertThat(authentication.getAdditionalParameters()).isEqualTo(this.additionalParameters); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProviderTests.java index df034997..95c9d848 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationProviderTests.java @@ -57,8 +57,11 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2TokenIntrospectionAuthenticationProviderTests { + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private OAuth2TokenIntrospectionAuthenticationProvider authenticationProvider; @BeforeEach @@ -72,15 +75,16 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationProvider(null, this.authorizationService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationProvider(this.registeredClientRepository, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + assertThatThrownBy( + () -> new OAuth2TokenIntrospectionAuthenticationProvider(this.registeredClientRepository, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test @@ -91,43 +95,46 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( - registeredClient.getClientId(), registeredClient.getClientSecret()); + TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(), + registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( "token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( "token", clientPrincipal, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenInvalidTokenThenNotActive() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( "token", clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); assertThat(authenticationResult.isAuthenticated()).isFalse(); @@ -142,14 +149,14 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); authorization = OAuth2AuthenticationProviderUtils.invalidate(authorization, accessToken); when(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull())) - .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + .thenReturn(authorization); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( accessToken.getTokenValue(), clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); assertThat(authenticationResult.isAuthenticated()).isTrue(); @@ -162,18 +169,20 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); Instant issuedAt = Instant.now().minus(Duration.ofHours(1)); Instant expiresAt = Instant.now().minus(Duration.ofMinutes(1)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", issuedAt, expiresAt); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).token(accessToken).build(); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + issuedAt, expiresAt); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) + .token(accessToken) + .build(); when(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull())) - .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + .thenReturn(authorization); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( accessToken.getTokenValue(), clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); assertThat(authenticationResult.isAuthenticated()).isTrue(); @@ -187,21 +196,21 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { Instant issuedAt = Instant.now(); Instant notBefore = issuedAt.plus(Duration.ofMinutes(5)); Instant expiresAt = issuedAt.plus(Duration.ofHours(1)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", issuedAt, expiresAt); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + issuedAt, expiresAt); Map accessTokenClaims = Collections.singletonMap(OAuth2TokenClaimNames.NBF, notBefore); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, accessToken, accessTokenClaims) - .build(); + .authorization(registeredClient, accessToken, accessTokenClaims) + .build(); when(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull())) - .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + .thenReturn(authorization); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( accessToken.getTokenValue(), clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); assertThat(authenticationResult.isAuthenticated()).isTrue(); @@ -214,9 +223,8 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { RegisteredClient authorizedClient = TestRegisteredClients.registeredClient().build(); Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(Duration.ofHours(1)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", issuedAt, expiresAt, - new HashSet<>(Arrays.asList("scope1", "scope2"))); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + issuedAt, expiresAt, new HashSet<>(Arrays.asList("scope1", "scope2"))); // @formatter:off OAuth2TokenClaimsSet claimsSet = OAuth2TokenClaimsSet.builder() @@ -233,19 +241,19 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { // @formatter:on OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(authorizedClient, accessToken, claimsSet.getClaims()) - .build(); + .authorization(authorizedClient, accessToken, claimsSet.getClaims()) + .build(); when(this.authorizationService.findByToken(eq(accessToken.getTokenValue()), isNull())) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorizedClient.getId()))).thenReturn(authorizedClient); RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( accessToken.getTokenValue(), clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); verify(this.registeredClientRepository).findById(eq(authorizedClient.getId())); @@ -271,16 +279,16 @@ public class OAuth2TokenIntrospectionAuthenticationProviderTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build(); OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); when(this.authorizationService.findByToken(eq(refreshToken.getTokenValue()), isNull())) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorizedClient.getId()))).thenReturn(authorizedClient); RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenIntrospectionAuthenticationToken authentication = new OAuth2TokenIntrospectionAuthenticationToken( refreshToken.getTokenValue(), clientPrincipal, null, null); - OAuth2TokenIntrospectionAuthenticationToken authenticationResult = - (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenIntrospectionAuthenticationToken authenticationResult = (OAuth2TokenIntrospectionAuthenticationToken) this.authenticationProvider + .authenticate(authentication); verify(this.authorizationService).findByToken(eq(authentication.getToken()), isNull()); verify(this.registeredClientRepository).findById(eq(authorizedClient.getId())); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationTokenTests.java index e66bf7ce..1fa87c76 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenIntrospectionAuthenticationTokenTests.java @@ -36,45 +36,52 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2TokenIntrospectionAuthenticationTokenTests { + private String token = "token"; + private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + + private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + private OAuth2TokenIntrospection tokenClaims = OAuth2TokenIntrospection.builder(true).build(); @Test public void constructorWhenTokenNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("token cannot be empty"); + assertThatThrownBy( + () -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, null, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("token cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, null, null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test public void constructorWhenAuthenticatedAndTokenNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, this.tokenClaims)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("token cannot be empty"); + assertThatThrownBy( + () -> new OAuth2TokenIntrospectionAuthenticationToken(null, this.clientPrincipal, this.tokenClaims)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("token cannot be empty"); } @Test public void constructorWhenAuthenticatedAndClientPrincipalNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, null, this.tokenClaims)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test public void constructorWhenAuthenticatedAndTokenClaimsNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, this.clientPrincipal, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenClaims cannot be null"); + assertThatThrownBy( + () -> new OAuth2TokenIntrospectionAuthenticationToken(this.token, this.clientPrincipal, null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenClaims cannot be null"); } @Test diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProviderTests.java index fe29551c..e1560cde 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationProviderTests.java @@ -49,7 +49,9 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2TokenRevocationAuthenticationProviderTests { + private OAuth2AuthorizationService authorizationService; + private OAuth2TokenRevocationAuthenticationProvider authenticationProvider; @BeforeEach @@ -61,8 +63,8 @@ public class OAuth2TokenRevocationAuthenticationProviderTests { @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationProvider(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test @@ -73,40 +75,41 @@ public class OAuth2TokenRevocationAuthenticationProviderTests { @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken( - registeredClient.getClientId(), registeredClient.getClientSecret()); - OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - "token", clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); + TestingAuthenticationToken clientPrincipal = new TestingAuthenticationToken(registeredClient.getClientId(), + registeredClient.getClientSecret()); + OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken("token", + clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenClientPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null); - OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - "token", clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); + registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, + registeredClient.getClientSecret(), null); + OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken("token", + clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenInvalidTokenThenNotRevoked() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - "token", clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); - OAuth2TokenRevocationAuthenticationToken authenticationResult = - (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken("token", + clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); + OAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isFalse(); verify(this.authorizationService, never()).save(any()); } @@ -114,42 +117,39 @@ public class OAuth2TokenRevocationAuthenticationProviderTests { @Test public void authenticateWhenTokenIssuedToAnotherClientThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - TestRegisteredClients.registeredClient2().build()).build(); - when(this.authorizationService.findByToken( - eq("token"), - isNull())) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(TestRegisteredClients.registeredClient2().build()) + .build(); + when(this.authorizationService.findByToken(eq("token"), isNull())).thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - "token", clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken("token", + clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); } @Test public void authenticateWhenValidRefreshTokenThenRevoked() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getRefreshToken().getToken().getTokenValue()), + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); + when(this.authorizationService.findByToken(eq(authorization.getRefreshToken().getToken().getTokenValue()), isNull())) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, OAuth2TokenType.REFRESH_TOKEN.getValue()); + authorization.getRefreshToken().getToken().getTokenValue(), clientPrincipal, + OAuth2TokenType.REFRESH_TOKEN.getValue()); - OAuth2TokenRevocationAuthenticationToken authenticationResult = - (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); @@ -165,20 +165,19 @@ public class OAuth2TokenRevocationAuthenticationProviderTests { @Test public void authenticateWhenValidAccessTokenThenRevoked() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient).build(); - when(this.authorizationService.findByToken( - eq(authorization.getAccessToken().getToken().getTokenValue()), + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); + when(this.authorizationService.findByToken(eq(authorization.getAccessToken().getToken().getTokenValue()), isNull())) - .thenReturn(authorization); + .thenReturn(authorization); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2TokenRevocationAuthenticationToken authentication = new OAuth2TokenRevocationAuthenticationToken( - authorization.getAccessToken().getToken().getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue()); + authorization.getAccessToken().getToken().getTokenValue(), clientPrincipal, + OAuth2TokenType.ACCESS_TOKEN.getValue()); - OAuth2TokenRevocationAuthenticationToken authenticationResult = - (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2TokenRevocationAuthenticationToken authenticationResult = (OAuth2TokenRevocationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); @@ -190,4 +189,5 @@ public class OAuth2TokenRevocationAuthenticationProviderTests { OAuth2Authorization.Token refreshToken = updatedAuthorization.getRefreshToken(); assertThat(refreshToken.isInvalidated()).isFalse(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationTokenTests.java index 0d17a0bd..a87f6b64 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2TokenRevocationAuthenticationTokenTests.java @@ -36,41 +36,46 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2TokenRevocationAuthenticationTokenTests { + private String token = "token"; + private RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - this.registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + + private OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(this.registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, this.registeredClient.getClientSecret()); + private String tokenTypeHint = OAuth2TokenType.ACCESS_TOKEN.getValue(); - private OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, this.token, + + private OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, this.token, Instant.now(), Instant.now().plus(Duration.ofHours(1))); @Test public void constructorWhenTokenNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal, this.tokenTypeHint)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("token cannot be empty"); + assertThatThrownBy( + () -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal, this.tokenTypeHint)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("token cannot be empty"); } @Test public void constructorWhenClientPrincipalNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.token, null, this.tokenTypeHint)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test public void constructorWhenRevokedTokenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(null, this.clientPrincipal)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("revokedToken cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("revokedToken cannot be null"); } @Test public void constructorWhenRevokedTokenAndClientPrincipalNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationAuthenticationToken(this.accessToken, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("clientPrincipal cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("clientPrincipal cannot be null"); } @Test @@ -94,4 +99,5 @@ public class OAuth2TokenRevocationAuthenticationTokenTests { assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.isAuthenticated()).isTrue(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProviderTests.java index ce37157c..062043f0 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/PublicClientAuthenticationProviderTests.java @@ -48,38 +48,43 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class PublicClientAuthenticationProviderTests { - // See RFC 7636: Appendix B. Example for the S256 code_challenge_method + + // See RFC 7636: Appendix B. Example for the S256 code_challenge_method // https://tools.ietf.org/html/rfc7636#appendix-B private static final String S256_CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String S256_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; private static final String AUTHORIZATION_CODE = "code"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private PublicClientAuthenticationProvider authenticationProvider; @BeforeEach public void setUp() { this.registeredClientRepository = mock(RegisteredClientRepository.class); this.authorizationService = mock(OAuth2AuthorizationService.class); - this.authenticationProvider = new PublicClientAuthenticationProvider( - this.registeredClientRepository, this.authorizationService); + this.authenticationProvider = new PublicClientAuthenticationProvider(this.registeredClientRepository, + this.authorizationService); } @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new PublicClientAuthenticationProvider(null, this.authorizationService)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClientRepository cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new PublicClientAuthenticationProvider(this.registeredClientRepository, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationService cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationService cannot be null"); } @Test @@ -91,160 +96,158 @@ public class PublicClientAuthenticationProviderTests { public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( registeredClient.getClientId() + "-invalid", ClientAuthenticationMethod.NONE, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); + }); } @Test public void authenticateWhenUnsupportedClientAuthenticationMethodThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - assertThat(error.getDescription()).contains("authentication_method"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + assertThat(error.getDescription()).contains("authentication_method"); + }); } @Test public void authenticateWhenInvalidCodeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); parameters.put(OAuth2ParameterNames.CODE, "invalid-code"); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CODE); + }); } @Test public void authenticateWhenMissingCodeChallengeThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient) - .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(PkceParameterNames.CODE_CHALLENGE); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(PkceParameterNames.CODE_CHALLENGE); + }); } @Test public void authenticateWhenMissingCodeVerifierThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createAuthorizationCodeTokenParameters(); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); + }); } @Test public void authenticateWhenS256MethodAndInvalidCodeVerifierThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters("invalid-code-verifier"); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); - assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + assertThat(error.getDescription()).contains(PkceParameterNames.CODE_VERIFIER); + }); } @Test public void authenticateWhenS256MethodAndValidCodeVerifierThenAuthenticated() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, createPkceAuthorizationParametersS256()) - .build(); + .authorization(registeredClient, createPkceAuthorizationParametersS256()) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); - OAuth2ClientAuthenticationToken authenticationResult = - (OAuth2ClientAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OAuth2ClientAuthenticationToken authenticationResult = (OAuth2ClientAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.isAuthenticated()).isTrue(); assertThat(authenticationResult.getPrincipal().toString()).isEqualTo(registeredClient.getClientId()); assertThat(authenticationResult.getCredentials()).isNull(); @@ -255,27 +258,28 @@ public class PublicClientAuthenticationProviderTests { public void authenticateWhenUnsupportedCodeChallengeMethodThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); Map authorizationRequestAdditionalParameters = createPkceAuthorizationParametersS256(); // This should never happen: the Authorization endpoint should not allow it - authorizationRequestAdditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, "unsupported-challenge-method"); + authorizationRequestAdditionalParameters.put(PkceParameterNames.CODE_CHALLENGE_METHOD, + "unsupported-challenge-method"); OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(registeredClient, authorizationRequestAdditionalParameters) - .build(); + .authorization(registeredClient, authorizationRequestAdditionalParameters) + .build(); when(this.authorizationService.findByToken(eq(AUTHORIZATION_CODE), eq(AUTHORIZATION_CODE_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); Map parameters = createPkceTokenParameters(S256_CODE_VERIFIER); - OAuth2ClientAuthenticationToken authentication = - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); + OAuth2ClientAuthenticationToken authentication = new OAuth2ClientAuthenticationToken( + registeredClient.getClientId(), ClientAuthenticationMethod.NONE, null, parameters); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_GRANT); } private static Map createAuthorizationCodeTokenParameters() { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepositoryTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepositoryTests.java index fbc44866..b17398ee 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepositoryTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/InMemoryRegisteredClientRepositoryTests.java @@ -35,76 +35,68 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Joe Grandja */ public class InMemoryRegisteredClientRepositoryTests { + private RegisteredClient registration = TestRegisteredClients.registeredClient().build(); private InMemoryRegisteredClientRepository clients = new InMemoryRegisteredClientRepository(this.registration); @Test public void constructorVarargsRegisteredClientWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - RegisteredClient registration = null; - new InMemoryRegisteredClientRepository(registration); - }) - .withMessageContaining("registration cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> { + RegisteredClient registration = null; + new InMemoryRegisteredClientRepository(registration); + }).withMessageContaining("registration cannot be null"); } @Test public void constructorListRegisteredClientWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - List registrations = null; - new InMemoryRegisteredClientRepository(registrations); - }) - .withMessageContaining("registrations cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> { + List registrations = null; + new InMemoryRegisteredClientRepository(registrations); + }).withMessageContaining("registrations cannot be empty"); } @Test public void constructorListRegisteredClientWhenEmptyThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - List registrations = Collections.emptyList(); - new InMemoryRegisteredClientRepository(registrations); - }) - .withMessageContaining("registrations cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> { + List registrations = Collections.emptyList(); + new InMemoryRegisteredClientRepository(registrations); + }).withMessageContaining("registrations cannot be empty"); } @Test public void constructorListRegisteredClientWhenDuplicateIdThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - RegisteredClient anotherRegistrationWithSameId = TestRegisteredClients.registeredClient2() - .id(this.registration.getId()).build(); - List registrations = Arrays.asList(this.registration, anotherRegistrationWithSameId); - new InMemoryRegisteredClientRepository(registrations); - }) - .withMessageStartingWith("Registered client must be unique. Found duplicate identifier:"); + assertThatIllegalArgumentException().isThrownBy(() -> { + RegisteredClient anotherRegistrationWithSameId = TestRegisteredClients.registeredClient2() + .id(this.registration.getId()) + .build(); + List registrations = Arrays.asList(this.registration, anotherRegistrationWithSameId); + new InMemoryRegisteredClientRepository(registrations); + }).withMessageStartingWith("Registered client must be unique. Found duplicate identifier:"); } @Test public void constructorListRegisteredClientWhenDuplicateClientIdThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - RegisteredClient anotherRegistrationWithSameClientId = TestRegisteredClients.registeredClient2() - .clientId(this.registration.getClientId()).build(); - List registrations = Arrays.asList(this.registration, - anotherRegistrationWithSameClientId); - new InMemoryRegisteredClientRepository(registrations); - }) - .withMessageStartingWith("Registered client must be unique. Found duplicate client identifier:"); + assertThatIllegalArgumentException().isThrownBy(() -> { + RegisteredClient anotherRegistrationWithSameClientId = TestRegisteredClients.registeredClient2() + .clientId(this.registration.getClientId()) + .build(); + List registrations = Arrays.asList(this.registration, + anotherRegistrationWithSameClientId); + new InMemoryRegisteredClientRepository(registrations); + }).withMessageStartingWith("Registered client must be unique. Found duplicate client identifier:"); } @Test public void constructorListRegisteredClientWhenDuplicateClientSecretThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> { - RegisteredClient anotherRegistrationWithSameClientSecret = TestRegisteredClients.registeredClient2() - .clientSecret(this.registration.getClientSecret()).build(); - List registrations = Arrays.asList(this.registration, - anotherRegistrationWithSameClientSecret); - new InMemoryRegisteredClientRepository(registrations); - }) - .withMessageStartingWith("Registered client must be unique. Found duplicate client secret for identifier:"); + assertThatIllegalArgumentException().isThrownBy(() -> { + RegisteredClient anotherRegistrationWithSameClientSecret = TestRegisteredClients.registeredClient2() + .clientSecret(this.registration.getClientSecret()) + .build(); + List registrations = Arrays.asList(this.registration, + anotherRegistrationWithSameClientSecret); + new InMemoryRegisteredClientRepository(registrations); + }).withMessageStartingWith("Registered client must be unique. Found duplicate client secret for identifier:"); } @Test @@ -121,9 +113,8 @@ public class InMemoryRegisteredClientRepositoryTests { @Test public void findByIdWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.clients.findById(null)) - .withMessageContaining("id cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> this.clients.findById(null)) + .withMessageContaining("id cannot be empty"); } @Test @@ -140,22 +131,20 @@ public class InMemoryRegisteredClientRepositoryTests { @Test public void findByClientIdWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.clients.findByClientId(null)) - .withMessageContaining("clientId cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> this.clients.findByClientId(null)) + .withMessageContaining("clientId cannot be empty"); } @Test public void saveWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.clients.save(null)) - .withMessageContaining("registeredClient cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(null)) + .withMessageContaining("registeredClient cannot be null"); } @Test public void saveWhenExistingIdThenUpdate() { - RegisteredClient registeredClient = createRegisteredClient( - this.registration.getId(), "client-id-2", "client-secret-2"); + RegisteredClient registeredClient = createRegisteredClient(this.registration.getId(), "client-id-2", + "client-secret-2"); this.clients.save(registeredClient); RegisteredClient savedClient = this.clients.findByClientId(registeredClient.getClientId()); assertThat(savedClient).isEqualTo(registeredClient); @@ -163,20 +152,20 @@ public class InMemoryRegisteredClientRepositoryTests { @Test public void saveWhenExistingClientIdThenThrowIllegalArgumentException() { - RegisteredClient registeredClient = createRegisteredClient( - "client-2", this.registration.getClientId(), "client-secret-2"); - assertThatIllegalArgumentException() - .isThrownBy(() -> this.clients.save(registeredClient)) - .withMessage("Registered client must be unique. Found duplicate client identifier: " + registeredClient.getClientId()); + RegisteredClient registeredClient = createRegisteredClient("client-2", this.registration.getClientId(), + "client-secret-2"); + assertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(registeredClient)) + .withMessage("Registered client must be unique. Found duplicate client identifier: " + + registeredClient.getClientId()); } @Test public void saveWhenExistingClientSecretThenThrowIllegalArgumentException() { - RegisteredClient registeredClient = createRegisteredClient( - "client-2", "client-id-2", this.registration.getClientSecret()); - assertThatIllegalArgumentException() - .isThrownBy(() -> this.clients.save(registeredClient)) - .withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + registeredClient.getId()); + RegisteredClient registeredClient = createRegisteredClient("client-2", "client-id-2", + this.registration.getClientSecret()); + assertThatIllegalArgumentException().isThrownBy(() -> this.clients.save(registeredClient)) + .withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + + registeredClient.getId()); } @Test diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java index a7f39a04..2b46f1c7 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/JdbcRegisteredClientRepositoryTests.java @@ -67,10 +67,15 @@ import static org.mockito.Mockito.verify; * @author Ovidiu Popa */ public class JdbcRegisteredClientRepositoryTests { + private static final String OAUTH2_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE = "/org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql"; + private static final String OAUTH2_CUSTOM_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE = "/org/springframework/security/oauth2/server/authorization/client/custom-oauth2-registered-client-schema.sql"; + private EmbeddedDatabase db; + private JdbcOperations jdbcOperations; + private JdbcRegisteredClientRepository registeredClientRepository; @BeforeEach @@ -114,9 +119,8 @@ public class JdbcRegisteredClientRepositoryTests { @Test public void saveWhenRegisteredClientNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.registeredClientRepository.save(null)) - .withMessageContaining("registeredClient cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(null)) + .withMessageContaining("registeredClient cannot be null"); } @Test @@ -124,19 +128,23 @@ public class JdbcRegisteredClientRepositoryTests { RegisteredClient originalRegisteredClient = TestRegisteredClients.registeredClient().build(); this.registeredClientRepository.save(originalRegisteredClient); - RegisteredClient registeredClient = this.registeredClientRepository.findById( - originalRegisteredClient.getId()); + RegisteredClient registeredClient = this.registeredClientRepository.findById(originalRegisteredClient.getId()); assertThat(registeredClient).isEqualTo(originalRegisteredClient); RegisteredClient updatedRegisteredClient = RegisteredClient.from(originalRegisteredClient) - .clientId("test").clientIdIssuedAt(Instant.now()).clientName("clientName").scope("scope2").build(); + .clientId("test") + .clientIdIssuedAt(Instant.now()) + .clientName("clientName") + .scope("scope2") + .build(); RegisteredClient expectedUpdatedRegisteredClient = RegisteredClient.from(originalRegisteredClient) - .clientName("clientName").scope("scope2").build(); + .clientName("clientName") + .scope("scope2") + .build(); this.registeredClientRepository.save(updatedRegisteredClient); - registeredClient = this.registeredClientRepository.findById( - updatedRegisteredClient.getId()); + registeredClient = this.registeredClientRepository.findById(updatedRegisteredClient.getId()); assertThat(registeredClient).isEqualTo(expectedUpdatedRegisteredClient); assertThat(registeredClient).isNotEqualTo(originalRegisteredClient); } @@ -144,10 +152,9 @@ public class JdbcRegisteredClientRepositoryTests { @Test public void saveWhenNewThenSaved() { RegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder() - .tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256).build() - ) - .build(); + .clientSettings( + ClientSettings.builder().tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256).build()) + .build(); this.registeredClientRepository.save(expectedRegisteredClient); RegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId()); assertThat(registeredClient).isEqualTo(expectedRegisteredClient); @@ -155,8 +162,7 @@ public class JdbcRegisteredClientRepositoryTests { @Test public void saveWhenClientSecretNullThenSaved() { - RegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient() - .clientSecret(null).build(); + RegisteredClient expectedRegisteredClient = TestRegisteredClients.registeredClient().clientSecret(null).build(); this.registeredClientRepository.save(expectedRegisteredClient); RegisteredClient registeredClient = this.registeredClientRepository.findById(expectedRegisteredClient.getId()); assertThat(registeredClient).isEqualTo(expectedRegisteredClient); @@ -165,35 +171,35 @@ public class JdbcRegisteredClientRepositoryTests { @Test public void saveWhenExistingClientIdThenThrowIllegalArgumentException() { RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient() - .id("registration-1") - .clientId("client-1") - .build(); + .id("registration-1") + .clientId("client-1") + .build(); this.registeredClientRepository.save(registeredClient1); RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() - .id("registration-2") - .clientId("client-1") - .build(); - assertThatIllegalArgumentException() - .isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) - .withMessage("Registered client must be unique. Found duplicate client identifier: " + registeredClient2.getClientId()); + .id("registration-2") + .clientId("client-1") + .build(); + assertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) + .withMessage("Registered client must be unique. Found duplicate client identifier: " + + registeredClient2.getClientId()); } @Test public void saveWhenExistingClientSecretThenThrowIllegalArgumentException() { RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient() - .id("registration-1") - .clientId("client-1") - .clientSecret("secret") - .build(); + .id("registration-1") + .clientId("client-1") + .clientSecret("secret") + .build(); this.registeredClientRepository.save(registeredClient1); RegisteredClient registeredClient2 = TestRegisteredClients.registeredClient() - .id("registration-2") - .clientId("client-2") - .clientSecret("secret") - .build(); - assertThatIllegalArgumentException() - .isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) - .withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + registeredClient2.getId()); + .id("registration-2") + .clientId("client-2") + .clientSecret("secret") + .build(); + assertThatIllegalArgumentException().isThrownBy(() -> this.registeredClientRepository.save(registeredClient2)) + .withMessage("Registered client must be unique. Found duplicate client secret for identifier: " + + registeredClient2.getId()); } @Test @@ -201,7 +207,8 @@ public class JdbcRegisteredClientRepositoryTests { RowMapper registeredClientRowMapper = spy(new RegisteredClientRowMapper()); this.registeredClientRepository.setRegisteredClientRowMapper(registeredClientRowMapper); RegisteredClientParametersMapper clientParametersMapper = new RegisteredClientParametersMapper(); - Function> registeredClientParametersMapper = spy(clientParametersMapper); + Function> registeredClientParametersMapper = spy( + clientParametersMapper); this.registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); @@ -261,12 +268,14 @@ public class JdbcRegisteredClientRepositoryTests { @Test public void tableDefinitionWhenCustomThenAbleToOverride() { EmbeddedDatabase db = createDb(OAUTH2_CUSTOM_REGISTERED_CLIENT_SCHEMA_SQL_RESOURCE); - CustomJdbcRegisteredClientRepository registeredClientRepository = new CustomJdbcRegisteredClientRepository(new JdbcTemplate(db)); + CustomJdbcRegisteredClientRepository registeredClientRepository = new CustomJdbcRegisteredClientRepository( + new JdbcTemplate(db)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); registeredClientRepository.save(registeredClient); RegisteredClient foundRegisteredClient1 = registeredClientRepository.findById(registeredClient.getId()); assertThat(foundRegisteredClient1).isEqualTo(registeredClient); - RegisteredClient foundRegisteredClient2 = registeredClientRepository.findByClientId(registeredClient.getClientId()); + RegisteredClient foundRegisteredClient2 = registeredClientRepository + .findByClientId(registeredClient.getClientId()); assertThat(foundRegisteredClient2).isEqualTo(registeredClient); db.shutdown(); } @@ -302,7 +311,8 @@ public class JdbcRegisteredClientRepositoryTests { private static final String TABLE_NAME = "oauth2RegisteredClient"; - private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + " WHERE "; + private static final String LOAD_REGISTERED_CLIENT_SQL = "SELECT " + COLUMN_NAMES + " FROM " + TABLE_NAME + + " WHERE "; // @formatter:off private static final String INSERT_REGISTERED_CLIENT_SQL = "INSERT INTO " + TABLE_NAME @@ -332,12 +342,13 @@ public class JdbcRegisteredClientRepositoryTests { } private RegisteredClient findBy(String filter, Object... args) { - List result = getJdbcOperations().query( - LOAD_REGISTERED_CLIENT_SQL + filter, getRegisteredClientRowMapper(), args); + List result = getJdbcOperations().query(LOAD_REGISTERED_CLIENT_SQL + filter, + getRegisteredClientRowMapper(), args); return !result.isEmpty() ? result.get(0) : null; } private static final class CustomRegisteredClientRowMapper implements RowMapper { + private final ObjectMapper objectMapper = new ObjectMapper(); private CustomRegisteredClientRowMapper() { @@ -351,10 +362,13 @@ public class JdbcRegisteredClientRepositoryTests { public RegisteredClient mapRow(ResultSet rs, int rowNum) throws SQLException { Timestamp clientIdIssuedAt = rs.getTimestamp("clientIdIssuedAt"); Timestamp clientSecretExpiresAt = rs.getTimestamp("clientSecretExpiresAt"); - Set clientAuthenticationMethods = StringUtils.commaDelimitedListToSet(rs.getString("clientAuthenticationMethods")); - Set authorizationGrantTypes = StringUtils.commaDelimitedListToSet(rs.getString("authorizationGrantTypes")); + Set clientAuthenticationMethods = StringUtils + .commaDelimitedListToSet(rs.getString("clientAuthenticationMethods")); + Set authorizationGrantTypes = StringUtils + .commaDelimitedListToSet(rs.getString("authorizationGrantTypes")); Set redirectUris = StringUtils.commaDelimitedListToSet(rs.getString("redirectUris")); - Set postLogoutRedirectUris = StringUtils.commaDelimitedListToSet(rs.getString("postLogoutRedirectUris")); + Set postLogoutRedirectUris = StringUtils + .commaDelimitedListToSet(rs.getString("postLogoutRedirectUris")); Set clientScopes = StringUtils.commaDelimitedListToSet(rs.getString("scopes")); // @formatter:off @@ -386,8 +400,10 @@ public class JdbcRegisteredClientRepositoryTests { private Map parseMap(String data) { try { - return this.objectMapper.readValue(data, new TypeReference>() {}); - } catch (Exception ex) { + return this.objectMapper.readValue(data, new TypeReference>() { + }); + } + catch (Exception ex) { throw new IllegalArgumentException(ex.getMessage(), ex); } } @@ -395,23 +411,30 @@ public class JdbcRegisteredClientRepositoryTests { private static AuthorizationGrantType resolveAuthorizationGrantType(String authorizationGrantType) { if (AuthorizationGrantType.AUTHORIZATION_CODE.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.AUTHORIZATION_CODE; - } else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) { + } + else if (AuthorizationGrantType.CLIENT_CREDENTIALS.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.CLIENT_CREDENTIALS; - } else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) { + } + else if (AuthorizationGrantType.REFRESH_TOKEN.getValue().equals(authorizationGrantType)) { return AuthorizationGrantType.REFRESH_TOKEN; } - return new AuthorizationGrantType(authorizationGrantType); // Custom authorization grant type + // Custom authorization grant type + return new AuthorizationGrantType(authorizationGrantType); } - private static ClientAuthenticationMethod resolveClientAuthenticationMethod(String clientAuthenticationMethod) { + private static ClientAuthenticationMethod resolveClientAuthenticationMethod( + String clientAuthenticationMethod) { if (ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.CLIENT_SECRET_BASIC; - } else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) { + } + else if (ClientAuthenticationMethod.CLIENT_SECRET_POST.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.CLIENT_SECRET_POST; - } else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) { + } + else if (ClientAuthenticationMethod.NONE.getValue().equals(clientAuthenticationMethod)) { return ClientAuthenticationMethod.NONE; } - return new ClientAuthenticationMethod(clientAuthenticationMethod); // Custom client authentication method + // Custom client authentication method + return new ClientAuthenticationMethod(clientAuthenticationMethod); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java index a6b73a89..91d36e33 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/RegisteredClientTests.java @@ -36,27 +36,33 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Anoop Garlapati */ public class RegisteredClientTests { + private static final String ID = "registration-1"; + private static final String CLIENT_ID = "client-1"; + private static final String CLIENT_SECRET = "secret"; + private static final Set REDIRECT_URIS = Collections.singleton("https://example.com"); - private static final Set POST_LOGOUT_REDIRECT_URIS = Collections.singleton("https://example.com/oidc-post-logout"); - private static final Set SCOPES = Collections.unmodifiableSet( - Stream.of("openid", "profile", "email").collect(Collectors.toSet())); - private static final Set CLIENT_AUTHENTICATION_METHODS = - Collections.singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + + private static final Set POST_LOGOUT_REDIRECT_URIS = Collections + .singleton("https://example.com/oidc-post-logout"); + + private static final Set SCOPES = Collections + .unmodifiableSet(Stream.of("openid", "profile", "email").collect(Collectors.toSet())); + + private static final Set CLIENT_AUTHENTICATION_METHODS = Collections + .singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); @Test public void buildWhenAuthorizationGrantTypesNotSetThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test @@ -64,17 +70,17 @@ public class RegisteredClientTests { Instant clientIdIssuedAt = Instant.now(); Instant clientSecretExpiresAt = clientIdIssuedAt.plus(30, ChronoUnit.DAYS); RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientIdIssuedAt(clientIdIssuedAt) - .clientSecret(CLIENT_SECRET) - .clientSecretExpiresAt(clientSecretExpiresAt) - .clientName("client-name") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .postLogoutRedirectUris(postLogoutRedirectUris -> postLogoutRedirectUris.addAll(POST_LOGOUT_REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientIdIssuedAt(clientIdIssuedAt) + .clientSecret(CLIENT_SECRET) + .clientSecretExpiresAt(clientSecretExpiresAt) + .clientName("client-name") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .postLogoutRedirectUris(postLogoutRedirectUris -> postLogoutRedirectUris.addAll(POST_LOGOUT_REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); assertThat(registration.getId()).isEqualTo(ID); assertThat(registration.getClientId()).isEqualTo(CLIENT_ID); @@ -83,7 +89,7 @@ public class RegisteredClientTests { assertThat(registration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt); assertThat(registration.getClientName()).isEqualTo("client-name"); assertThat(registration.getAuthorizationGrantTypes()) - .isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE)); + .isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE)); assertThat(registration.getClientAuthenticationMethods()).isEqualTo(CLIENT_AUTHENTICATION_METHODS); assertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS); assertThat(registration.getPostLogoutRedirectUris()).isEqualTo(POST_LOGOUT_REDIRECT_URIS); @@ -92,270 +98,251 @@ public class RegisteredClientTests { @Test public void buildWhenIdIsNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> RegisteredClient.withId(null)) - .isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(null)).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenClientIdIsNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(null) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(null) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUrisNotProvidedThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUrisConsumerClearsSetThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUri("https://example.com") - .redirectUris(Set::clear) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUri("https://example.com") + .redirectUris(Set::clear) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenClientAuthenticationMethodNotProvidedThenDefaultToBasic() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); assertThat(registration.getClientAuthenticationMethods()) - .isEqualTo(Collections.singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)); + .isEqualTo(Collections.singleton(ClientAuthenticationMethod.CLIENT_SECRET_BASIC)); } @Test public void buildWhenScopeIsEmptyThenScopeNotRequired() { RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .build(); } @Test public void buildWhenScopeConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); assertThat(registration.getScopes()).isEqualTo(SCOPES); } @Test public void buildWhenScopeContainsSpaceThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scope("openid profile") - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scope("openid profile") + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenScopeContainsInvalidCharacterThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scope("an\"invalid\"scope") - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scope("an\"invalid\"scope") + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUriInvalidThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUri("invalid URI") - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUri("invalid URI") + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenRedirectUriContainsFragmentThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUri("https://example.com/page#fragment") - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUri("https://example.com/page#fragment") + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenPostLogoutRedirectUriInvalidThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .postLogoutRedirectUri("invalid URI") - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .postLogoutRedirectUri("invalid URI") + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenPostLogoutRedirectUriContainsFragmentThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> - RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUri("https://example.com") - .postLogoutRedirectUri("https://example.com/index#fragment") - .scopes(scopes -> scopes.addAll(SCOPES)) - .build() - ).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> RegisteredClient.withId(ID) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUri("https://example.com") + .postLogoutRedirectUri("https://example.com/index#fragment") + .scopes(scopes -> scopes.addAll(SCOPES)) + .build()).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenTwoAuthorizationGrantTypesAreProvidedThenBothAreRegistered() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); - assertThat(registration.getAuthorizationGrantTypes()) - .containsExactlyInAnyOrder(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); + assertThat(registration.getAuthorizationGrantTypes()).containsExactlyInAnyOrder( + AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); } @Test public void buildWhenAuthorizationGrantTypesConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantTypes(authorizationGrantTypes -> { - authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); - authorizationGrantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS); - }) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantTypes(authorizationGrantTypes -> { + authorizationGrantTypes.add(AuthorizationGrantType.AUTHORIZATION_CODE); + authorizationGrantTypes.add(AuthorizationGrantType.CLIENT_CREDENTIALS); + }) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); - assertThat(registration.getAuthorizationGrantTypes()) - .containsExactlyInAnyOrder(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); + assertThat(registration.getAuthorizationGrantTypes()).containsExactlyInAnyOrder( + AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); } @Test public void buildWhenAuthorizationGrantTypesConsumerClearsSetThenThrowIllegalArgumentException() { assertThatThrownBy(() -> { RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantTypes(Set::clear) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantTypes(Set::clear) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); }).isInstanceOf(IllegalArgumentException.class); } @Test public void buildWhenTwoClientAuthenticationMethodsAreProvidedThenBothAreRegistered() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); - assertThat(registration.getClientAuthenticationMethods()) - .containsExactlyInAnyOrder(ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST); + assertThat(registration.getClientAuthenticationMethods()).containsExactlyInAnyOrder( + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST); } @Test public void buildWhenClientAuthenticationMethodsConsumerIsProvidedThenConsumerAccepted() { RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethods(clientAuthenticationMethods -> { - clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); - clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST); - }) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethods(clientAuthenticationMethods -> { + clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + clientAuthenticationMethods.add(ClientAuthenticationMethod.CLIENT_SECRET_POST); + }) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); - assertThat(registration.getClientAuthenticationMethods()) - .containsExactlyInAnyOrder(ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST); + assertThat(registration.getClientAuthenticationMethods()).containsExactlyInAnyOrder( + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, ClientAuthenticationMethod.CLIENT_SECRET_POST); } @Test public void buildWhenOverrideIdThenOverridden() { String overriddenId = "override"; RegisteredClient registration = RegisteredClient.withId(ID) - .id(overriddenId) - .clientId(CLIENT_ID) - .clientSecret(CLIENT_SECRET) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .id(overriddenId) + .clientId(CLIENT_ID) + .clientSecret(CLIENT_SECRET) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); assertThat(registration.getId()).isEqualTo(overriddenId); } @@ -396,21 +383,21 @@ public class RegisteredClientTests { String newRedirectUri = "https://another-redirect-uri.com"; String newPostLogoutRedirectUri = "https://another-post-logout-redirect-uri.com"; RegisteredClient updated = RegisteredClient.from(registration) - .clientName(newName) - .clientSecret(newSecret) - .scopes(scopes -> { - scopes.clear(); - scopes.add(newScope); - }) - .redirectUris(redirectUris -> { - redirectUris.clear(); - redirectUris.add(newRedirectUri); - }) - .postLogoutRedirectUris(postLogoutRedirectUris -> { - postLogoutRedirectUris.clear(); - postLogoutRedirectUris.add(newPostLogoutRedirectUri); - }) - .build(); + .clientName(newName) + .clientSecret(newSecret) + .scopes(scopes -> { + scopes.clear(); + scopes.add(newScope); + }) + .redirectUris(redirectUris -> { + redirectUris.clear(); + redirectUris.add(newRedirectUri); + }) + .postLogoutRedirectUris(postLogoutRedirectUris -> { + postLogoutRedirectUris.clear(); + postLogoutRedirectUris.add(newPostLogoutRedirectUri); + }) + .build(); assertThat(registration.getClientName()).isNotEqualTo(newName); assertThat(updated.getClientName()).isEqualTo(newName); @@ -428,23 +415,23 @@ public class RegisteredClientTests { public void buildWhenPublicClientTypeThenDefaultSettings() { Instant clientIdIssuedAt = Instant.now(); RegisteredClient registration = RegisteredClient.withId(ID) - .clientId(CLIENT_ID) - .clientIdIssuedAt(clientIdIssuedAt) - .clientName("client-name") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) - .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) - .scopes(scopes -> scopes.addAll(SCOPES)) - .build(); + .clientId(CLIENT_ID) + .clientIdIssuedAt(clientIdIssuedAt) + .clientName("client-name") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) + .redirectUris(redirectUris -> redirectUris.addAll(REDIRECT_URIS)) + .scopes(scopes -> scopes.addAll(SCOPES)) + .build(); assertThat(registration.getId()).isEqualTo(ID); assertThat(registration.getClientId()).isEqualTo(CLIENT_ID); assertThat(registration.getClientIdIssuedAt()).isEqualTo(clientIdIssuedAt); assertThat(registration.getClientName()).isEqualTo("client-name"); assertThat(registration.getAuthorizationGrantTypes()) - .isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE)); + .isEqualTo(Collections.singleton(AuthorizationGrantType.AUTHORIZATION_CODE)); assertThat(registration.getClientAuthenticationMethods()) - .isEqualTo(Collections.singleton(ClientAuthenticationMethod.NONE)); + .isEqualTo(Collections.singleton(ClientAuthenticationMethod.NONE)); assertThat(registration.getRedirectUris()).isEqualTo(REDIRECT_URIS); assertThat(registration.getScopes()).isEqualTo(SCOPES); assertThat(registration.getClientSettings().isRequireProofKey()).isTrue(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java index 90151201..208f0ccb 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java @@ -29,43 +29,44 @@ public class TestRegisteredClients { public static RegisteredClient.Builder registeredClient() { return RegisteredClient.withId("registration-1") - .clientId("client-1") - .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) - .clientSecret("secret-1") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .redirectUri("https://example.com/callback-1") - .redirectUri("https://example.com/callback-2") - .redirectUri("https://example.com/callback-3") - .postLogoutRedirectUri("https://example.com/oidc-post-logout") - .scope("scope1"); + .clientId("client-1") + .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) + .clientSecret("secret-1") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .redirectUri("https://example.com/callback-1") + .redirectUri("https://example.com/callback-2") + .redirectUri("https://example.com/callback-3") + .postLogoutRedirectUri("https://example.com/oidc-post-logout") + .scope("scope1"); } public static RegisteredClient.Builder registeredClient2() { return RegisteredClient.withId("registration-2") - .clientId("client-2") - .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) - .clientSecret("secret-2") - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) - .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) - .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) - .redirectUri("https://example.com") - .postLogoutRedirectUri("https://example.com/oidc-post-logout") - .scope("scope1") - .scope("scope2"); + .clientId("client-2") + .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) + .clientSecret("secret-2") + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrantType(AuthorizationGrantType.REFRESH_TOKEN) + .authorizationGrantType(AuthorizationGrantType.CLIENT_CREDENTIALS) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC) + .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) + .redirectUri("https://example.com") + .postLogoutRedirectUri("https://example.com/oidc-post-logout") + .scope("scope1") + .scope("scope2"); } public static RegisteredClient.Builder registeredPublicClient() { return RegisteredClient.withId("registration-3") - .clientId("client-3") - .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) - .redirectUri("https://example.com") - .scope("scope1") - .clientSettings(ClientSettings.builder().requireProofKey(true).build()); + .clientId("client-3") + .clientIdIssuedAt(Instant.now().truncatedTo(ChronoUnit.SECONDS)) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) + .redirectUri("https://example.com") + .scope("scope1") + .clientSettings(ClientSettings.builder().requireProofKey(true).build()); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java index ff5adef6..cb632b3e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/OAuth2AuthorizationServerConfigurationTests.java @@ -35,11 +35,9 @@ public class OAuth2AuthorizationServerConfigurationTests { @Test public void assertOrderHighestPrecedence() { - Method authorizationServerSecurityFilterChainMethod = - ClassUtils.getMethod( - OAuth2AuthorizationServerConfiguration.class, - "authorizationServerSecurityFilterChain", - HttpSecurity.class); + Method authorizationServerSecurityFilterChainMethod = ClassUtils.getMethod( + OAuth2AuthorizationServerConfiguration.class, "authorizationServerSecurityFilterChain", + HttpSecurity.class); Integer order = OrderUtils.getOrder(authorizationServerSecurityFilterChainMethod); assertThat(order).isEqualTo(Ordered.HIGHEST_PRECEDENCE); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java index 5487f5a6..7cada95e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configuration/RegisterMissingBeanPostProcessorTests.java @@ -38,6 +38,7 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Steve Riesenberg */ public class RegisterMissingBeanPostProcessorTests { + private final RegisterMissingBeanPostProcessor postProcessor = new RegisterMissingBeanPostProcessor(); @Test @@ -95,6 +96,7 @@ public class RegisterMissingBeanPostProcessorTests { } private static final class SimpleBean { + private final String field; private SimpleBean(String field) { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java index 9c3dfae3..51d19c9e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/JwkSetTests.java @@ -60,9 +60,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class JwkSetTests { + private static final String DEFAULT_JWK_SET_ENDPOINT_URI = "/oauth2/jwks"; + private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static AuthorizationServerSettings authorizationServerSettings; public final SpringTestContext spring = new SpringTestContext(); @@ -78,13 +82,13 @@ public class JwkSetTests { JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK); jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); authorizationServerSettings = AuthorizationServerSettings.builder().jwkSetEndpoint("/test/jwks").build(); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @AfterEach @@ -114,11 +118,11 @@ public class JwkSetTests { private void assertJwkSetRequestThenReturnKeys(String jwkSetEndpointUri) throws Exception { this.mvc.perform(get(jwkSetEndpointUri)) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.keys").isNotEmpty()) - .andExpect(jsonPath("$.keys").isArray()); + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.keys").isNotEmpty()) + .andExpect(jsonPath("$.keys").isArray()); } @EnableWebSecurity @@ -126,8 +130,10 @@ public class JwkSetTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; @@ -165,6 +171,7 @@ public class JwkSetTests { } } + } @EnableWebSecurity @@ -175,6 +182,7 @@ public class JwkSetTests { AuthorizationServerSettings authorizationServerSettings() { return authorizationServerSettings; } + } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java index 23bf9409..8a18cbce 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationCodeGrantTests.java @@ -162,32 +162,51 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2AuthorizationCodeGrantTests { + private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize"; + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; - // See RFC 7636: Appendix B. Example for the S256 code_challenge_method + + // See RFC 7636: Appendix B. Example for the S256 code_challenge_method // https://tools.ietf.org/html/rfc7636#appendix-B private static final String S256_CODE_VERIFIER = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"; + private static final String S256_CODE_CHALLENGE = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"; + private static final String AUTHORITIES_CLAIM = "authorities"; + private static final String STATE_URL_UNENCODED = "awrD0fCnEcTUPFgmyy2SU89HZNcnAJ60ZW6l39YI0KyVjmIZ+004pwm9j55li7BoydXYysH4enZMF21Q"; + private static final String STATE_URL_ENCODED = "awrD0fCnEcTUPFgmyy2SU89HZNcnAJ60ZW6l39YI0KyVjmIZ%2B004pwm9j55li7BoydXYysH4enZMF21Q"; private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private static final OAuth2TokenType STATE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.STATE); private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static NimbusJwtEncoder jwtEncoder; + private static AuthorizationServerSettings authorizationServerSettings; - private static HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + + private static HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + private static AuthenticationConverter authorizationRequestConverter; + private static Consumer> authorizationRequestConvertersConsumer; + private static AuthenticationProvider authorizationRequestAuthenticationProvider; + private static Consumer> authorizationRequestAuthenticationProvidersConsumer; + private static AuthenticationSuccessHandler authorizationResponseHandler; + private static AuthenticationFailureHandler authorizationErrorResponseHandler; + private static SecurityContextRepository securityContextRepository; + private static String consentPage = "/oauth2/consent"; public final SpringTestContext spring = new SpringTestContext(); @@ -216,9 +235,9 @@ public class OAuth2AuthorizationCodeGrantTests { jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); jwtEncoder = new NimbusJwtEncoder(jwkSource); authorizationServerSettings = AuthorizationServerSettings.builder() - .authorizationEndpoint("/test/authorize") - .tokenEndpoint("/test/token") - .build(); + .authorizationEndpoint("/test/authorize") + .tokenEndpoint("/test/token") + .build(); authorizationRequestConverter = mock(AuthenticationConverter.class); authorizationRequestConvertersConsumer = mock(Consumer.class); authorizationRequestAuthenticationProvider = mock(AuthenticationProvider.class); @@ -226,14 +245,15 @@ public class OAuth2AuthorizationCodeGrantTests { authorizationResponseHandler = mock(AuthenticationSuccessHandler.class); authorizationErrorResponseHandler = mock(AuthenticationFailureHandler.class); securityContextRepository = spy(new HttpSessionSecurityContextRepository()); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/oauth2-authorization-consent-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @BeforeEach @@ -260,10 +280,11 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); this.registeredClientRepository.save(registeredClient); - this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .queryParams(getAuthorizationRequestParameters(registeredClient))) - .andExpect(status().isUnauthorized()) - .andReturn(); + .andExpect(status().isUnauthorized()) + .andReturn(); } @Test @@ -272,10 +293,11 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .params(getAuthorizationRequestParameters(registeredClient))) - .andExpect(status().isBadRequest()) - .andReturn(); + this.mvc + .perform( + get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient))) + .andExpect(status().isBadRequest()) + .andReturn(); } @Test @@ -293,27 +315,26 @@ public class OAuth2AuthorizationCodeGrantTests { } private void assertAuthorizationRequestRedirectsToClient(String authorizationEndpointUri) throws Exception { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUris(redirectUris -> { - redirectUris.clear(); - redirectUris.add("https://example.com/callback-1?param=encoded%20parameter%20value"); // gh-1011 - }) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris(redirectUris -> { + redirectUris.clear(); + redirectUris.add("https://example.com/callback-1?param=encoded%20parameter%20value"); // gh-1011 + }).build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(authorizationEndpointUri) - .queryParams(authorizationRequestParameters) - .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(authorizationEndpointUri).queryParams(authorizationRequestParameters).with(user("user"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String redirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); String code = extractParameterFromRedirectUri(redirectedUrl, "code"); assertThat(redirectedUrl).isEqualTo(redirectUri + "&code=" + code + "&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorization).isNotNull(); assertThat(authorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); } @@ -328,8 +349,8 @@ public class OAuth2AuthorizationCodeGrantTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); this.authorizationService.save(authorization); - OAuth2AccessTokenResponse accessTokenResponse = assertTokenRequestReturnsAccessTokenResponse( - registeredClient, authorization, DEFAULT_TOKEN_ENDPOINT_URI); + OAuth2AccessTokenResponse accessTokenResponse = assertTokenRequestReturnsAccessTokenResponse(registeredClient, + authorization, DEFAULT_TOKEN_ENDPOINT_URI); // Assert user authorities was propagated as claim in JWT Jwt jwt = this.jwtDecoder.decode(accessTokenResponse.getAccessToken().getTokenValue()); @@ -353,37 +374,39 @@ public class OAuth2AuthorizationCodeGrantTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); this.authorizationService.save(authorization); - assertTokenRequestReturnsAccessTokenResponse( - registeredClient, authorization, authorizationServerSettings.getTokenEndpoint()); + assertTokenRequestReturnsAccessTokenResponse(registeredClient, authorization, + authorizationServerSettings.getTokenEndpoint()); } private OAuth2AccessTokenResponse assertTokenRequestReturnsAccessTokenResponse(RegisteredClient registeredClient, OAuth2Authorization authorization, String tokenEndpointUri) throws Exception { - MvcResult mvcResult = this.mvc.perform(post(tokenEndpointUri) - .params(getTokenRequestParameters(registeredClient, authorization)) + MvcResult mvcResult = this.mvc + .perform(post(tokenEndpointUri).params(getTokenRequestParameters(registeredClient, authorization)) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andReturn(); + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").isNotEmpty()) + .andReturn(); OAuth2Authorization accessTokenAuthorization = this.authorizationService.findById(authorization.getId()); assertThat(accessTokenAuthorization).isNotNull(); assertThat(accessTokenAuthorization.getAccessToken()).isNotNull(); assertThat(accessTokenAuthorization.getRefreshToken()).isNotNull(); - OAuth2Authorization.Token authorizationCodeToken = accessTokenAuthorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCodeToken = accessTokenAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCodeToken).isNotNull(); - assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)).isEqualTo(true); + assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)) + .isEqualTo(true); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); return accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); } @@ -394,41 +417,48 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); this.registeredClientRepository.save(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .queryParams(getAuthorizationRequestParameters(registeredClient)) .queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE) .queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256") .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); assertThat(redirectedUrl).matches("https://example.com\\?code=.{15,}&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorizationCodeAuthorization).isNotNull(); - assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()) + .isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER)) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").doesNotExist()) - .andExpect(jsonPath("$.scope").isNotEmpty()); + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").doesNotExist()) + .andExpect(jsonPath("$.scope").isNotEmpty()); - OAuth2Authorization accessTokenAuthorization = this.authorizationService.findById(authorizationCodeAuthorization.getId()); + OAuth2Authorization accessTokenAuthorization = this.authorizationService + .findById(authorizationCodeAuthorization.getId()); assertThat(accessTokenAuthorization).isNotNull(); assertThat(accessTokenAuthorization.getAccessToken()).isNotNull(); - OAuth2Authorization.Token authorizationCodeToken = accessTokenAuthorization.getToken(OAuth2AuthorizationCode.class); + OAuth2Authorization.Token authorizationCodeToken = accessTokenAuthorization + .getToken(OAuth2AuthorizationCode.class); assertThat(authorizationCodeToken).isNotNull(); - assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)).isEqualTo(true); + assertThat(authorizationCodeToken.getMetadata().get(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME)) + .isEqualTo(true); } // gh-1430 @@ -486,87 +516,94 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) .queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE) .queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256") .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorizationCodeAuthorization).isNotNull(); - assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()) + .isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()); } // gh-1011 @Test - public void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeThenErrorResponseEncoded() throws Exception { + public void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeThenErrorResponseEncoded() + throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); String redirectUri = "https://example.com/callback-1?param=encoded%20parameter%20value"; - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUris(redirectUris -> { - redirectUris.clear(); - redirectUris.add(redirectUri); - }) - .clientSettings(ClientSettings.builder().requireProofKey(true).build()) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris(redirectUris -> { + redirectUris.clear(); + redirectUris.add(redirectUri); + }).clientSettings(ClientSettings.builder().requireProofKey(true).build()).build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); - String expectedRedirectUri = redirectUri + "&" + - "error=invalid_request&" + - "error_description=" + UriUtils.encode("OAuth 2.0 Parameter: code_challenge", StandardCharsets.UTF_8) + "&" + - "error_uri=" + UriUtils.encode("https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1", StandardCharsets.UTF_8) + "&" + - "state=" + STATE_URL_ENCODED; + String expectedRedirectUri = redirectUri + "&" + "error=invalid_request&" + "error_description=" + + UriUtils.encode("OAuth 2.0 Parameter: code_challenge", StandardCharsets.UTF_8) + "&" + "error_uri=" + + UriUtils.encode("https://datatracker.ietf.org/doc/html/rfc7636#section-4.4.1", StandardCharsets.UTF_8) + + "&" + "state=" + STATE_URL_ENCODED; assertThat(redirectedUrl).isEqualTo(expectedRedirectUri); } @Test - public void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeButCodeVerifierProvidedThenBadRequest() throws Exception { + public void requestWhenConfidentialClientWithPkceAndMissingCodeChallengeButCodeVerifierProvidedThenBadRequest() + throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) - .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) + .with(user("user"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); assertThat(authorizationCodeAuthorization).isNotNull(); - assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); + assertThat(authorizationCodeAuthorization.getAuthorizationGrantType()) + .isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isBadRequest()); + .andExpect(status().isBadRequest()); } @Test @@ -579,10 +616,10 @@ public class OAuth2AuthorizationCodeGrantTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); this.authorizationService.save(authorization); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient, authorization)) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization)) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); verify(this.tokenGenerator, times(2)).generate(any()); } @@ -591,23 +628,21 @@ public class OAuth2AuthorizationCodeGrantTests { public void requestWhenRequiresConsentThenDisplaysConsentPage() throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add("message.read"); - scopes.add("message.write"); - }) - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add("message.read"); + scopes.add("message.write"); + }).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build(); this.registeredClientRepository.save(registeredClient); - String consentPage = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + String consentPage = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .queryParams(getAuthorizationRequestParameters(registeredClient)) .with(user("user"))) - .andExpect(status().is2xxSuccessful()) - .andReturn() - .getResponse() - .getContentAsString(); + .andExpect(status().is2xxSuccessful()) + .andReturn() + .getResponse() + .getContentAsString(); assertThat(consentPage).contains("Consent required"); assertThat(consentPage).contains(scopeCheckbox("message.read")); @@ -618,77 +653,76 @@ public class OAuth2AuthorizationCodeGrantTests { public void requestWhenConsentRequestThenReturnAccessTokenResponse() throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add("message.read"); - scopes.add("message.write"); - }) - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add("message.read"); + scopes.add("message.write"); + }).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build(); this.registeredClientRepository.save(registeredClient); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName("user") - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationRequest updatedAuthorizationRequest = - OAuth2AuthorizationRequest.from(authorizationRequest) - .state(STATE_URL_UNENCODED) - .build(); + .principalName("user") + .build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest updatedAuthorizationRequest = OAuth2AuthorizationRequest.from(authorizationRequest) + .state(STATE_URL_UNENCODED) + .build(); authorization = OAuth2Authorization.from(authorization) - .attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest) - .build(); + .attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest) + .build(); this.authorizationService.save(authorization); - MvcResult mvcResult = this.mvc.perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .param(OAuth2ParameterNames.SCOPE, "message.read") .param(OAuth2ParameterNames.SCOPE, "message.write") .param(OAuth2ParameterNames.STATE, authorization.getAttribute(OAuth2ParameterNames.STATE)) .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); - assertThat(redirectedUrl).matches(authorizationRequest.getRedirectUri() + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); + assertThat(redirectedUrl) + .matches(authorizationRequest.getRedirectUri() + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andReturn(); + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").isNotEmpty()) + .andReturn(); } @Test public void requestWhenCustomConsentPageConfiguredThenRedirect() throws Exception { this.spring.register(AuthorizationServerConfigurationCustomConsentPage.class).autowire(); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add("message.read"); - scopes.add("message.write"); - }) - .clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add("message.read"); + scopes.add("message.write"); + }).clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).build()).build(); this.registeredClientRepository.save(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .queryParams(getAuthorizationRequestParameters(registeredClient)) .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); assertThat(redirectedUrl).matches("http://localhost/oauth2/consent\\?scope=.+&client_id=.+&state=.+"); @@ -698,7 +732,8 @@ public class OAuth2AuthorizationCodeGrantTests { assertThat(uriComponents.getPath()).isEqualTo(consentPage); assertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.SCOPE)).isEqualTo("message.read message.write"); - assertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.CLIENT_ID)).isEqualTo(registeredClient.getClientId()); + assertThat(redirectQueryParams.getFirst(OAuth2ParameterNames.CLIENT_ID)) + .isEqualTo(registeredClient.getClientId()); String state = extractParameterFromRedirectUri(redirectedUrl, "state"); OAuth2Authorization authorization = this.authorizationService.findByToken(state, STATE_TOKEN_TYPE); @@ -710,59 +745,62 @@ public class OAuth2AuthorizationCodeGrantTests { this.spring.register(AuthorizationServerConfigurationCustomConsentRequest.class).autowire(); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientSettings(ClientSettings.builder() - .requireAuthorizationConsent(true) - .setting("custom.allowed-authorities", "authority-1 authority-2") - .build()) - .build(); + .clientSettings(ClientSettings.builder() + .requireAuthorizationConsent(true) + .setting("custom.allowed-authorities", "authority-1 authority-2") + .build()) + .build(); this.registeredClientRepository.save(registeredClient); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .build(); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute(OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationRequest updatedAuthorizationRequest = - OAuth2AuthorizationRequest.from(authorizationRequest) - .state(STATE_URL_UNENCODED) - .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest updatedAuthorizationRequest = OAuth2AuthorizationRequest.from(authorizationRequest) + .state(STATE_URL_UNENCODED) + .build(); authorization = OAuth2Authorization.from(authorization) - .attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest) - .build(); + .attribute(OAuth2AuthorizationRequest.class.getName(), updatedAuthorizationRequest) + .build(); this.authorizationService.save(authorization); - MvcResult mvcResult = this.mvc.perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(post(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .param("authority", "authority-1 authority-2") .param(OAuth2ParameterNames.STATE, authorization.getAttribute(OAuth2ParameterNames.STATE)) .with(user("principal"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); - assertThat(redirectedUrl).matches(authorizationRequest.getRedirectUri() + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); + assertThat(redirectedUrl) + .matches(authorizationRequest.getRedirectUri() + "\\?code=.{15,}&state=" + STATE_URL_ENCODED); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .header(HttpHeaders.AUTHORIZATION, getAuthorizationHeader(registeredClient))) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.access_token").value(new AssertionMatcher() { - @Override - public void assertion(String accessToken) throws AssertionError { - Jwt jwt = jwtDecoder.decode(accessToken); - assertThat(jwt.getClaimAsStringList(AUTHORITIES_CLAIM)) - .containsExactlyInAnyOrder("authority-1", "authority-2"); - } - })) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").doesNotExist()) - .andReturn(); + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.access_token").value(new AssertionMatcher() { + @Override + public void assertion(String accessToken) throws AssertionError { + Jwt jwt = jwtDecoder.decode(accessToken); + assertThat(jwt.getClaimAsStringList(AUTHORITIES_CLAIM)).containsExactlyInAnyOrder("authority-1", + "authority-2"); + } + })) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").doesNotExist()) + .andReturn(); } @Test @@ -771,44 +809,49 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); TestingAuthenticationToken principal = new TestingAuthenticationToken("principalName", "password"); - OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode( - "code", Instant.now(), Instant.now().plus(5, ChronoUnit.MINUTES)); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - "https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal, authorizationCode, - registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED, registeredClient.getScopes()); + OAuth2AuthorizationCode authorizationCode = new OAuth2AuthorizationCode("code", Instant.now(), + Instant.now().plus(5, ChronoUnit.MINUTES)); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + "https://provider.com/oauth2/authorize", registeredClient.getClientId(), principal, authorizationCode, + registeredClient.getRedirectUris().iterator().next(), STATE_URL_UNENCODED, + registeredClient.getScopes()); when(authorizationRequestConverter.convert(any())).thenReturn(authorizationCodeRequestAuthenticationResult); - when(authorizationRequestAuthenticationProvider.supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).thenReturn(true); - when(authorizationRequestAuthenticationProvider.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); + when(authorizationRequestAuthenticationProvider + .supports(eq(OAuth2AuthorizationCodeRequestAuthenticationToken.class))).thenReturn(true); + when(authorizationRequestAuthenticationProvider.authenticate(any())) + .thenReturn(authorizationCodeRequestAuthenticationResult); - this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .params(getAuthorizationRequestParameters(registeredClient)) + this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).params(getAuthorizationRequestParameters(registeredClient)) .with(user("user"))) - .andExpect(status().isOk()); + .andExpect(status().isOk()); verify(authorizationRequestConverter).convert(any()); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authorizationRequestConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).allMatch((converter) -> - converter == authorizationRequestConverter || - converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter || - converter instanceof OAuth2AuthorizationConsentAuthenticationConverter); + assertThat(authenticationConverters).allMatch((converter) -> converter == authorizationRequestConverter + || converter instanceof OAuth2AuthorizationCodeRequestAuthenticationConverter + || converter instanceof OAuth2AuthorizationConsentAuthenticationConverter); - verify(authorizationRequestAuthenticationProvider).authenticate(eq(authorizationCodeRequestAuthenticationResult)); + verify(authorizationRequestAuthenticationProvider) + .authenticate(eq(authorizationCodeRequestAuthenticationResult)); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authorizationRequestAuthenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).allMatch((provider) -> - provider == authorizationRequestAuthenticationProvider || - provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider || - provider instanceof OAuth2AuthorizationConsentAuthenticationProvider); + assertThat(authenticationProviders) + .allMatch((provider) -> provider == authorizationRequestAuthenticationProvider + || provider instanceof OAuth2AuthorizationCodeRequestAuthenticationProvider + || provider instanceof OAuth2AuthorizationConsentAuthenticationProvider); - verify(authorizationResponseHandler).onAuthenticationSuccess(any(), any(), eq(authorizationCodeRequestAuthenticationResult)); + verify(authorizationResponseHandler).onAuthenticationSuccess(any(), any(), + eq(authorizationCodeRequestAuthenticationResult)); } // gh-482 @@ -819,39 +862,44 @@ public class OAuth2AuthorizationCodeGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredPublicClient().build(); this.registeredClientRepository.save(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) .queryParams(getAuthorizationRequestParameters(registeredClient)) .queryParam(PkceParameterNames.CODE_CHALLENGE, S256_CODE_CHALLENGE) .queryParam(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256") .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); - ArgumentCaptor securityContextCaptor = - ArgumentCaptor.forClass(org.springframework.security.core.context.SecurityContext.class); + ArgumentCaptor securityContextCaptor = ArgumentCaptor + .forClass(org.springframework.security.core.context.SecurityContext.class); verify(securityContextRepository, times(1)).saveContext(securityContextCaptor.capture(), any(), any()); - assertThat(securityContextCaptor.getValue().getAuthentication()).isInstanceOf(UsernamePasswordAuthenticationToken.class); + assertThat(securityContextCaptor.getValue().getAuthentication()) + .isInstanceOf(UsernamePasswordAuthenticationToken.class); reset(securityContextRepository); String authorizationCode = extractParameterFromRedirectUri(mvcResult.getResponse().getRedirectedUrl(), "code"); - OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorizationCodeAuthorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .params(getTokenRequestParameters(registeredClient, authorizationCodeAuthorization)) .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .param(PkceParameterNames.CODE_VERIFIER, S256_CODE_VERIFIER)) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").doesNotExist()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andReturn(); + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").doesNotExist()) + .andExpect(jsonPath("$.scope").isNotEmpty()) + .andReturn(); - org.springframework.security.core.context.SecurityContext securityContext = - securityContextRepository.loadDeferredContext(mvcResult.getRequest()).get(); + org.springframework.security.core.context.SecurityContext securityContext = securityContextRepository + .loadDeferredContext(mvcResult.getRequest()) + .get(); assertThat(securityContext.getAuthentication()).isNull(); } @@ -870,7 +918,8 @@ public class OAuth2AuthorizationCodeGrantTests { OAuth2Authorization authorization) { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - parameters.set(OAuth2ParameterNames.CODE, authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); + parameters.set(OAuth2ParameterNames.CODE, + authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); return parameters; } @@ -887,12 +936,11 @@ public class OAuth2AuthorizationCodeGrantTests { private static String scopeCheckbox(String scope) { return MessageFormat.format( - "", - scope - ); + "", scope); } - private String extractParameterFromRedirectUri(String redirectUri, String param) throws UnsupportedEncodingException { + private String extractParameterFromRedirectUri(String redirectUri, String param) + throws UnsupportedEncodingException { String locationHeader = URLDecoder.decode(redirectUri, StandardCharsets.UTF_8.name()); UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build(); return uriComponents.getQueryParams().getFirst(param); @@ -903,21 +951,25 @@ public class OAuth2AuthorizationCodeGrantTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; } @Bean - OAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { + OAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository); } @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; @@ -941,8 +993,8 @@ public class OAuth2AuthorizationCodeGrantTests { @Bean OAuth2TokenCustomizer jwtCustomizer() { return context -> { - if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) && - OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { + if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) + && OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { Authentication principal = context.getPrincipal(); Set authorities = new HashSet<>(); for (GrantedAuthority authority : principal.getAuthorities()) { @@ -1016,7 +1068,9 @@ public class OAuth2AuthorizationCodeGrantTests { @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationWithSecurityContextRepository extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithSecurityContextRepository + extends AuthorizationServerConfiguration { + // @formatter:off @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { @@ -1036,6 +1090,7 @@ public class OAuth2AuthorizationCodeGrantTests { return http.build(); } // @formatter:on + } @EnableWebSecurity @@ -1052,8 +1107,8 @@ public class OAuth2AuthorizationCodeGrantTests { JwtGenerator jwtGenerator = new JwtGenerator(jwtEncoder()); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); - OAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator); + OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator( + jwtGenerator, refreshTokenGenerator); return spy(new OAuth2TokenGenerator() { @Override public OAuth2Token generate(OAuth2TokenContext context) { @@ -1072,11 +1127,13 @@ public class OAuth2AuthorizationCodeGrantTests { AuthorizationServerSettings authorizationServerSettings() { return authorizationServerSettings; } + } @EnableWebSecurity @Configuration(proxyBeanMethods = false) static class AuthorizationServerConfigurationCustomConsentPage extends AuthorizationServerConfiguration { + // @formatter:off @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { @@ -1097,6 +1154,7 @@ public class OAuth2AuthorizationCodeGrantTests { return http.build(); } // @formatter:on + } @EnableWebSecurity @@ -1131,10 +1189,10 @@ public class OAuth2AuthorizationCodeGrantTests { @Override OAuth2TokenCustomizer jwtCustomizer() { return context -> { - if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) && - OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { - OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService.findById( - context.getRegisteredClient().getId(), context.getPrincipal().getName()); + if (AuthorizationGrantType.AUTHORIZATION_CODE.equals(context.getAuthorizationGrantType()) + && OAuth2TokenType.ACCESS_TOKEN.equals(context.getTokenType())) { + OAuth2AuthorizationConsent authorizationConsent = this.authorizationConsentService + .findById(context.getRegisteredClient().getId(), context.getPrincipal().getName()); Set authorities = new HashSet<>(); for (GrantedAuthority authority : authorizationConsent.getAuthorities()) { @@ -1146,25 +1204,25 @@ public class OAuth2AuthorizationCodeGrantTests { } private Consumer> configureAuthenticationProviders() { - return (authenticationProviders) -> - authenticationProviders.forEach((authenticationProvider) -> { - if (authenticationProvider instanceof OAuth2AuthorizationConsentAuthenticationProvider) { - ((OAuth2AuthorizationConsentAuthenticationProvider) authenticationProvider) - .setAuthorizationConsentCustomizer(new AuthorizationConsentCustomizer()); - } - }); + return (authenticationProviders) -> authenticationProviders.forEach((authenticationProvider) -> { + if (authenticationProvider instanceof OAuth2AuthorizationConsentAuthenticationProvider) { + ((OAuth2AuthorizationConsentAuthenticationProvider) authenticationProvider) + .setAuthorizationConsentCustomizer(new AuthorizationConsentCustomizer()); + } + }); } - static class AuthorizationConsentCustomizer implements Consumer { + static class AuthorizationConsentCustomizer + implements Consumer { @Override - public void accept(OAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext) { - OAuth2AuthorizationConsent.Builder authorizationConsentBuilder = - authorizationConsentAuthenticationContext.getAuthorizationConsent(); - OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = - authorizationConsentAuthenticationContext.getAuthentication(); - Map additionalParameters = - authorizationConsentAuthentication.getAdditionalParameters(); + public void accept( + OAuth2AuthorizationConsentAuthenticationContext authorizationConsentAuthenticationContext) { + OAuth2AuthorizationConsent.Builder authorizationConsentBuilder = authorizationConsentAuthenticationContext + .getAuthorizationConsent(); + OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthentication = authorizationConsentAuthenticationContext + .getAuthentication(); + Map additionalParameters = authorizationConsentAuthentication.getAdditionalParameters(); RegisteredClient registeredClient = authorizationConsentAuthenticationContext.getRegisteredClient(); ClientSettings clientSettings = registeredClient.getClientSettings(); @@ -1186,12 +1244,15 @@ public class OAuth2AuthorizationCodeGrantTests { return authorities; } + } + } @EnableWebSecurity @Configuration(proxyBeanMethods = false) static class AuthorizationServerConfigurationCustomAuthorizationEndpoint extends AuthorizationServerConfiguration { + // @formatter:off @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { @@ -1218,6 +1279,7 @@ public class OAuth2AuthorizationCodeGrantTests { return http.build(); } // @formatter:on + } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java index 083f8327..6824ee53 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2AuthorizationServerMetadataTests.java @@ -64,9 +64,13 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2AuthorizationServerMetadataTests { + private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server"; + private static final String ISSUER_URL = "https://example.com"; + private static EmbeddedDatabase db; + private static JWKSource jwkSource; public final SpringTestContext spring = new SpringTestContext(); @@ -81,13 +85,13 @@ public class OAuth2AuthorizationServerMetadataTests { public static void setupClass() { JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK); jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @AfterEach @@ -106,9 +110,9 @@ public class OAuth2AuthorizationServerMetadataTests { this.spring.register(AuthorizationServerConfiguration.class).autowire(); this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath("issuer").value(ISSUER_URL)) - .andReturn(); + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("issuer").value(ISSUER_URL)) + .andReturn(); } @Test @@ -116,20 +120,21 @@ public class OAuth2AuthorizationServerMetadataTests { this.spring.register(AuthorizationServerConfigurationWithIssuerNotSet.class).autowire(); this.mvc.perform(get("http://localhost".concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath("issuer").value("http://localhost")) - .andReturn(); + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath("issuer").value("http://localhost")) + .andReturn(); } // gh-616 @Test - public void requestWhenAuthorizationServerMetadataRequestAndMetadataCustomizerSetThenReturnCustomMetadataResponse() throws Exception { + public void requestWhenAuthorizationServerMetadataRequestAndMetadataCustomizerSetThenReturnCustomMetadataResponse() + throws Exception { this.spring.register(AuthorizationServerConfigurationWithMetadataCustomizer.class).autowire(); this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, - hasItems("scope1", "scope2"))); + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, + hasItems("scope1", "scope2"))); } @EnableWebSecurity @@ -139,7 +144,8 @@ public class OAuth2AuthorizationServerMetadataTests { @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); registeredClientRepository.save(registeredClient); return registeredClientRepository; } @@ -158,6 +164,7 @@ public class OAuth2AuthorizationServerMetadataTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL).build(); } + } @EnableWebSecurity @@ -190,8 +197,7 @@ public class OAuth2AuthorizationServerMetadataTests { // @formatter:on private Consumer authorizationServerMetadataCustomizer() { - return (authorizationServerMetadata) -> - authorizationServerMetadata.scope("scope1").scope("scope2"); + return (authorizationServerMetadata) -> authorizationServerMetadata.scope("scope1").scope("scope2"); } } @@ -204,6 +210,7 @@ public class OAuth2AuthorizationServerMetadataTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().build(); } + } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java index 9b097db8..b3b63d0f 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2ClientCredentialsGrantTests.java @@ -122,15 +122,25 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2ClientCredentialsGrantTests { + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static OAuth2TokenCustomizer jwtCustomizer; + private static AuthenticationConverter authenticationConverter; + private static Consumer> authenticationConvertersConsumer; + private static AuthenticationProvider authenticationProvider; + private static Consumer> authenticationProvidersConsumer; + private static AuthenticationSuccessHandler authenticationSuccessHandler; + private static AuthenticationFailureHandler authenticationFailureHandler; public final SpringTestContext spring = new SpringTestContext(); @@ -155,13 +165,13 @@ public class OAuth2ClientCredentialsGrantTests { authenticationProvidersConsumer = mock(Consumer.class); authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); authenticationFailureHandler = mock(AuthenticationFailureHandler.class); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @SuppressWarnings("unchecked") @@ -191,9 +201,10 @@ public class OAuth2ClientCredentialsGrantTests { public void requestWhenTokenRequestNotAuthenticatedThenUnauthorized() throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); - this.mvc.perform(MockMvcRequestBuilders.post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(MockMvcRequestBuilders.post(DEFAULT_TOKEN_ENDPOINT_URI) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())) - .andExpect(status().isUnauthorized()); + .andExpect(status().isUnauthorized()); } @Test @@ -203,14 +214,15 @@ public class OAuth2ClientCredentialsGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); this.registeredClientRepository.save(registeredClient); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) .param(OAuth2ParameterNames.SCOPE, "scope1 scope2") - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value("scope1 scope2")); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value("scope1 scope2")); verify(jwtCustomizer).customize(any()); } @@ -222,37 +234,43 @@ public class OAuth2ClientCredentialsGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); this.registeredClientRepository.save(registeredClient); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) .param(OAuth2ParameterNames.SCOPE, "scope1 scope2") .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) .param(OAuth2ParameterNames.CLIENT_SECRET, registeredClient.getClientSecret())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value("scope1 scope2")); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value("scope1 scope2")); verify(jwtCustomizer).customize(any()); } @Test - public void requestWhenTokenRequestPostsClientCredentialsAndRequiresUpgradingThenClientSecretUpgraded() throws Exception { + public void requestWhenTokenRequestPostsClientCredentialsAndRequiresUpgradingThenClientSecretUpgraded() + throws Exception { this.spring.register(AuthorizationServerConfigurationCustomPasswordEncoder.class).autowire(); String clientSecret = "secret-2"; - RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().clientSecret("{noop}" + clientSecret).build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient2() + .clientSecret("{noop}" + clientSecret) + .build(); this.registeredClientRepository.save(registeredClient); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .param(OAuth2ParameterNames.SCOPE, "scope1 scope2") - .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) - .param(OAuth2ParameterNames.CLIENT_SECRET, clientSecret)) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value("scope1 scope2")); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) + .param(OAuth2ParameterNames.SCOPE, "scope1 scope2") + .param(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) + .param(OAuth2ParameterNames.CLIENT_SECRET, clientSecret)) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value("scope1 scope2")); verify(jwtCustomizer).customize(any()); - RegisteredClient updatedRegisteredClient = this.registeredClientRepository.findByClientId(registeredClient.getClientId()); + RegisteredClient updatedRegisteredClient = this.registeredClientRepository + .findByClientId(registeredClient.getClientId()); assertThat(updatedRegisteredClient.getClientSecret()).startsWith("{bcrypt}"); } @@ -263,51 +281,51 @@ public class OAuth2ClientCredentialsGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); this.registeredClientRepository.save(registeredClient); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = - new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, null, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = new OAuth2ClientCredentialsAuthenticationToken( + clientPrincipal, null, null); when(authenticationConverter.convert(any())).thenReturn(clientCredentialsAuthentication); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken); when(authenticationProvider.supports(eq(OAuth2ClientCredentialsAuthenticationToken.class))).thenReturn(true); when(authenticationProvider.authenticate(any())).thenReturn(accessTokenAuthentication); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); verify(authenticationConverter).convert(any()); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).allMatch((converter) -> - converter == authenticationConverter || - converter instanceof OAuth2AuthorizationCodeAuthenticationConverter || - converter instanceof OAuth2RefreshTokenAuthenticationConverter || - converter instanceof OAuth2ClientCredentialsAuthenticationConverter || - converter instanceof OAuth2DeviceCodeAuthenticationConverter); + assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter + || converter instanceof OAuth2AuthorizationCodeAuthenticationConverter + || converter instanceof OAuth2RefreshTokenAuthenticationConverter + || converter instanceof OAuth2ClientCredentialsAuthenticationConverter + || converter instanceof OAuth2DeviceCodeAuthenticationConverter); verify(authenticationProvider).authenticate(eq(clientCredentialsAuthentication)); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).allMatch((provider) -> - provider == authenticationProvider || - provider instanceof OAuth2AuthorizationCodeAuthenticationProvider || - provider instanceof OAuth2RefreshTokenAuthenticationProvider || - provider instanceof OAuth2ClientCredentialsAuthenticationProvider || - provider instanceof OAuth2DeviceCodeAuthenticationProvider); + assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider + || provider instanceof OAuth2AuthorizationCodeAuthenticationProvider + || provider instanceof OAuth2RefreshTokenAuthenticationProvider + || provider instanceof OAuth2ClientCredentialsAuthenticationProvider + || provider instanceof OAuth2DeviceCodeAuthenticationProvider); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(accessTokenAuthentication)); } @@ -319,40 +337,41 @@ public class OAuth2ClientCredentialsGrantTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); this.registeredClientRepository.save(registeredClient); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, new ClientAuthenticationMethod("custom"), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + new ClientAuthenticationMethod("custom"), null); when(authenticationConverter.convert(any())).thenReturn(clientPrincipal); when(authenticationProvider.supports(eq(OAuth2ClientAuthenticationToken.class))).thenReturn(true); when(authenticationProvider.authenticate(any())).thenReturn(clientPrincipal); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())) - .andExpect(status().isOk()); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).param(OAuth2ParameterNames.GRANT_TYPE, + AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())) + .andExpect(status().isOk()); verify(authenticationConverter).convert(any()); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).allMatch((converter) -> - converter == authenticationConverter || - converter instanceof JwtClientAssertionAuthenticationConverter || - converter instanceof ClientSecretBasicAuthenticationConverter || - converter instanceof ClientSecretPostAuthenticationConverter || - converter instanceof PublicClientAuthenticationConverter); + assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter + || converter instanceof JwtClientAssertionAuthenticationConverter + || converter instanceof ClientSecretBasicAuthenticationConverter + || converter instanceof ClientSecretPostAuthenticationConverter + || converter instanceof PublicClientAuthenticationConverter); verify(authenticationProvider).authenticate(eq(clientPrincipal)); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).allMatch((provider) -> - provider == authenticationProvider || - provider instanceof JwtClientAssertionAuthenticationProvider || - provider instanceof ClientSecretAuthenticationProvider || - provider instanceof PublicClientAuthenticationProvider); + assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider + || provider instanceof JwtClientAssertionAuthenticationProvider + || provider instanceof ClientSecretAuthenticationProvider + || provider instanceof PublicClientAuthenticationProvider); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(clientPrincipal)); } @@ -370,8 +389,10 @@ public class OAuth2ClientCredentialsGrantTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; @@ -379,7 +400,8 @@ public class OAuth2ClientCredentialsGrantTests { @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; @@ -428,6 +450,7 @@ public class OAuth2ClientCredentialsGrantTests { @EnableWebSecurity @Configuration(proxyBeanMethods = false) static class AuthorizationServerConfigurationCustomTokenEndpoint extends AuthorizationServerConfiguration { + // @formatter:off @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { @@ -454,20 +477,24 @@ public class OAuth2ClientCredentialsGrantTests { return http.build(); } // @formatter:on + } @EnableWebSecurity @Configuration(proxyBeanMethods = false) static class AuthorizationServerConfigurationCustomPasswordEncoder extends AuthorizationServerConfiguration { + @Override PasswordEncoder passwordEncoder() { return PasswordEncoderFactories.createDelegatingPasswordEncoder(); } + } @EnableWebSecurity @Configuration(proxyBeanMethods = false) static class AuthorizationServerConfigurationCustomClientAuthentication extends AuthorizationServerConfiguration { + // @formatter:off @Bean public SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { @@ -500,8 +527,10 @@ public class OAuth2ClientCredentialsGrantTests { private AuthenticationSuccessHandler authenticationSuccessHandler() { return new AuthenticationSuccessHandler() { @Override - public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException { - org.springframework.security.core.context.SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); + public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, + Authentication authentication) throws IOException, ServletException { + org.springframework.security.core.context.SecurityContext securityContext = SecurityContextHolder + .createEmptyContext(); securityContext.setAuthentication(authentication); SecurityContextHolder.setContext(securityContext); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java index f8b03983..88450aa9 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2DeviceCodeGrantTests.java @@ -93,23 +93,28 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2DeviceCodeGrantTests { + private static final String DEFAULT_DEVICE_AUTHORIZATION_ENDPOINT_URI = "/oauth2/device_authorization"; + private static final String DEFAULT_DEVICE_VERIFICATION_ENDPOINT_URI = "/oauth2/device_verification"; + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static final OAuth2TokenType DEVICE_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.DEVICE_CODE); + private static final String USER_CODE = "ABCD-EFGH"; + private static final String STATE = "123"; + private static final String DEVICE_CODE = "abc-XYZ"; private static EmbeddedDatabase db; private static JWKSource jwkSource; - private static final HttpMessageConverter deviceAuthorizationResponseHttpMessageConverter = - new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); + private static final HttpMessageConverter deviceAuthorizationResponseHttpMessageConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); - private static final HttpMessageConverter accessTokenResponseHttpMessageConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + private static final HttpMessageConverter accessTokenResponseHttpMessageConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); public final SpringTestContext spring = new SpringTestContext(); @@ -234,15 +239,14 @@ public class OAuth2DeviceCodeGrantTests { MockHttpServletResponse servletResponse = mvcResult.getResponse(); MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), HttpStatus.OK); - OAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = - deviceAuthorizationResponseHttpMessageConverter.read(OAuth2DeviceAuthorizationResponse.class, - httpResponse); + OAuth2DeviceAuthorizationResponse deviceAuthorizationResponse = deviceAuthorizationResponseHttpMessageConverter + .read(OAuth2DeviceAuthorizationResponse.class, httpResponse); String userCode = deviceAuthorizationResponse.getUserCode().getTokenValue(); assertThat(userCode).matches("[A-Z]{4}-[A-Z]{4}"); assertThat(deviceAuthorizationResponse.getVerificationUri()) - .isEqualTo("http://localhost/oauth2/device_verification"); + .isEqualTo("http://localhost/oauth2/device_verification"); assertThat(deviceAuthorizationResponse.getVerificationUriComplete()) - .isEqualTo("http://localhost/oauth2/device_verification?user_code=" + userCode); + .isEqualTo("http://localhost/oauth2/device_verification?user_code=" + userCode); String deviceCode = deviceAuthorizationResponse.getDeviceCode().getTokenValue(); OAuth2Authorization authorization = this.authorizationService.findByToken(deviceCode, DEVICE_CODE_TOKEN_TYPE); @@ -520,8 +524,8 @@ public class OAuth2DeviceCodeGrantTests { MockHttpServletResponse servletResponse = mvcResult.getResponse(); MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), HttpStatus.OK); - OAuth2AccessTokenResponse accessTokenResponse = - accessTokenResponseHttpMessageConverter.read(OAuth2AccessTokenResponse.class, httpResponse); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenResponseHttpMessageConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); String accessToken = accessTokenResponse.getAccessToken().getTokenValue(); OAuth2Authorization accessTokenAuthorization = this.authorizationService.findByToken(accessToken, diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2RefreshTokenGrantTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2RefreshTokenGrantTests.java index 35533142..23ba2a5c 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2RefreshTokenGrantTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2RefreshTokenGrantTests.java @@ -115,14 +115,20 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2RefreshTokenGrantTests { + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke"; + private static final String AUTHORITIES_CLAIM = "authorities"; + private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static NimbusJwtDecoder jwtDecoder; - private static HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + + private static HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); public final SpringTestContext spring = new SpringTestContext(); @@ -143,13 +149,13 @@ public class OAuth2RefreshTokenGrantTests { JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK); jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); jwtDecoder = NimbusJwtDecoder.withPublicKey(TestKeys.DEFAULT_PUBLIC_KEY).build(); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @AfterEach @@ -173,25 +179,25 @@ public class OAuth2RefreshTokenGrantTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); this.authorizationService.save(authorization); - MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getRefreshTokenRequestParameters(authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andReturn(); + MvcResult mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").isNotEmpty()) + .andReturn(); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read( - OAuth2AccessTokenResponse.class, httpResponse); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); // Assert user authorities was propagated as claim in JWT Jwt jwt = jwtDecoder.decode(accessTokenResponse.getAccessToken().getTokenValue()); @@ -218,17 +224,18 @@ public class OAuth2RefreshTokenGrantTests { OAuth2AccessToken token = authorization.getAccessToken().getToken(); OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN; - this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) .params(getTokenRevocationRequestParameters(token, tokenType)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getRefreshTokenRequestParameters(authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getRefreshTokenRequestParameters(authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId()); OAuth2Authorization.Token accessToken = updatedAuthorization.getAccessToken(); @@ -268,7 +275,8 @@ public class OAuth2RefreshTokenGrantTests { return parameters; } - private static MultiValueMap getTokenRevocationRequestParameters(OAuth2Token token, OAuth2TokenType tokenType) { + private static MultiValueMap getTokenRevocationRequestParameters(OAuth2Token token, + OAuth2TokenType tokenType) { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue()); parameters.set(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenType.getValue()); @@ -288,8 +296,10 @@ public class OAuth2RefreshTokenGrantTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; @@ -297,7 +307,8 @@ public class OAuth2RefreshTokenGrantTests { @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java index 728e29a0..dc53cc78 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenIntrospectionTests.java @@ -120,19 +120,28 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2TokenIntrospectionTests { + private static EmbeddedDatabase db; + private static AuthorizationServerSettings authorizationServerSettings; + private static OAuth2TokenCustomizer accessTokenCustomizer; + private static AuthenticationConverter authenticationConverter; + private static Consumer> authenticationConvertersConsumer; + private static AuthenticationProvider authenticationProvider; + private static Consumer> authenticationProvidersConsumer; + private static AuthenticationSuccessHandler authenticationSuccessHandler; + private static AuthenticationFailureHandler authenticationFailureHandler; - private static final HttpMessageConverter tokenIntrospectionHttpResponseConverter = - new OAuth2TokenIntrospectionHttpMessageConverter(); - private static final HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + + private static final HttpMessageConverter tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter(); + + private static final HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); public final SpringTestContext spring = new SpringTestContext(); @@ -150,7 +159,9 @@ public class OAuth2TokenIntrospectionTests { @BeforeAll public static void init() { - authorizationServerSettings = AuthorizationServerSettings.builder().tokenIntrospectionEndpoint("/test/introspect").build(); + authorizationServerSettings = AuthorizationServerSettings.builder() + .tokenIntrospectionEndpoint("/test/introspect") + .build(); authenticationConverter = mock(AuthenticationConverter.class); authenticationConvertersConsumer = mock(Consumer.class); authenticationProvider = mock(AuthenticationProvider.class); @@ -158,13 +169,13 @@ public class OAuth2TokenIntrospectionTests { authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); authenticationFailureHandler = mock(AuthenticationFailureHandler.class); accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @AfterEach @@ -183,15 +194,15 @@ public class OAuth2TokenIntrospectionTests { this.spring.register(AuthorizationServerConfiguration.class).autowire(); RegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2() - .clientSecret("secret-2").build(); + .clientSecret("secret-2") + .build(); this.registeredClientRepository.save(introspectRegisteredClient); RegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build(); Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(Duration.ofHours(1)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "access-token", issuedAt, expiresAt, - new HashSet<>(Arrays.asList("scope1", "scope2"))); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + issuedAt, expiresAt, new HashSet<>(Arrays.asList("scope1", "scope2"))); // @formatter:off OAuth2TokenClaimsSet accessTokenClaims = OAuth2TokenClaimsSet.builder() .issuer("https://provider.com") @@ -205,8 +216,8 @@ public class OAuth2TokenIntrospectionTests { .build(); // @formatter:on OAuth2Authorization authorization = TestOAuth2Authorizations - .authorization(authorizedRegisteredClient, accessToken, accessTokenClaims.getClaims()) - .build(); + .authorization(authorizedRegisteredClient, accessToken, accessTokenClaims.getClaims()) + .build(); this.registeredClientRepository.save(authorizedRegisteredClient); this.authorizationService.save(authorization); @@ -222,8 +233,8 @@ public class OAuth2TokenIntrospectionTests { assertThat(tokenIntrospectionResponse.isActive()).isTrue(); assertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId()); assertThat(tokenIntrospectionResponse.getUsername()).isNull(); - assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween( - accessTokenClaims.getIssuedAt().minusSeconds(1), accessTokenClaims.getIssuedAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(accessTokenClaims.getIssuedAt().minusSeconds(1), + accessTokenClaims.getIssuedAt().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween( accessTokenClaims.getExpiresAt().minusSeconds(1), accessTokenClaims.getExpiresAt().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(accessToken.getScopes()); @@ -231,7 +242,8 @@ public class OAuth2TokenIntrospectionTests { assertThat(tokenIntrospectionResponse.getNotBefore()).isBetween( accessTokenClaims.getNotBefore().minusSeconds(1), accessTokenClaims.getNotBefore().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(accessTokenClaims.getSubject()); - assertThat(tokenIntrospectionResponse.getAudience()).containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience()); + assertThat(tokenIntrospectionResponse.getAudience()) + .containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience()); assertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(accessTokenClaims.getIssuer()); assertThat(tokenIntrospectionResponse.getId()).isEqualTo(accessTokenClaims.getId()); } @@ -241,7 +253,8 @@ public class OAuth2TokenIntrospectionTests { this.spring.register(AuthorizationServerConfiguration.class).autowire(); RegisteredClient introspectRegisteredClient = TestRegisteredClients.registeredClient2() - .clientSecret("secret-2").build(); + .clientSecret("secret-2") + .build(); this.registeredClientRepository.save(introspectRegisteredClient); RegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient().build(); @@ -262,10 +275,10 @@ public class OAuth2TokenIntrospectionTests { assertThat(tokenIntrospectionResponse.isActive()).isTrue(); assertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId()); assertThat(tokenIntrospectionResponse.getUsername()).isNull(); - assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween( - refreshToken.getIssuedAt().minusSeconds(1), refreshToken.getIssuedAt().plusSeconds(1)); - assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween( - refreshToken.getExpiresAt().minusSeconds(1), refreshToken.getExpiresAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(refreshToken.getIssuedAt().minusSeconds(1), + refreshToken.getIssuedAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(refreshToken.getExpiresAt().minusSeconds(1), + refreshToken.getExpiresAt().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getScopes()).isNull(); assertThat(tokenIntrospectionResponse.getTokenType()).isNull(); assertThat(tokenIntrospectionResponse.getNotBefore()).isNull(); @@ -316,7 +329,8 @@ public class OAuth2TokenIntrospectionTests { OAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(mvcResult); - ArgumentCaptor accessTokenClaimsContextCaptor = ArgumentCaptor.forClass(OAuth2TokenClaimsContext.class); + ArgumentCaptor accessTokenClaimsContextCaptor = ArgumentCaptor + .forClass(OAuth2TokenClaimsContext.class); verify(accessTokenCustomizer).customize(accessTokenClaimsContextCaptor.capture()); OAuth2TokenClaimsContext accessTokenClaimsContext = accessTokenClaimsContextCaptor.getValue(); @@ -325,8 +339,8 @@ public class OAuth2TokenIntrospectionTests { assertThat(tokenIntrospectionResponse.isActive()).isTrue(); assertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(authorizedRegisteredClient.getClientId()); assertThat(tokenIntrospectionResponse.getUsername()).isNull(); - assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween( - accessTokenClaims.getIssuedAt().minusSeconds(1), accessTokenClaims.getIssuedAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(accessTokenClaims.getIssuedAt().minusSeconds(1), + accessTokenClaims.getIssuedAt().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween( accessTokenClaims.getExpiresAt().minusSeconds(1), accessTokenClaims.getExpiresAt().plusSeconds(1)); List scopes = new ArrayList<>(accessTokenClaims.getClaim(OAuth2ParameterNames.SCOPE)); @@ -335,7 +349,8 @@ public class OAuth2TokenIntrospectionTests { assertThat(tokenIntrospectionResponse.getNotBefore()).isBetween( accessTokenClaims.getNotBefore().minusSeconds(1), accessTokenClaims.getNotBefore().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(accessTokenClaims.getSubject()); - assertThat(tokenIntrospectionResponse.getAudience()).containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience()); + assertThat(tokenIntrospectionResponse.getAudience()) + .containsExactlyInAnyOrderElementsOf(accessTokenClaims.getAudience()); assertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(accessTokenClaims.getIssuer()); assertThat(tokenIntrospectionResponse.getId()).isEqualTo(accessTokenClaims.getId()); } @@ -355,11 +370,10 @@ public class OAuth2TokenIntrospectionTests { OAuth2AccessToken accessToken = authorization.getAccessToken().getToken(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - introspectRegisteredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret()); - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = - new OAuth2TokenIntrospectionAuthenticationToken( - accessToken.getTokenValue(), clientPrincipal, null, null); + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(introspectRegisteredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, introspectRegisteredClient.getClientSecret()); + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken( + accessToken.getTokenValue(), clientPrincipal, null, null); when(authenticationConverter.convert(any())).thenReturn(tokenIntrospectionAuthentication); when(authenticationProvider.supports(eq(OAuth2TokenIntrospectionAuthenticationToken.class))).thenReturn(true); @@ -375,24 +389,25 @@ public class OAuth2TokenIntrospectionTests { verify(authenticationConverter).convert(any()); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).allMatch((converter) -> - converter == authenticationConverter || - converter instanceof OAuth2TokenIntrospectionAuthenticationConverter); + assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter + || converter instanceof OAuth2TokenIntrospectionAuthenticationConverter); verify(authenticationProvider).authenticate(eq(tokenIntrospectionAuthentication)); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).allMatch((provider) -> - provider == authenticationProvider || - provider instanceof OAuth2TokenIntrospectionAuthenticationProvider); + assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider + || provider instanceof OAuth2TokenIntrospectionAuthenticationProvider); - verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(tokenIntrospectionAuthentication)); + verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), + eq(tokenIntrospectionAuthentication)); } private static MultiValueMap getTokenIntrospectionRequestParameters(OAuth2Token token, @@ -405,24 +420,25 @@ public class OAuth2TokenIntrospectionTests { private static OAuth2TokenIntrospection readTokenIntrospectionResponse(MvcResult mvcResult) throws Exception { MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); return tokenIntrospectionHttpResponseConverter.read(OAuth2TokenIntrospection.class, httpResponse); } - private static MultiValueMap getAuthorizationCodeTokenRequestParameters(RegisteredClient registeredClient, - OAuth2Authorization authorization) { + private static MultiValueMap getAuthorizationCodeTokenRequestParameters( + RegisteredClient registeredClient, OAuth2Authorization authorization) { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - parameters.set(OAuth2ParameterNames.CODE, authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); + parameters.set(OAuth2ParameterNames.CODE, + authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); return parameters; } private static OAuth2AccessTokenResponse readAccessTokenResponse(MvcResult mvcResult) throws Exception { MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); return accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); } @@ -441,21 +457,25 @@ public class OAuth2TokenIntrospectionTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; } @Bean - OAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { + OAuth2AuthorizationConsentService authorizationConsentService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationConsentService(jdbcOperations, registeredClientRepository); } @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; @@ -503,7 +523,8 @@ public class OAuth2TokenIntrospectionTests { @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationCustomTokenIntrospectionEndpoint + extends AuthorizationServerConfiguration { // @formatter:off @Bean diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java index 18777bf4..67cec39d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenRevocationTests.java @@ -97,14 +97,23 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OAuth2TokenRevocationTests { + private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke"; + private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static AuthenticationConverter authenticationConverter; + private static Consumer> authenticationConvertersConsumer; + private static AuthenticationProvider authenticationProvider; + private static Consumer> authenticationProvidersConsumer; + private static AuthenticationSuccessHandler authenticationSuccessHandler; + private static AuthenticationFailureHandler authenticationFailureHandler; public final SpringTestContext spring = new SpringTestContext(); @@ -131,13 +140,13 @@ public class OAuth2TokenRevocationTests { authenticationProvidersConsumer = mock(Consumer.class); authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); authenticationFailureHandler = mock(AuthenticationFailureHandler.class); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); } @AfterEach @@ -163,11 +172,12 @@ public class OAuth2TokenRevocationTests { OAuth2TokenType tokenType = OAuth2TokenType.REFRESH_TOKEN; this.authorizationService.save(authorization); - this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) .params(getTokenRevocationRequestParameters(token, tokenType)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId()); OAuth2Authorization.Token refreshToken = updatedAuthorization.getRefreshToken(); @@ -188,11 +198,12 @@ public class OAuth2TokenRevocationTests { OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN; this.authorizationService.save(authorization); - this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) .params(getTokenRevocationRequestParameters(token, tokenType)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); OAuth2Authorization updatedAuthorization = this.authorizationService.findById(authorization.getId()); OAuth2Authorization.Token accessToken = updatedAuthorization.getAccessToken(); @@ -213,45 +224,47 @@ public class OAuth2TokenRevocationTests { OAuth2TokenType tokenType = OAuth2TokenType.ACCESS_TOKEN; this.authorizationService.save(authorization); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = - new OAuth2TokenRevocationAuthenticationToken(token, clientPrincipal); + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken( + token, clientPrincipal); when(authenticationConverter.convert(any())).thenReturn(tokenRevocationAuthentication); when(authenticationProvider.supports(eq(OAuth2TokenRevocationAuthenticationToken.class))).thenReturn(true); when(authenticationProvider.authenticate(any())).thenReturn(tokenRevocationAuthentication); - this.mvc.perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) + this.mvc + .perform(post(DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI) .params(getTokenRevocationRequestParameters(token, tokenType)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); verify(authenticationConverter).convert(any()); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); - assertThat(authenticationConverters).allMatch((converter) -> - converter == authenticationConverter || - converter instanceof OAuth2TokenRevocationAuthenticationConverter); + assertThat(authenticationConverters).allMatch((converter) -> converter == authenticationConverter + || converter instanceof OAuth2TokenRevocationAuthenticationConverter); verify(authenticationProvider).authenticate(eq(tokenRevocationAuthentication)); @SuppressWarnings("unchecked") - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).allMatch((provider) -> - provider == authenticationProvider || - provider instanceof OAuth2TokenRevocationAuthenticationProvider); + assertThat(authenticationProviders).allMatch((provider) -> provider == authenticationProvider + || provider instanceof OAuth2TokenRevocationAuthenticationProvider); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), eq(tokenRevocationAuthentication)); } - private static MultiValueMap getTokenRevocationRequestParameters(OAuth2Token token, OAuth2TokenType tokenType) { + private static MultiValueMap getTokenRevocationRequestParameters(OAuth2Token token, + OAuth2TokenType tokenType) { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.set(OAuth2ParameterNames.TOKEN, token.getTokenValue()); parameters.set(OAuth2ParameterNames.TOKEN_TYPE_HINT, tokenType.getValue()); @@ -271,8 +284,10 @@ public class OAuth2TokenRevocationTests { static class AuthorizationServerConfiguration { @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; @@ -280,7 +295,8 @@ public class OAuth2TokenRevocationTests { @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; @@ -323,7 +339,8 @@ public class OAuth2TokenRevocationTests { @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationCustomTokenRevocationEndpoint extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationCustomTokenRevocationEndpoint + extends AuthorizationServerConfiguration { // @formatter:off @Bean diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java index b4b33d8e..fbee0131 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java @@ -137,15 +137,21 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OidcClientRegistrationTests { + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = "/connect/register"; - private static final HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); - private static final HttpMessageConverter clientRegistrationHttpMessageConverter = - new OidcClientRegistrationHttpMessageConverter(); + + private static final HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + + private static final HttpMessageConverter clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter(); + private static EmbeddedDatabase db; + private static JWKSource jwkSource; + private static JWKSet clientJwkSet; + private static JwtEncoder jwtClientAssertionEncoder; public final SpringTestContext spring = new SpringTestContext(); @@ -175,22 +181,23 @@ public class OidcClientRegistrationTests { private static AuthenticationFailureHandler authenticationFailureHandler; private MockWebServer server; - private String clientJwkSetUrl; + private String clientJwkSetUrl; @BeforeAll public static void init() { JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK); jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); clientJwkSet = new JWKSet(TestJwks.generateRsaJwk().build()); - jwtClientAssertionEncoder = new NimbusJwtEncoder((jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet)); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + jwtClientAssertionEncoder = new NimbusJwtEncoder( + (jwkSelector, securityContext) -> jwkSelector.select(clientJwkSet)); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); authenticationConverter = mock(AuthenticationConverter.class); authenticationConvertersConsumer = mock(Consumer.class); authenticationProvider = mock(AuthenticationProvider.class); @@ -254,17 +261,17 @@ public class OidcClientRegistrationTests { assertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isNull(); assertThat(clientRegistrationResponse.getClientName()).isEqualTo(clientRegistration.getClientName()); assertThat(clientRegistrationResponse.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(clientRegistration.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(clientRegistration.getRedirectUris()); assertThat(clientRegistrationResponse.getGrantTypes()) - .containsExactlyInAnyOrderElementsOf(clientRegistration.getGrantTypes()); + .containsExactlyInAnyOrderElementsOf(clientRegistration.getGrantTypes()); assertThat(clientRegistrationResponse.getResponseTypes()) - .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); + .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); assertThat(clientRegistrationResponse.getScopes()) - .containsExactlyInAnyOrderElementsOf(clientRegistration.getScopes()); + .containsExactlyInAnyOrderElementsOf(clientRegistration.getScopes()); assertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod()) - .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(SignatureAlgorithm.RS256.getName()); + .isEqualTo(SignatureAlgorithm.RS256.getName()); assertThat(clientRegistrationResponse.getRegistrationClientUrl()).isNotNull(); assertThat(clientRegistrationResponse.getRegistrationAccessToken()).isNotEmpty(); } @@ -289,34 +296,36 @@ public class OidcClientRegistrationTests { HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setBearerAuth(clientRegistrationResponse.getRegistrationAccessToken()); - MvcResult mvcResult = this.mvc.perform(get(clientRegistrationResponse.getRegistrationClientUrl().toURI()) - .headers(httpHeaders)) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andReturn(); + MvcResult mvcResult = this.mvc + .perform(get(clientRegistrationResponse.getRegistrationClientUrl().toURI()).headers(httpHeaders)) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andReturn(); OidcClientRegistration clientConfigurationResponse = readClientRegistrationResponse(mvcResult.getResponse()); assertThat(clientConfigurationResponse.getClientId()).isEqualTo(clientRegistrationResponse.getClientId()); - assertThat(clientConfigurationResponse.getClientIdIssuedAt()).isEqualTo(clientRegistrationResponse.getClientIdIssuedAt()); + assertThat(clientConfigurationResponse.getClientIdIssuedAt()) + .isEqualTo(clientRegistrationResponse.getClientIdIssuedAt()); assertThat(clientConfigurationResponse.getClientSecret()).isNotNull(); - assertThat(clientConfigurationResponse.getClientSecretExpiresAt()).isEqualTo(clientRegistrationResponse.getClientSecretExpiresAt()); + assertThat(clientConfigurationResponse.getClientSecretExpiresAt()) + .isEqualTo(clientRegistrationResponse.getClientSecretExpiresAt()); assertThat(clientConfigurationResponse.getClientName()).isEqualTo(clientRegistrationResponse.getClientName()); assertThat(clientConfigurationResponse.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getRedirectUris()); assertThat(clientConfigurationResponse.getGrantTypes()) - .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getGrantTypes()); + .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getGrantTypes()); assertThat(clientConfigurationResponse.getResponseTypes()) - .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getResponseTypes()); + .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getResponseTypes()); assertThat(clientConfigurationResponse.getScopes()) - .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getScopes()); + .containsExactlyInAnyOrderElementsOf(clientRegistrationResponse.getScopes()); assertThat(clientConfigurationResponse.getTokenEndpointAuthenticationMethod()) - .isEqualTo(clientRegistrationResponse.getTokenEndpointAuthenticationMethod()); + .isEqualTo(clientRegistrationResponse.getTokenEndpointAuthenticationMethod()); assertThat(clientConfigurationResponse.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); + .isEqualTo(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); assertThat(clientConfigurationResponse.getRegistrationClientUrl()) - .isEqualTo(clientRegistrationResponse.getRegistrationClientUrl()); + .isEqualTo(clientRegistrationResponse.getRegistrationClientUrl()); assertThat(clientConfigurationResponse.getRegistrationAccessToken()).isNull(); } @@ -346,36 +355,38 @@ public class OidcClientRegistrationTests { registerClient(clientRegistration); verify(authenticationConverter).convert(any()); - ArgumentCaptor> authenticationConvertersCaptor = - ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); assertThat(authenticationConverters).hasSize(2) - .allMatch(converter -> converter == authenticationConverter - || converter instanceof OidcClientRegistrationAuthenticationConverter); + .allMatch(converter -> converter == authenticationConverter + || converter instanceof OidcClientRegistrationAuthenticationConverter); verify(authenticationProvider).authenticate(any()); - ArgumentCaptor> authenticationProvidersCaptor = - ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); assertThat(authenticationProviders).hasSize(3) - .allMatch(provider -> provider == authenticationProvider - || provider instanceof OidcClientRegistrationAuthenticationProvider - || provider instanceof OidcClientConfigurationAuthenticationProvider); + .allMatch(provider -> provider == authenticationProvider + || provider instanceof OidcClientRegistrationAuthenticationProvider + || provider instanceof OidcClientConfigurationAuthenticationProvider); verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any()); verifyNoInteractions(authenticationFailureHandler); } @Test - public void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() throws Exception { + public void requestWhenClientRegistrationEndpointCustomizedWithAuthenticationFailureHandlerThenUsed() + throws Exception { this.spring.register(CustomClientRegistrationConfiguration.class).autowire(); when(authenticationProvider.authenticate(any())).thenThrow(new OAuth2AuthenticationException("error")); - this.mvc.perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI) - .param(OAuth2ParameterNames.CLIENT_ID, "invalid").with(jwt())); + this.mvc + .perform(get(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI).param(OAuth2ParameterNames.CLIENT_ID, "invalid") + .with(jwt())); verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any()); verifyNoInteractions(authenticationSuccessHandler); @@ -399,14 +410,16 @@ public class OidcClientRegistrationTests { OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .param(OAuth2ParameterNames.SCOPE, "scope1") - .with(httpBasic(clientRegistrationResponse.getClientId(), clientRegistrationResponse.getClientSecret()))) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value("scope1")) - .andReturn(); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) + .param(OAuth2ParameterNames.SCOPE, "scope1") + .with(httpBasic(clientRegistrationResponse.getClientId(), + clientRegistrationResponse.getClientSecret()))) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value("scope1")) + .andReturn(); } // gh-1344 @@ -428,34 +441,38 @@ public class OidcClientRegistrationTests { OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration); - JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256) - .build(); + JwsHeader jwsHeader = JwsHeader.with(MacAlgorithm.HS256).build(); Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); JwtClaimsSet jwtClaimsSet = JwtClaimsSet.builder() - .issuer(clientRegistrationResponse.getClientId()) - .subject(clientRegistrationResponse.getClientId()) - .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), this.authorizationServerSettings.getTokenEndpoint()))) - .issuedAt(issuedAt) - .expiresAt(expiresAt) - .build(); + .issuer(clientRegistrationResponse.getClientId()) + .subject(clientRegistrationResponse.getClientId()) + .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), + this.authorizationServerSettings.getTokenEndpoint()))) + .issuedAt(issuedAt) + .expiresAt(expiresAt) + .build(); - JWKSet jwkSet = new JWKSet(TestJwks.jwk( - new SecretKeySpec(clientRegistrationResponse.getClientSecret().getBytes(), "HS256")).build()); - JwtEncoder jwtClientAssertionEncoder = new NimbusJwtEncoder((jwkSelector, securityContext) -> jwkSelector.select(jwkSet)); + JWKSet jwkSet = new JWKSet( + TestJwks.jwk(new SecretKeySpec(clientRegistrationResponse.getClientSecret().getBytes(), "HS256")) + .build()); + JwtEncoder jwtClientAssertionEncoder = new NimbusJwtEncoder( + (jwkSelector, securityContext) -> jwkSelector.select(jwkSet)); Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) - .param(OAuth2ParameterNames.SCOPE, "scope1") - .param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") - .param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue()) - .param(OAuth2ParameterNames.CLIENT_ID, clientRegistrationResponse.getClientId())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value("scope1")); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) + .param(OAuth2ParameterNames.SCOPE, "scope1") + .param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") + .param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue()) + .param(OAuth2ParameterNames.CLIENT_ID, clientRegistrationResponse.getClientId())) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value("scope1")); } @Test @@ -478,17 +495,17 @@ public class OidcClientRegistrationTests { OidcClientRegistration clientRegistrationResponse = registerClient(clientRegistration); - RegisteredClient registeredClient = this.registeredClientRepository.findByClientId( - clientRegistrationResponse.getClientId()); + RegisteredClient registeredClient = this.registeredClientRepository + .findByClientId(clientRegistrationResponse.getClientId()); assertThat(clientRegistrationResponse.getClaim("custom-metadata-name-1")).isEqualTo("value-1"); assertThat(clientRegistrationResponse.getClaim("custom-metadata-name-2")).isEqualTo("value-2"); assertThat(clientRegistrationResponse.getClaim("non-registered-custom-metadata")).isNull(); assertThat(registeredClient.getClientSettings().getSetting("custom-metadata-name-1")) - .isEqualTo("value-1"); + .isEqualTo("value-1"); assertThat(registeredClient.getClientSettings().getSetting("custom-metadata-name-2")) - .isEqualTo("value-2"); + .isEqualTo("value-2"); assertThat(registeredClient.getClientSettings().getSetting("non-registered-custom-metadata")).isNull(); } @@ -520,16 +537,18 @@ public class OidcClientRegistrationTests { // @formatter:on Jwt jwtAssertion = jwtClientAssertionEncoder.encode(JwtEncoderParameters.from(jwsHeader, jwtClaimsSet)); - MvcResult mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + MvcResult mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.CLIENT_CREDENTIALS.getValue()) .param(OAuth2ParameterNames.SCOPE, clientRegistrationScope) - .param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") + .param(OAuth2ParameterNames.CLIENT_ASSERTION_TYPE, + "urn:ietf:params:oauth:client-assertion-type:jwt-bearer") .param(OAuth2ParameterNames.CLIENT_ASSERTION, jwtAssertion.getTokenValue()) .param(OAuth2ParameterNames.CLIENT_ID, clientRegistrar.getClientId())) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").value(clientRegistrationScope)) - .andReturn(); + .andExpect(status().isOk()) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").value(clientRegistrationScope)) + .andReturn(); OAuth2AccessToken accessToken = readAccessTokenResponse(mvcResult.getResponse()).getAccessToken(); @@ -539,14 +558,14 @@ public class OidcClientRegistrationTests { httpHeaders.setBearerAuth(accessToken.getTokenValue()); // Register the client - mvcResult = this.mvc.perform(post(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI) - .headers(httpHeaders) + mvcResult = this.mvc + .perform(post(DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI).headers(httpHeaders) .contentType(MediaType.APPLICATION_JSON) .content(getClientRegistrationRequestContent(clientRegistration))) - .andExpect(status().isCreated()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andReturn(); + .andExpect(status().isCreated()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andReturn(); return readClientRegistrationResponse(mvcResult.getResponse()); } @@ -555,32 +574,36 @@ public class OidcClientRegistrationTests { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); return JwtClaimsSet.builder() - .issuer(registeredClient.getClientId()) - .subject(registeredClient.getClientId()) - .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), this.authorizationServerSettings.getTokenEndpoint()))) - .issuedAt(issuedAt) - .expiresAt(expiresAt); + .issuer(registeredClient.getClientId()) + .subject(registeredClient.getClientId()) + .audience(Collections.singletonList(asUrl(this.authorizationServerSettings.getIssuer(), + this.authorizationServerSettings.getTokenEndpoint()))) + .issuedAt(issuedAt) + .expiresAt(expiresAt); } private static String asUrl(String uri, String path) { return UriComponentsBuilder.fromUriString(uri).path(path).build().toUriString(); } - private static OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + private static OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) + throws Exception { + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); } - private static byte[] getClientRegistrationRequestContent(OidcClientRegistration clientRegistration) throws Exception { + private static byte[] getClientRegistrationRequestContent(OidcClientRegistration clientRegistration) + throws Exception { MockHttpOutputMessage httpRequest = new MockHttpOutputMessage(); clientRegistrationHttpMessageConverter.write(clientRegistration, null, httpRequest); return httpRequest.getBodyAsBytes(); } - private static OidcClientRegistration readClientRegistrationResponse(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + private static OidcClientRegistration readClientRegistrationResponse(MockHttpServletResponse response) + throws Exception { + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse); } @@ -622,6 +645,7 @@ public class OidcClientRegistrationTests { return http.build(); } // @formatter:on + } @EnableWebSecurity @@ -705,14 +729,16 @@ public class OidcClientRegistrationTests { RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); - JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository registeredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); registeredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); registeredClientRepository.save(registeredClient); return registeredClientRepository; } @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { return new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); } @@ -733,9 +759,7 @@ public class OidcClientRegistrationTests { @Bean AuthorizationServerSettings authorizationServerSettings() { - return AuthorizationServerSettings.builder() - .issuer("https://auth-server:9000") - .build(); + return AuthorizationServerSettings.builder().issuer("https://auth-server:9000").build(); } @Bean diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java index 1a4a62b7..83607d0b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcProviderConfigurationTests.java @@ -62,7 +62,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OidcProviderConfigurationTests { + private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration"; + private static final String ISSUER_URL = "https://example.com"; public final SpringTestContext spring = new SpringTestContext(); @@ -78,8 +80,8 @@ public class OidcProviderConfigurationTests { this.spring.register(AuthorizationServerConfiguration.class).autowire(); this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpectAll(defaultConfigurationMatchers()); + .andExpect(status().is2xxSuccessful()) + .andExpectAll(defaultConfigurationMatchers()); } // gh-632 @@ -87,31 +89,33 @@ public class OidcProviderConfigurationTests { public void requestWhenConfigurationRequestAndUserAuthenticatedThenReturnConfigurationResponse() throws Exception { this.spring.register(AuthorizationServerConfiguration.class).autowire(); - this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)) - .with(user("user"))) - .andExpect(status().is2xxSuccessful()) - .andExpectAll(defaultConfigurationMatchers()); + this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI)).with(user("user"))) + .andExpect(status().is2xxSuccessful()) + .andExpectAll(defaultConfigurationMatchers()); } // gh-616 @Test - public void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse() throws Exception { + public void requestWhenConfigurationRequestAndConfigurationCustomizerSetThenReturnCustomConfigurationResponse() + throws Exception { this.spring.register(AuthorizationServerConfigurationWithProviderConfigurationCustomizer.class).autowire(); this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, - hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL))); + .andExpect(status().is2xxSuccessful()) + .andExpect(jsonPath(OAuth2AuthorizationServerMetadataClaimNames.SCOPES_SUPPORTED, + hasItems(OidcScopes.OPENID, OidcScopes.PROFILE, OidcScopes.EMAIL))); } @Test - public void requestWhenConfigurationRequestAndClientRegistrationEnabledThenConfigurationResponseIncludesRegistrationEndpoint() throws Exception { + public void requestWhenConfigurationRequestAndClientRegistrationEnabledThenConfigurationResponseIncludesRegistrationEndpoint() + throws Exception { this.spring.register(AuthorizationServerConfigurationWithClientRegistrationEnabled.class).autowire(); this.mvc.perform(get(ISSUER_URL.concat(DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI))) - .andExpect(status().is2xxSuccessful()) - .andExpectAll(defaultConfigurationMatchers()) - .andExpect(jsonPath("$.registration_endpoint").value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint()))); + .andExpect(status().is2xxSuccessful()) + .andExpectAll(defaultConfigurationMatchers()) + .andExpect(jsonPath("$.registration_endpoint") + .value(ISSUER_URL.concat(this.authorizationServerSettings.getOidcClientRegistrationEndpoint()))); } private ResultMatcher[] defaultConfigurationMatchers() { @@ -152,50 +156,43 @@ public class OidcProviderConfigurationTests { @Test public void loadContextWhenIssuerNotValidUrlThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUrl.class).autowire()); } @Test public void loadContextWhenIssuerNotValidUriThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithInvalidIssuerUri.class).autowire()); } @Test public void loadContextWhenIssuerWithQueryThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithIssuerQuery.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithIssuerQuery.class).autowire()); } @Test public void loadContextWhenIssuerWithFragmentThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithIssuerFragment.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithIssuerFragment.class).autowire()); } @Test public void loadContextWhenIssuerWithQueryAndFragmentThenThrowException() { - assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithIssuerQueryAndFragment.class).autowire() - ); + assertThatThrownBy(() -> this.spring.register(AuthorizationServerConfigurationWithIssuerQueryAndFragment.class) + .autowire()); } @Test public void loadContextWhenIssuerWithEmptyQueryThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyQuery.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyQuery.class).autowire()); } @Test public void loadContextWhenIssuerWithEmptyFragmentThenThrowException() { assertThatThrownBy( - () -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyFragment.class).autowire() - ); + () -> this.spring.register(AuthorizationServerConfigurationWithIssuerEmptyFragment.class).autowire()); } @EnableWebSecurity @@ -204,8 +201,8 @@ public class OidcProviderConfigurationTests { @Bean SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); - http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) - .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 + // Enable OpenID Connect 1.0 + http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults()); return http.build(); } @@ -217,16 +214,15 @@ public class OidcProviderConfigurationTests { @Bean AuthorizationServerSettings authorizationServerSettings() { - return AuthorizationServerSettings.builder() - .issuer(ISSUER_URL) - .build(); + return AuthorizationServerSettings.builder().issuer(ISSUER_URL).build(); } } @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationWithProviderConfigurationCustomizer extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithProviderConfigurationCustomizer + extends AuthorizationServerConfiguration { // @formatter:off @Bean @@ -255,15 +251,15 @@ public class OidcProviderConfigurationTests { // @formatter:on private Consumer providerConfigurationCustomizer() { - return (providerConfiguration) -> - providerConfiguration.scope(OidcScopes.PROFILE).scope(OidcScopes.EMAIL); + return (providerConfiguration) -> providerConfiguration.scope(OidcScopes.PROFILE).scope(OidcScopes.EMAIL); } } @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationWithClientRegistrationEnabled extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithClientRegistrationEnabled + extends AuthorizationServerConfiguration { // @formatter:off @Bean @@ -290,6 +286,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer("urn:example").build(); } + } @EnableWebSecurity @@ -299,6 +296,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer("https://not a valid uri").build(); } + } @EnableWebSecurity @@ -308,6 +306,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value").build(); } + } @EnableWebSecurity @@ -317,6 +316,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#fragment").build(); } + } @EnableWebSecurity @@ -326,6 +326,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?param=value#fragment").build(); } + } @EnableWebSecurity @@ -335,6 +336,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "?").build(); } + } @EnableWebSecurity @@ -344,6 +346,7 @@ public class OidcProviderConfigurationTests { AuthorizationServerSettings authorizationServerSettings() { return AuthorizationServerSettings.builder().issuer(ISSUER_URL + "#").build(); } + } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java index ebc4680c..1a0d7db0 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcTests.java @@ -126,15 +126,23 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OidcTests { + private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize"; + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = "/connect/logout"; + private static final String AUTHORITIES_CLAIM = "authorities"; + private static final OAuth2TokenType AUTHORIZATION_CODE_TOKEN_TYPE = new OAuth2TokenType(OAuth2ParameterNames.CODE); + private static EmbeddedDatabase db; + private static JWKSource jwkSource; - private static HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + + private static HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); + private static SessionRegistry sessionRegistry; public final SpringTestContext spring = new SpringTestContext(); @@ -161,13 +169,13 @@ public class OidcTests { public static void init() { JWKSet jwkSet = new JWKSet(TestJwks.DEFAULT_RSA_JWK); jwkSource = (jwkSelector, securityContext) -> jwkSelector.select(jwkSet); - db = new EmbeddedDatabaseBuilder() - .generateUniqueName(true) - .setType(EmbeddedDatabaseType.HSQL) - .setScriptEncoding("UTF-8") - .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") - .addScript("org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") - .build(); + db = new EmbeddedDatabaseBuilder().generateUniqueName(true) + .setType(EmbeddedDatabaseType.HSQL) + .setScriptEncoding("UTF-8") + .addScript("org/springframework/security/oauth2/server/authorization/oauth2-authorization-schema.sql") + .addScript( + "org/springframework/security/oauth2/server/authorization/client/oauth2-registered-client-schema.sql") + .build(); sessionRegistry = spy(new SessionRegistryImpl()); } @@ -191,40 +199,44 @@ public class OidcTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) .with(user("user").roles("A", "B"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=state"); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient, authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) - .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) - .andExpect(jsonPath("$.access_token").isNotEmpty()) - .andExpect(jsonPath("$.token_type").isNotEmpty()) - .andExpect(jsonPath("$.expires_in").isNotEmpty()) - .andExpect(jsonPath("$.refresh_token").isNotEmpty()) - .andExpect(jsonPath("$.scope").isNotEmpty()) - .andExpect(jsonPath("$.id_token").isNotEmpty()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andExpect(header().string(HttpHeaders.CACHE_CONTROL, containsString("no-store"))) + .andExpect(header().string(HttpHeaders.PRAGMA, containsString("no-cache"))) + .andExpect(jsonPath("$.access_token").isNotEmpty()) + .andExpect(jsonPath("$.token_type").isNotEmpty()) + .andExpect(jsonPath("$.expires_in").isNotEmpty()) + .andExpect(jsonPath("$.refresh_token").isNotEmpty()) + .andExpect(jsonPath("$.scope").isNotEmpty()) + .andExpect(jsonPath("$.id_token").isNotEmpty()) + .andReturn(); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); - Jwt idToken = this.jwtDecoder.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); + Jwt idToken = this.jwtDecoder + .decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); // Assert user authorities was propagated as claim in ID Token List authoritiesClaim = idToken.getClaim(AUTHORITIES_CLAIM); @@ -247,51 +259,57 @@ public class OidcTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); this.registeredClientRepository.save(registeredClient); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) - .with(user("user").roles("A", "B"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) + .with(user("user").roles("A", "B"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String expectedRedirectUri = authorizationRequestParameters.getFirst(OAuth2ParameterNames.REDIRECT_URI); assertThat(redirectedUrl).matches(expectedRedirectUri + "\\?code=.{15,}&state=state"); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient, authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andReturn(); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); - Jwt idToken = this.jwtDecoder.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); + Jwt idToken = this.jwtDecoder + .decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); String sidClaim = idToken.getClaim("sid"); assertThat(sidClaim).isNotNull(); // Refresh access token - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue()) - .param(OAuth2ParameterNames.REFRESH_TOKEN, accessTokenResponse.getRefreshToken().getTokenValue()) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .param(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.REFRESH_TOKEN.getValue()) + .param(OAuth2ParameterNames.REFRESH_TOKEN, accessTokenResponse.getRefreshToken().getTokenValue()) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andReturn(); servletResponse = mvcResult.getResponse(); - httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); + httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); - idToken = this.jwtDecoder.decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); + idToken = this.jwtDecoder + .decode((String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN)); assertThat(idToken.getClaim("sid")).isEqualTo(sidClaim); } @@ -304,41 +322,43 @@ public class OidcTests { this.registeredClientRepository.save(registeredClient); // Login - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) - .with(user("user"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) + .with(user("user"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); MockHttpSession session = (MockHttpSession) mvcResult.getRequest().getSession(); assertThat(session.isNew()).isTrue(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); // Get ID Token - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient, authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()) + .andReturn(); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); String idToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN); // Logout - mvcResult = this.mvc.perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI) - .param("id_token_hint", idToken) - .session(session)) - .andExpect(status().is3xxRedirection()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI).param("id_token_hint", idToken).session(session)) + .andExpect(status().is3xxRedirection()) + .andReturn(); redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); assertThat(redirectedUrl).matches("/"); @@ -353,31 +373,36 @@ public class OidcTests { RegisteredClient registeredClient1 = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); this.registeredClientRepository.save(registeredClient1); - MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient1); - MvcResult mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) - .with(user("user1"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + MultiValueMap authorizationRequestParameters = getAuthorizationRequestParameters( + registeredClient1); + MvcResult mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) + .with(user("user1"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); MockHttpSession user1Session = (MockHttpSession) mvcResult.getRequest().getSession(); assertThat(user1Session.isNew()).isTrue(); String redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); String authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization user1Authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization user1Authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient1, user1Authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient1.getClientId(), registeredClient1.getClientSecret()))) - .andExpect(status().isOk()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .params(getTokenRequestParameters(registeredClient1, user1Authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient1.getClientId(), + registeredClient1.getClientSecret()))) + .andExpect(status().isOk()) + .andReturn(); MockHttpServletResponse servletResponse = mvcResult.getResponse(); - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); - OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); + OAuth2AccessTokenResponse accessTokenResponse = accessTokenHttpResponseConverter + .read(OAuth2AccessTokenResponse.class, httpResponse); String user1IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN); @@ -386,40 +411,42 @@ public class OidcTests { this.registeredClientRepository.save(registeredClient2); authorizationRequestParameters = getAuthorizationRequestParameters(registeredClient2); - mvcResult = this.mvc.perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI) - .queryParams(authorizationRequestParameters) - .with(user("user2"))) - .andExpect(status().is3xxRedirection()) - .andReturn(); + mvcResult = this.mvc + .perform(get(DEFAULT_AUTHORIZATION_ENDPOINT_URI).queryParams(authorizationRequestParameters) + .with(user("user2"))) + .andExpect(status().is3xxRedirection()) + .andReturn(); MockHttpSession user2Session = (MockHttpSession) mvcResult.getRequest().getSession(); assertThat(user2Session.isNew()).isTrue(); redirectedUrl = mvcResult.getResponse().getRedirectedUrl(); authorizationCode = extractParameterFromRedirectUri(redirectedUrl, "code"); - OAuth2Authorization user2Authorization = this.authorizationService.findByToken(authorizationCode, AUTHORIZATION_CODE_TOKEN_TYPE); + OAuth2Authorization user2Authorization = this.authorizationService.findByToken(authorizationCode, + AUTHORIZATION_CODE_TOKEN_TYPE); - mvcResult = this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient2, user2Authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient2.getClientId(), registeredClient2.getClientSecret()))) - .andExpect(status().isOk()) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI) + .params(getTokenRequestParameters(registeredClient2, user2Authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient2.getClientId(), + registeredClient2.getClientSecret()))) + .andExpect(status().isOk()) + .andReturn(); servletResponse = mvcResult.getResponse(); - httpResponse = new MockClientHttpResponse( - servletResponse.getContentAsByteArray(), HttpStatus.valueOf(servletResponse.getStatus())); + httpResponse = new MockClientHttpResponse(servletResponse.getContentAsByteArray(), + HttpStatus.valueOf(servletResponse.getStatus())); accessTokenResponse = accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); String user2IdToken = (String) accessTokenResponse.getAdditionalParameters().get(OidcParameterNames.ID_TOKEN); // Attempt to log out user1 using user2's ID Token - mvcResult = this.mvc.perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI) - .param("id_token_hint", user2IdToken) - .session(user1Session)) - .andExpect(status().isBadRequest()) - .andExpect(status().reason("[invalid_token] OpenID Connect 1.0 Logout Request Parameter: sub")) - .andReturn(); + mvcResult = this.mvc + .perform(post(DEFAULT_OIDC_LOGOUT_ENDPOINT_URI).param("id_token_hint", user2IdToken).session(user1Session)) + .andExpect(status().isBadRequest()) + .andExpect(status().reason("[invalid_token] OpenID Connect 1.0 Logout Request Parameter: sub")) + .andReturn(); assertThat(user1Session.isInvalid()).isFalse(); } @@ -434,11 +461,11 @@ public class OidcTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); this.authorizationService.save(authorization); - this.mvc.perform(post(DEFAULT_TOKEN_ENDPOINT_URI) - .params(getTokenRequestParameters(registeredClient, authorization)) - .header(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth( - registeredClient.getClientId(), registeredClient.getClientSecret()))) - .andExpect(status().isOk()); + this.mvc + .perform(post(DEFAULT_TOKEN_ENDPOINT_URI).params(getTokenRequestParameters(registeredClient, authorization)) + .header(HttpHeaders.AUTHORIZATION, + "Basic " + encodeBasicAuth(registeredClient.getClientId(), registeredClient.getClientSecret()))) + .andExpect(status().isOk()); verify(this.tokenGenerator, times(3)).generate(any()); } @@ -537,7 +564,8 @@ public class OidcTests { OAuth2Authorization authorization) { MultiValueMap parameters = new LinkedMultiValueMap<>(); parameters.set(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - parameters.set(OAuth2ParameterNames.CODE, authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); + parameters.set(OAuth2ParameterNames.CODE, + authorization.getToken(OAuth2AuthorizationCode.class).getToken().getTokenValue()); parameters.set(OAuth2ParameterNames.REDIRECT_URI, registeredClient.getRedirectUris().iterator().next()); return parameters; } @@ -550,7 +578,8 @@ public class OidcTests { return new String(encodedBytes, StandardCharsets.UTF_8); } - private String extractParameterFromRedirectUri(String redirectUri, String param) throws UnsupportedEncodingException { + private String extractParameterFromRedirectUri(String redirectUri, String param) + throws UnsupportedEncodingException { String locationHeader = URLDecoder.decode(redirectUri, StandardCharsets.UTF_8.name()); UriComponents uriComponents = UriComponentsBuilder.fromUriString(locationHeader).build(); return uriComponents.getQueryParams().getFirst(param); @@ -563,14 +592,16 @@ public class OidcTests { @Bean SecurityFilterChain authorizationServerSecurityFilterChain(HttpSecurity http) throws Exception { OAuth2AuthorizationServerConfiguration.applyDefaultSecurity(http); - http.getConfigurer(OAuth2AuthorizationServerConfigurer.class) - .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 + // Enable OpenID Connect 1.0 + http.getConfigurer(OAuth2AuthorizationServerConfigurer.class).oidc(Customizer.withDefaults()); return http.build(); } @Bean - OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, RegisteredClientRepository registeredClientRepository) { - JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, registeredClientRepository); + OAuth2AuthorizationService authorizationService(JdbcOperations jdbcOperations, + RegisteredClientRepository registeredClientRepository) { + JdbcOAuth2AuthorizationService authorizationService = new JdbcOAuth2AuthorizationService(jdbcOperations, + registeredClientRepository); authorizationService.setAuthorizationRowMapper(new RowMapper(registeredClientRepository)); authorizationService.setAuthorizationParametersMapper(new ParametersMapper()); return authorizationService; @@ -578,7 +609,8 @@ public class OidcTests { @Bean RegisteredClientRepository registeredClientRepository(JdbcOperations jdbcOperations) { - JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository(jdbcOperations); + JdbcRegisteredClientRepository jdbcRegisteredClientRepository = new JdbcRegisteredClientRepository( + jdbcOperations); RegisteredClientParametersMapper registeredClientParametersMapper = new RegisteredClientParametersMapper(); jdbcRegisteredClientRepository.setRegisteredClientParametersMapper(registeredClientParametersMapper); return jdbcRegisteredClientRepository; @@ -659,9 +691,10 @@ public class OidcTests { new OAuth2AuthorizationServerConfigurer(); http.apply(authorizationServerConfigurer); + // Enable OpenID Connect 1.0 authorizationServerConfigurer .tokenGenerator(tokenGenerator()) - .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 + .oidc(Customizer.withDefaults()); RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); @@ -681,8 +714,8 @@ public class OidcTests { JwtGenerator jwtGenerator = new JwtGenerator(new NimbusJwtEncoder(jwkSource())); jwtGenerator.setJwtCustomizer(jwtCustomizer()); OAuth2RefreshTokenGenerator refreshTokenGenerator = new OAuth2RefreshTokenGenerator(); - OAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(jwtGenerator, refreshTokenGenerator); + OAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator( + jwtGenerator, refreshTokenGenerator); return spy(new OAuth2TokenGenerator() { @Override public OAuth2Token generate(OAuth2TokenContext context) { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java index 66f92761..bdc58a11 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcUserInfoTests.java @@ -106,7 +106,9 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers. */ @ExtendWith(SpringTestContextExtension.class) public class OidcUserInfoTests { + private static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = "/userinfo"; + private static SecurityContextRepository securityContextRepository; public final SpringTestContext spring = new SpringTestContext(); @@ -214,15 +216,16 @@ public class OidcUserInfoTests { verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), any()); verifyNoInteractions(authenticationFailureHandler); - ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationProvidersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationProvidersConsumer).accept(authenticationProvidersCaptor.capture()); List authenticationProviders = authenticationProvidersCaptor.getValue(); - assertThat(authenticationProviders).hasSize(2).allMatch(provider -> - provider == authenticationProvider || - provider instanceof OidcUserInfoAuthenticationProvider - ); + assertThat(authenticationProviders).hasSize(2) + .allMatch(provider -> provider == authenticationProvider + || provider instanceof OidcUserInfoAuthenticationProvider); - ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor.forClass(List.class); + ArgumentCaptor> authenticationConvertersCaptor = ArgumentCaptor + .forClass(List.class); verify(authenticationConvertersConsumer).accept(authenticationConvertersCaptor.capture()); List authenticationConverters = authenticationConvertersCaptor.getValue(); assertThat(authenticationConverters).hasSize(2).allMatch(AuthenticationConverter.class::isInstance); @@ -260,14 +263,12 @@ public class OidcUserInfoTests { this.spring.register(CustomUserInfoConfiguration.class).autowire(); when(userInfoMapper.apply(any())).thenReturn(createUserInfo()); - doAnswer( - invocation -> { - HttpServletResponse response = invocation.getArgument(1); - response.setStatus(HttpStatus.UNAUTHORIZED.value()); - response.getWriter().write("unauthorized"); - return null; - } - ).when(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any()); + doAnswer(invocation -> { + HttpServletResponse response = invocation.getArgument(1); + response.setStatus(HttpStatus.UNAUTHORIZED.value()); + response.getWriter().write("unauthorized"); + return null; + }).when(authenticationFailureHandler).onAuthenticationFailure(any(), any(), any()); OAuth2AccessToken accessToken = createAuthorization().getAccessToken().getToken(); // @formatter:off @@ -298,8 +299,9 @@ public class OidcUserInfoTests { .andReturn(); // @formatter:on - org.springframework.security.core.context.SecurityContext securityContext = - securityContextRepository.loadDeferredContext(mvcResult.getRequest()).get(); + org.springframework.security.core.context.SecurityContext securityContext = securityContextRepository + .loadDeferredContext(mvcResult.getRequest()) + .get(); assertThat(securityContext.getAuthentication()).isNull(); } @@ -340,18 +342,15 @@ public class OidcUserInfoTests { Jwt jwt = this.jwtEncoder.encode(JwtEncoderParameters.from(headers, claimSet)); Instant now = Instant.now(); - Set scopes = new HashSet<>(Arrays.asList( - OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL, OidcScopes.PHONE, OidcScopes.PROFILE)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), now, now.plusSeconds(300), scopes); + Set scopes = new HashSet<>(Arrays.asList(OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL, + OidcScopes.PHONE, OidcScopes.PROFILE)); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, jwt.getTokenValue(), + now, now.plusSeconds(300), scopes); OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .claims(claims -> claims.putAll(createUserInfo().getClaims())) - .build(); + .claims(claims -> claims.putAll(createUserInfo().getClaims())) + .build(); - return TestOAuth2Authorizations.authorization() - .accessToken(accessToken) - .token(idToken) - .build(); + return TestOAuth2Authorizations.authorization().accessToken(accessToken).token(idToken).build(); } private static OidcUserInfo createUserInfo() { @@ -388,10 +387,8 @@ public class OidcUserInfoTests { @Bean @Override SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = - new OAuth2AuthorizationServerConfigurer(); - RequestMatcher endpointsMatcher = authorizationServerConfigurer - .getEndpointsMatcher(); + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); + RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); // @formatter:off http @@ -419,21 +416,21 @@ public class OidcUserInfoTests { return http.build(); } + } @EnableWebSecurity @Configuration(proxyBeanMethods = false) - static class AuthorizationServerConfigurationWithSecurityContextRepository extends AuthorizationServerConfiguration { + static class AuthorizationServerConfigurationWithSecurityContextRepository + extends AuthorizationServerConfiguration { @Bean @Override SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = - new OAuth2AuthorizationServerConfigurer(); - authorizationServerConfigurer - .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 - RequestMatcher endpointsMatcher = authorizationServerConfigurer - .getEndpointsMatcher(); + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); + // Enable OpenID Connect 1.0 + authorizationServerConfigurer.oidc(Customizer.withDefaults()); + RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); // @formatter:off http @@ -452,6 +449,7 @@ public class OidcUserInfoTests { return http.build(); } + } @EnableWebSecurity @@ -460,12 +458,10 @@ public class OidcUserInfoTests { @Bean SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception { - OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = - new OAuth2AuthorizationServerConfigurer(); - authorizationServerConfigurer - .oidc(Customizer.withDefaults()); // Enable OpenID Connect 1.0 - RequestMatcher endpointsMatcher = authorizationServerConfigurer - .getEndpointsMatcher(); + OAuth2AuthorizationServerConfigurer authorizationServerConfigurer = new OAuth2AuthorizationServerConfigurer(); + // Enable OpenID Connect 1.0 + authorizationServerConfigurer.oidc(Customizer.withDefaults()); + RequestMatcher endpointsMatcher = authorizationServerConfigurer.getEndpointsMatcher(); // @formatter:off http @@ -511,9 +507,7 @@ public class OidcUserInfoTests { @Bean AuthorizationServerSettings authorizationServerSettings() { - return AuthorizationServerSettings.builder() - .issuer("https://auth-server:9000") - .build(); + return AuthorizationServerSettings.builder().issuer("https://auth-server:9000").build(); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java index 19e8299c..fc406ac2 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/context/TestAuthorizationServerContext.java @@ -24,19 +24,20 @@ import org.springframework.security.oauth2.server.authorization.settings.Authori * @author Joe Grandja */ public class TestAuthorizationServerContext implements AuthorizationServerContext { + private final AuthorizationServerSettings authorizationServerSettings; + private final Supplier issuerSupplier; - public TestAuthorizationServerContext(AuthorizationServerSettings authorizationServerSettings, @Nullable Supplier issuerSupplier) { + public TestAuthorizationServerContext(AuthorizationServerSettings authorizationServerSettings, + @Nullable Supplier issuerSupplier) { this.authorizationServerSettings = authorizationServerSettings; this.issuerSupplier = issuerSupplier; } @Override public String getIssuer() { - return this.issuerSupplier != null ? - this.issuerSupplier.get() : - getAuthorizationServerSettings().getIssuer(); + return this.issuerSupplier != null ? this.issuerSupplier.get() : getAuthorizationServerSettings().getIssuer(); } @Override diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverterTests.java index 362dc683..7c291e2d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2AuthorizationServerMetadataHttpMessageConverterTests.java @@ -15,7 +15,6 @@ */ package org.springframework.security.oauth2.server.authorization.http.converter; - import java.net.URL; import java.util.Arrays; import java.util.Map; @@ -41,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Daniel Garnier-Moiroux */ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { + private final OAuth2AuthorizationServerMetadataHttpMessageConverter messageConverter = new OAuth2AuthorizationServerMetadataHttpMessageConverter(); @Test @@ -50,12 +50,14 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { @Test public void setAuthorizationServerMetadataParametersConverterWhenConverterIsNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataParametersConverter(null)); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataParametersConverter(null)); } @Test public void setAuthorizationServerMetadataConverterWhenConverterIsNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataConverter(null)); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.messageConverter.setAuthorizationServerMetadataConverter(null)); } @Test @@ -68,13 +70,16 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { + " \"response_types_supported\": [\"code\"]\n" + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), + HttpStatus.OK); OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.messageConverter - .readInternal(OAuth2AuthorizationServerMetadata.class, response); + .readInternal(OAuth2AuthorizationServerMetadata.class, response); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(new URL("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(new URL("https://example.com/oauth2/authorize")); - assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(new URL("https://example.com/oauth2/token")); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getTokenEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/token")); assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).isNull(); assertThat(authorizationServerMetadata.getJwkSetUrl()).isNull(); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("code"); @@ -108,26 +113,36 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { + " \"custom_collection_claim\": [\"value1\", \"value2\"]\n" + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), + HttpStatus.OK); OAuth2AuthorizationServerMetadata authorizationServerMetadata = this.messageConverter - .readInternal(OAuth2AuthorizationServerMetadata.class, response); + .readInternal(OAuth2AuthorizationServerMetadata.class, response); assertThat(authorizationServerMetadata.getClaims()).hasSize(15); assertThat(authorizationServerMetadata.getIssuer()).isEqualTo(new URL("https://example.com")); - assertThat(authorizationServerMetadata.getAuthorizationEndpoint()).isEqualTo(new URL("https://example.com/oauth2/authorize")); - assertThat(authorizationServerMetadata.getTokenEndpoint()).isEqualTo(new URL("https://example.com/oauth2/token")); - assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getAuthorizationEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/authorize")); + assertThat(authorizationServerMetadata.getTokenEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/token")); + assertThat(authorizationServerMetadata.getTokenEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(authorizationServerMetadata.getJwkSetUrl()).isEqualTo(new URL("https://example.com/oauth2/jwks")); assertThat(authorizationServerMetadata.getScopes()).containsExactly("openid"); assertThat(authorizationServerMetadata.getResponseTypes()).containsExactly("code"); - assertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); - assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()).isEqualTo(new URL("https://example.com/oauth2/revoke")); - assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()).isEqualTo(new URL("https://example.com/oauth2/introspect")); - assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); + assertThat(authorizationServerMetadata.getTokenRevocationEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/revoke")); + assertThat(authorizationServerMetadata.getTokenRevocationEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/introspect")); + assertThat(authorizationServerMetadata.getTokenIntrospectionEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(authorizationServerMetadata.getCodeChallengeMethods()).containsExactly("S256"); assertThat(authorizationServerMetadata.getClaimAsString("custom_claim")).isEqualTo("value"); - assertThat(authorizationServerMetadata.getClaimAsStringList("custom_collection_claim")).containsExactlyInAnyOrder("value1", "value2"); + assertThat(authorizationServerMetadata.getClaimAsStringList("custom_collection_claim")) + .containsExactlyInAnyOrder("value1", "value2"); } @Test @@ -139,60 +154,68 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse("{}".getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response)) - .withMessageContaining("An error occurred reading the OAuth 2.0 Authorization Server Metadata") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response)) + .withMessageContaining("An error occurred reading the OAuth 2.0 Authorization Server Metadata") + .withMessageContaining(errorMessage); } @Test public void readInternalWhenInvalidOAuth2AuthorizationServerMetadataThenThrowException() { String authorizationServerMetadataResponse = "{ \"issuer\": null }"; - MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(authorizationServerMetadataResponse.getBytes(), + HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response)) - .withMessageContaining("An error occurred reading the OAuth 2.0 Authorization Server Metadata") - .withMessageContaining("issuer cannot be null"); + .isThrownBy(() -> this.messageConverter.readInternal(OAuth2AuthorizationServerMetadata.class, response)) + .withMessageContaining("An error occurred reading the OAuth 2.0 Authorization Server Metadata") + .withMessageContaining("issuer cannot be null"); } @Test public void writeInternalWhenOAuth2AuthorizationServerMetadataThenSuccess() { - OAuth2AuthorizationServerMetadata authorizationServerMetadata = - OAuth2AuthorizationServerMetadata.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .jwkSetUrl("https://example.com/oauth2/jwks") - .scope("openid") - .responseType("code") - .grantType("authorization_code") - .grantType("client_credentials") - .tokenRevocationEndpoint("https://example.com/oauth2/revoke") - .tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .tokenIntrospectionEndpoint("https://example.com/oauth2/introspect") - .tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .codeChallengeMethod("S256") - .claim("custom_claim", "value") - .claim("custom_collection_claim", Arrays.asList("value1", "value2")) - .build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .jwkSetUrl("https://example.com/oauth2/jwks") + .scope("openid") + .responseType("code") + .grantType("authorization_code") + .grantType("client_credentials") + .tokenRevocationEndpoint("https://example.com/oauth2/revoke") + .tokenRevocationEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .tokenIntrospectionEndpoint("https://example.com/oauth2/introspect") + .tokenIntrospectionEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .codeChallengeMethod("S256") + .claim("custom_claim", "value") + .claim("custom_collection_claim", Arrays.asList("value1", "value2")) + .build(); MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); this.messageConverter.writeInternal(authorizationServerMetadata, outputMessage); String authorizationServerMetadataResponse = outputMessage.getBodyAsString(); assertThat(authorizationServerMetadataResponse).contains("\"issuer\":\"https://example.com\""); - assertThat(authorizationServerMetadataResponse).contains("\"authorization_endpoint\":\"https://example.com/oauth2/authorize\""); - assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint\":\"https://example.com/oauth2/token\""); - assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"authorization_endpoint\":\"https://example.com/oauth2/authorize\""); + assertThat(authorizationServerMetadataResponse) + .contains("\"token_endpoint\":\"https://example.com/oauth2/token\""); + assertThat(authorizationServerMetadataResponse) + .contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); assertThat(authorizationServerMetadataResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/jwks\""); assertThat(authorizationServerMetadataResponse).contains("\"scopes_supported\":[\"openid\"]"); assertThat(authorizationServerMetadataResponse).contains("\"response_types_supported\":[\"code\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint\":\"https://example.com/oauth2/revoke\""); - assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/introspect\""); - assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"revocation_endpoint\":\"https://example.com/oauth2/revoke\""); + assertThat(authorizationServerMetadataResponse) + .contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"introspection_endpoint\":\"https://example.com/oauth2/introspect\""); + assertThat(authorizationServerMetadataResponse) + .contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\"]"); assertThat(authorizationServerMetadataResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]"); assertThat(authorizationServerMetadataResponse).contains("\"custom_claim\":\"value\""); assertThat(authorizationServerMetadataResponse).contains("\"custom_collection_claim\":[\"value1\",\"value2\"]"); @@ -201,24 +224,23 @@ public class OAuth2AuthorizationServerMetadataHttpMessageConverterTests { @Test public void writeInternalWhenWriteFailsThenThrowException() { String errorMessage = "this is not a valid converter"; - Converter> failingConverter = - source -> { - throw new RuntimeException(errorMessage); - }; + Converter> failingConverter = source -> { + throw new RuntimeException(errorMessage); + }; this.messageConverter.setAuthorizationServerMetadataParametersConverter(failingConverter); MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); - OAuth2AuthorizationServerMetadata authorizationServerMetadata = - OAuth2AuthorizationServerMetadata.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .responseType("code") - .build(); + OAuth2AuthorizationServerMetadata authorizationServerMetadata = OAuth2AuthorizationServerMetadata.builder() + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .responseType("code") + .build(); assertThatExceptionOfType(HttpMessageNotWritableException.class) - .isThrownBy(() -> this.messageConverter.writeInternal(authorizationServerMetadata, outputMessage)) - .withMessageContaining("An error occurred writing the OAuth 2.0 Authorization Server Metadata") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.writeInternal(authorizationServerMetadata, outputMessage)) + .withMessageContaining("An error occurred writing the OAuth 2.0 Authorization Server Metadata") + .withMessageContaining(errorMessage); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverterTests.java index 3843310a..ddf06789 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/http/converter/OAuth2TokenIntrospectionHttpMessageConverterTests.java @@ -43,6 +43,7 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; * @author Joe Grandja */ public class OAuth2TokenIntrospectionHttpMessageConverterTests { + private final OAuth2TokenIntrospectionHttpMessageConverter messageConverter = new OAuth2TokenIntrospectionHttpMessageConverter(); @Test @@ -53,13 +54,13 @@ public class OAuth2TokenIntrospectionHttpMessageConverterTests { @Test public void setTokenIntrospectionParametersConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.messageConverter.setTokenIntrospectionParametersConverter(null)); + .isThrownBy(() -> this.messageConverter.setTokenIntrospectionParametersConverter(null)); } @Test public void setTokenIntrospectionConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.messageConverter.setTokenIntrospectionConverter(null)); + .isThrownBy(() -> this.messageConverter.setTokenIntrospectionConverter(null)); } @Test @@ -80,21 +81,23 @@ public class OAuth2TokenIntrospectionHttpMessageConverterTests { + " \"jti\": \"jwtId1\"\n" + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse( - tokenIntrospectionResponseBody.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(tokenIntrospectionResponseBody.getBytes(), + HttpStatus.OK); OAuth2TokenIntrospection tokenIntrospectionResponse = this.messageConverter - .readInternal(OAuth2TokenIntrospection.class, response); + .readInternal(OAuth2TokenIntrospection.class, response); assertThat(tokenIntrospectionResponse.isActive()).isTrue(); assertThat(tokenIntrospectionResponse.getClientId()).isEqualTo("clientId1"); assertThat(tokenIntrospectionResponse.getUsername()).isEqualTo("username1"); assertThat(tokenIntrospectionResponse.getIssuedAt()).isEqualTo(Instant.ofEpochSecond(1607633867L)); assertThat(tokenIntrospectionResponse.getExpiresAt()).isEqualTo(Instant.ofEpochSecond(1607637467L)); - assertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(Arrays.asList("scope1", "scope2")); + assertThat(tokenIntrospectionResponse.getScopes()) + .containsExactlyInAnyOrderElementsOf(Arrays.asList("scope1", "scope2")); assertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo("Bearer"); assertThat(tokenIntrospectionResponse.getNotBefore()).isEqualTo(Instant.ofEpochSecond(1607633867L)); assertThat(tokenIntrospectionResponse.getSubject()).isEqualTo("subject1"); - assertThat(tokenIntrospectionResponse.getAudience()).containsExactlyInAnyOrderElementsOf(Arrays.asList("audience1", "audience2")); + assertThat(tokenIntrospectionResponse.getAudience()) + .containsExactlyInAnyOrderElementsOf(Arrays.asList("audience1", "audience2")); assertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(new URL("https://example.com/issuer1")); assertThat(tokenIntrospectionResponse.getId()).isEqualTo("jwtId1"); } @@ -108,9 +111,9 @@ public class OAuth2TokenIntrospectionHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse("{}".getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OAuth2TokenIntrospection.class, response)) - .withMessageContaining("An error occurred reading the Token Introspection Response") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.readInternal(OAuth2TokenIntrospection.class, response)) + .withMessageContaining("An error occurred reading the Token Introspection Response") + .withMessageContaining(errorMessage); } @Test @@ -163,8 +166,9 @@ public class OAuth2TokenIntrospectionHttpMessageConverterTests { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); assertThatThrownBy(() -> this.messageConverter.writeInternal(tokenClaims, outputMessage)) - .isInstanceOf(HttpMessageNotWritableException.class) - .hasMessageContaining("An error occurred writing the Token Introspection Response") - .hasMessageContaining(errorMessage); + .isInstanceOf(HttpMessageNotWritableException.class) + .hasMessageContaining("An error occurred writing the Token Introspection Response") + .hasMessageContaining(errorMessage); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2ModuleTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2ModuleTests.java index f92d6b3e..c5df9e6d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2ModuleTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/jackson2/OAuth2AuthorizationServerJackson2ModuleTests.java @@ -39,6 +39,7 @@ public class OAuth2AuthorizationServerJackson2ModuleTests { private static final TypeReference> STRING_OBJECT_MAP = new TypeReference>() { }; + private static final TypeReference> STRING_SET = new TypeReference>() { }; @@ -71,4 +72,5 @@ public class OAuth2AuthorizationServerJackson2ModuleTests { String json = this.objectMapper.writeValueAsString(set); assertThat(this.objectMapper.readValue(json, STRING_SET)).isEqualTo(set); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistrationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistrationTests.java index de126fda..5ae9279f 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistrationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcClientRegistrationTests.java @@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Joe Grandja */ public class OidcClientRegistrationTests { + // @formatter:off private final OidcClientRegistration.Builder minimalBuilder = OidcClientRegistration.builder() @@ -80,16 +81,21 @@ public class OidcClientRegistrationTests { assertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt); assertThat(clientRegistration.getClientName()).isEqualTo("client-name"); assertThat(clientRegistration.getRedirectUris()).containsOnly("https://client.example.com"); - assertThat(clientRegistration.getPostLogoutRedirectUris()).containsOnly("https://client.example.com/oidc-post-logout"); - assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); - assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()).isEqualTo(MacAlgorithm.HS256.getName()); - assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); + assertThat(clientRegistration.getPostLogoutRedirectUris()) + .containsOnly("https://client.example.com/oidc-post-logout"); + assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); + assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()) + .isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); assertThat(clientRegistration.getResponseTypes()).containsOnly("code"); assertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder("scope1", "scope2"); assertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL("https://client.example.com/jwks")); assertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo("RS256"); assertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo("registration-access-token"); - assertThat(clientRegistration.getRegistrationClientUrl().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1"); + assertThat(clientRegistration.getRegistrationClientUrl().toString()) + .isEqualTo("https://auth-server.com/connect/register?client_id=1"); assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value"); } @@ -110,17 +116,21 @@ public class OidcClientRegistrationTests { claims.put(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, clientSecretExpiresAt); claims.put(OidcClientMetadataClaimNames.CLIENT_NAME, "client-name"); claims.put(OidcClientMetadataClaimNames.REDIRECT_URIS, Collections.singletonList("https://client.example.com")); - claims.put(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, Collections.singletonList("https://client.example.com/oidc-post-logout")); - claims.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD, ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); + claims.put(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, + Collections.singletonList("https://client.example.com/oidc-post-logout")); + claims.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_METHOD, + ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); claims.put(OidcClientMetadataClaimNames.TOKEN_ENDPOINT_AUTH_SIGNING_ALG, MacAlgorithm.HS256.getName()); - claims.put(OidcClientMetadataClaimNames.GRANT_TYPES, Arrays.asList( - AuthorizationGrantType.AUTHORIZATION_CODE.getValue(), AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())); + claims.put(OidcClientMetadataClaimNames.GRANT_TYPES, + Arrays.asList(AuthorizationGrantType.AUTHORIZATION_CODE.getValue(), + AuthorizationGrantType.CLIENT_CREDENTIALS.getValue())); claims.put(OidcClientMetadataClaimNames.RESPONSE_TYPES, Collections.singletonList("code")); claims.put(OidcClientMetadataClaimNames.SCOPE, Arrays.asList("scope1", "scope2")); claims.put(OidcClientMetadataClaimNames.JWKS_URI, "https://client.example.com/jwks"); claims.put(OidcClientMetadataClaimNames.ID_TOKEN_SIGNED_RESPONSE_ALG, SignatureAlgorithm.RS256.getName()); claims.put(OidcClientMetadataClaimNames.REGISTRATION_ACCESS_TOKEN, "registration-access-token"); - claims.put(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, "https://auth-server.com/connect/register?client_id=1"); + claims.put(OidcClientMetadataClaimNames.REGISTRATION_CLIENT_URI, + "https://auth-server.com/connect/register?client_id=1"); claims.put("a-claim", "a-value"); OidcClientRegistration clientRegistration = OidcClientRegistration.withClaims(claims).build(); @@ -131,51 +141,48 @@ public class OidcClientRegistrationTests { assertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(clientSecretExpiresAt); assertThat(clientRegistration.getClientName()).isEqualTo("client-name"); assertThat(clientRegistration.getRedirectUris()).containsOnly("https://client.example.com"); - assertThat(clientRegistration.getPostLogoutRedirectUris()).containsOnly("https://client.example.com/oidc-post-logout"); - assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); - assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()).isEqualTo(MacAlgorithm.HS256.getName()); - assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); + assertThat(clientRegistration.getPostLogoutRedirectUris()) + .containsOnly("https://client.example.com/oidc-post-logout"); + assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); + assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()) + .isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); assertThat(clientRegistration.getResponseTypes()).containsOnly("code"); assertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder("scope1", "scope2"); assertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL("https://client.example.com/jwks")); assertThat(clientRegistration.getIdTokenSignedResponseAlgorithm()).isEqualTo("RS256"); assertThat(clientRegistration.getRegistrationAccessToken()).isEqualTo("registration-access-token"); - assertThat(clientRegistration.getRegistrationClientUrl().toString()).isEqualTo("https://auth-server.com/connect/register?client_id=1"); + assertThat(clientRegistration.getRegistrationClientUrl().toString()) + .isEqualTo("https://auth-server.com/connect/register?client_id=1"); assertThat(clientRegistration.getClaimAsString("a-claim")).isEqualTo("a-value"); } @Test public void withClaimsWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OidcClientRegistration.withClaims(null)) - .withMessage("claims cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OidcClientRegistration.withClaims(null)) + .withMessage("claims cannot be empty"); } @Test public void withClaimsWhenEmptyThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OidcClientRegistration.withClaims(Collections.emptyMap())) - .withMessage("claims cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OidcClientRegistration.withClaims(Collections.emptyMap())) + .withMessage("claims cannot be empty"); } @Test public void buildWhenMissingClientIdThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .clientIdIssuedAt(Instant.now()); + OidcClientRegistration.Builder builder = this.minimalBuilder.clientIdIssuedAt(Instant.now()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("client_id cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("client_id cannot be null"); } @Test public void buildWhenClientSecretAndMissingClientIdThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .clientSecret("client-secret"); + OidcClientRegistration.Builder builder = this.minimalBuilder.clientSecret("client-secret"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("client_id cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("client_id cannot be null"); } @Test @@ -186,9 +193,8 @@ public class OidcClientRegistrationTests { .claim(OidcClientMetadataClaimNames.CLIENT_ID_ISSUED_AT, "clientIdIssuedAt"); // @formatter:on - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("client_id_issued_at must be of type Instant"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("client_id_issued_at must be of type Instant"); } @Test @@ -200,9 +206,7 @@ public class OidcClientRegistrationTests { .clientSecretExpiresAt(Instant.now().plus(30, ChronoUnit.DAYS)); // @formatter:on - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("client_secret cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("client_secret cannot be null"); } @Test @@ -215,39 +219,32 @@ public class OidcClientRegistrationTests { .claim(OidcClientMetadataClaimNames.CLIENT_SECRET_EXPIRES_AT, "clientSecretExpiresAt"); // @formatter:on - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("client_secret_expires_at must be of type Instant"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("client_secret_expires_at must be of type Instant"); } @Test public void buildWhenMissingRedirectUrisThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = OidcClientRegistration.builder() - .clientName("client-name"); + OidcClientRegistration.Builder builder = OidcClientRegistration.builder().clientName("client-name"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("redirect_uris cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("redirect_uris cannot be null"); } @Test public void buildWhenRedirectUrisNotListThenThrowIllegalArgumentException() { OidcClientRegistration.Builder builder = OidcClientRegistration.builder() - .claim(OidcClientMetadataClaimNames.REDIRECT_URIS, "redirectUris"); + .claim(OidcClientMetadataClaimNames.REDIRECT_URIS, "redirectUris"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("redirect_uris must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("redirect_uris must be of type List"); } @Test public void buildWhenRedirectUrisEmptyListThenThrowIllegalArgumentException() { OidcClientRegistration.Builder builder = OidcClientRegistration.builder() - .claim(OidcClientMetadataClaimNames.REDIRECT_URIS, Collections.emptyList()); + .claim(OidcClientMetadataClaimNames.REDIRECT_URIS, Collections.emptyList()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("redirect_uris cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("redirect_uris cannot be empty"); } @Test @@ -268,21 +265,19 @@ public class OidcClientRegistrationTests { @Test public void buildWhenPostLogoutRedirectUrisNotListThenThrowIllegalArgumentException() { OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, "postLogoutRedirectUris"); + .claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, "postLogoutRedirectUris"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("post_logout_redirect_uris must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("post_logout_redirect_uris must be of type List"); } @Test public void buildWhenPostLogoutRedirectUrisEmptyListThenThrowIllegalArgumentException() { OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, Collections.emptyList()); + .claim(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS, Collections.emptyList()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("post_logout_redirect_uris cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("post_logout_redirect_uris cannot be empty"); } @Test @@ -297,27 +292,25 @@ public class OidcClientRegistrationTests { .build(); // @formatter:on - assertThat(clientRegistration.getPostLogoutRedirectUris()).containsExactly("https://client2.example.com/oidc-post-logout"); + assertThat(clientRegistration.getPostLogoutRedirectUris()) + .containsExactly("https://client2.example.com/oidc-post-logout"); } @Test public void buildWhenGrantTypesNotListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.GRANT_TYPES, "grantTypes"); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.GRANT_TYPES, + "grantTypes"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("grant_types must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("grant_types must be of type List"); } @Test public void buildWhenGrantTypesEmptyListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.GRANT_TYPES, Collections.emptyList()); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.GRANT_TYPES, + Collections.emptyList()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("grant_types cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("grant_types cannot be empty"); } @Test @@ -337,22 +330,19 @@ public class OidcClientRegistrationTests { @Test public void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.RESPONSE_TYPES, "responseTypes"); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.RESPONSE_TYPES, + "responseTypes"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("response_types must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("response_types must be of type List"); } @Test public void buildWhenResponseTypesEmptyListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.RESPONSE_TYPES, Collections.emptyList()); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.RESPONSE_TYPES, + Collections.emptyList()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("response_types cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("response_types cannot be empty"); } @Test @@ -372,22 +362,19 @@ public class OidcClientRegistrationTests { @Test public void buildWhenScopesNotListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.SCOPE, "scopes"); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.SCOPE, + "scopes"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("scope must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("scope must be of type List"); } @Test public void buildWhenScopesEmptyListThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.SCOPE, Collections.emptyList()); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.SCOPE, + Collections.emptyList()); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("scope cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("scope cannot be empty"); } @Test @@ -407,26 +394,24 @@ public class OidcClientRegistrationTests { @Test public void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() { - OidcClientRegistration.Builder builder = this.minimalBuilder - .claim(OidcClientMetadataClaimNames.JWKS_URI, "not an url"); + OidcClientRegistration.Builder builder = this.minimalBuilder.claim(OidcClientMetadataClaimNames.JWKS_URI, + "not an url"); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("jwksUri must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("jwksUri must be a valid URL"); } @Test public void claimWhenNameNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OidcClientRegistration.builder().claim(null, "claim-value")) - .withMessage("name cannot be empty"); + .isThrownBy(() -> OidcClientRegistration.builder().claim(null, "claim-value")) + .withMessage("name cannot be empty"); } @Test public void claimWhenValueNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OidcClientRegistration.builder().claim("claim-name", null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> OidcClientRegistration.builder().claim("claim-name", null)) + .withMessage("value cannot be null"); } @Test diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfigurationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfigurationTests.java index 6a160b26..0ff05965 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfigurationTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/OidcProviderConfigurationTests.java @@ -35,49 +35,53 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Daniel Garnier-Moiroux */ public class OidcProviderConfigurationTests { - private final OidcProviderConfiguration.Builder minimalConfigurationBuilder = - OidcProviderConfiguration.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .jwkSetUrl("https://example.com/oauth2/jwks") - .scope("openid") - .responseType("code") - .subjectType("public") - .idTokenSigningAlgorithm("RS256"); + + private final OidcProviderConfiguration.Builder minimalConfigurationBuilder = OidcProviderConfiguration.builder() + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .jwkSetUrl("https://example.com/oauth2/jwks") + .scope("openid") + .responseType("code") + .subjectType("public") + .idTokenSigningAlgorithm("RS256"); @Test public void buildWhenAllRequiredClaimsAndAdditionalClaimsThenCreated() { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .jwkSetUrl("https://example.com/oauth2/jwks") - .scope("openid") - .responseType("code") - .grantType("authorization_code") - .grantType("client_credentials") - .subjectType("public") - .idTokenSigningAlgorithm("RS256") - .userInfoEndpoint("https://example.com/userinfo") - .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .clientRegistrationEndpoint("https://example.com/connect/register") - .endSessionEndpoint("https://example.com/connect/logout") - .claim("a-claim", "a-value") - .build(); + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .jwkSetUrl("https://example.com/oauth2/jwks") + .scope("openid") + .responseType("code") + .grantType("authorization_code") + .grantType("client_credentials") + .subjectType("public") + .idTokenSigningAlgorithm("RS256") + .userInfoEndpoint("https://example.com/userinfo") + .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .clientRegistrationEndpoint("https://example.com/connect/register") + .endSessionEndpoint("https://example.com/connect/logout") + .claim("a-claim", "a-value") + .build(); assertThat(providerConfiguration.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getScopes()).containsExactly("openid"); assertThat(providerConfiguration.getResponseTypes()).containsExactly("code"); - assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); + assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256"); assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/userinfo")); - assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); - assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/connect/register")); + assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(providerConfiguration.getClientRegistrationEndpoint()) + .isEqualTo(url("https://example.com/connect/register")); assertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url("https://example.com/connect/logout")); assertThat(providerConfiguration.getClaim("a-claim")).isEqualTo("a-value"); } @@ -85,18 +89,19 @@ public class OidcProviderConfigurationTests { @Test public void buildWhenOnlyRequiredClaimsThenCreated() { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .jwkSetUrl("https://example.com/oauth2/jwks") - .scope("openid") - .responseType("code") - .subjectType("public") - .idTokenSigningAlgorithm("RS256") - .build(); + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .jwkSetUrl("https://example.com/oauth2/jwks") + .scope("openid") + .responseType("code") + .subjectType("public") + .idTokenSigningAlgorithm("RS256") + .build(); assertThat(providerConfiguration.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getScopes()).containsExactly("openid"); @@ -117,7 +122,8 @@ public class OidcProviderConfigurationTests { 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(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256")); + claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, + Collections.singletonList("RS256")); claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "https://example.com/userinfo"); claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, "https://example.com/connect/register"); claims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, "https://example.com/connect/logout"); @@ -126,7 +132,8 @@ public class OidcProviderConfigurationTests { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build(); assertThat(providerConfiguration.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getScopes()).containsExactly("openid"); @@ -136,7 +143,8 @@ public class OidcProviderConfigurationTests { assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256"); assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/userinfo")); assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull(); - assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/connect/register")); + assertThat(providerConfiguration.getClientRegistrationEndpoint()) + .isEqualTo(url("https://example.com/connect/register")); assertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url("https://example.com/connect/logout")); assertThat(providerConfiguration.getClaim("some-claim")).isEqualTo("some-value"); } @@ -151,7 +159,8 @@ public class OidcProviderConfigurationTests { 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(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.singletonList("RS256")); + claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, + Collections.singletonList("RS256")); claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, url("https://example.com/userinfo")); claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, url("https://example.com/connect/register")); claims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, url("https://example.com/connect/logout")); @@ -160,7 +169,8 @@ public class OidcProviderConfigurationTests { OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.withClaims(claims).build(); assertThat(providerConfiguration.getIssuer()).isEqualTo(url("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(url("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(url("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(url("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(url("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getScopes()).containsExactly("openid"); @@ -170,7 +180,8 @@ public class OidcProviderConfigurationTests { assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256"); assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(url("https://example.com/userinfo")); assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).isNull(); - assertThat(providerConfiguration.getClientRegistrationEndpoint()).isEqualTo(url("https://example.com/connect/register")); + assertThat(providerConfiguration.getClientRegistrationEndpoint()) + .isEqualTo(url("https://example.com/connect/register")); assertThat(providerConfiguration.getEndSessionEndpoint()).isEqualTo(url("https://example.com/connect/logout")); assertThat(providerConfiguration.getClaim("some-claim")).isEqualTo("some-value"); } @@ -178,33 +189,27 @@ public class OidcProviderConfigurationTests { @Test public void withClaimsWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException().isThrownBy(() -> OidcProviderConfiguration.withClaims(null)) - .isInstanceOf(IllegalArgumentException.class) - .withMessage("claims cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .withMessage("claims cannot be empty"); } @Test public void withClaimsWhenMissingRequiredClaimsThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OidcProviderConfiguration.withClaims(Collections.emptyMap())) - .withMessage("claims cannot be empty"); + .isThrownBy(() -> OidcProviderConfiguration.withClaims(Collections.emptyMap())) + .withMessage("claims cannot be empty"); } @Test public void buildWhenCalledTwiceThenGeneratesTwoConfigurations() { - OidcProviderConfiguration first = this.minimalConfigurationBuilder - .grantType("client_credentials") - .build(); + OidcProviderConfiguration first = this.minimalConfigurationBuilder.grantType("client_credentials").build(); - OidcProviderConfiguration second = this.minimalConfigurationBuilder - .claims((claims) -> - { - List newGrantTypes = new ArrayList<>(); - newGrantTypes.add("authorization_code"); - newGrantTypes.add("custom_grant"); - claims.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes); - } - ) - .build(); + OidcProviderConfiguration second = this.minimalConfigurationBuilder.claims((claims) -> { + List newGrantTypes = new ArrayList<>(); + newGrantTypes.add("authorization_code"); + newGrantTypes.add("custom_grant"); + claims.put(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED, newGrantTypes); + }).build(); assertThat(first.getGrantTypes()).containsExactly("client_credentials"); assertThat(second.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "custom_grant"); @@ -213,230 +218,197 @@ public class OidcProviderConfigurationTests { @Test public void buildWhenMissingIssuerThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ISSUER)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ISSUER)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("issuer cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("issuer cannot be null"); } @Test public void buildWhenIssuerNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.ISSUER, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.ISSUER, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("issuer must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("issuer must be a valid URL"); } @Test public void buildWhenMissingAuthorizationEndpointThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("authorizationEndpoint cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("authorizationEndpoint cannot be null"); } @Test public void buildWhenAuthorizationEndpointNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.AUTHORIZATION_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("authorizationEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("authorizationEndpoint must be a valid URL"); } @Test public void buildWhenMissingTokenEndpointThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("tokenEndpoint cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("tokenEndpoint cannot be null"); } @Test public void buildWhenTokenEndpointNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.TOKEN_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("tokenEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("tokenEndpoint must be a valid URL"); } @Test public void buildWhenMissingJwksUriThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.JWKS_URI)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.JWKS_URI)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("jwksUri cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("jwksUri cannot be null"); } @Test public void buildWhenJwksUriNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.JWKS_URI, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.JWKS_URI, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageStartingWith("jwksUri must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageStartingWith("jwksUri must be a valid URL"); } @Test public void buildWhenMissingResponseTypesThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("responseTypes cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("responseTypes cannot be null"); } @Test public void buildWhenResponseTypesNotListThenThrowIllegalArgumentException() { - OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> { - claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); - claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, "code"); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, "code"); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("responseTypes must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("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()); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED, Collections.emptyList()); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("responseTypes cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("responseTypes cannot be empty"); } @Test public void buildWhenMissingSubjectTypesThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("subjectTypes cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build).withMessage("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"); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, "public"); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("subjectTypes must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("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()); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.SUBJECT_TYPES_SUPPORTED, Collections.emptyList()); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("subjectTypes cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("subjectTypes cannot be empty"); } @Test public void buildWhenMissingIdTokenSigningAlgorithmsThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED)); + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED)); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("idTokenSigningAlgorithms cannot be null"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("idTokenSigningAlgorithms cannot be null"); } @Test public void buildWhenIdTokenSigningAlgorithmsNotListThenThrowIllegalArgumentException() { - OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> { - claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED); - claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, "RS256"); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, "RS256"); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("idTokenSigningAlgorithms must be of type List"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("idTokenSigningAlgorithms must be of type List"); } @Test public void buildWhenIdTokenSigningAlgorithmsEmptyListThenThrowIllegalArgumentException() { - OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> { - claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED); - claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.emptyList()); - }); + OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder.claims((claims) -> { + claims.remove(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED); + claims.put(OidcProviderMetadataClaimNames.ID_TOKEN_SIGNING_ALG_VALUES_SUPPORTED, Collections.emptyList()); + }); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessageContaining("idTokenSigningAlgorithms cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessageContaining("idTokenSigningAlgorithms cannot be empty"); } @Test public void buildWhenUserInfoEndpointNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.USER_INFO_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("userInfoEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("userInfoEndpoint must be a valid URL"); } @Test public void buildWhenClientRegistrationEndpointNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.REGISTRATION_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("clientRegistrationEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("clientRegistrationEndpoint must be a valid URL"); } @Test public void buildWhenEndSessionEndpointNotUrlThenThrowIllegalArgumentException() { OidcProviderConfiguration.Builder builder = this.minimalConfigurationBuilder - .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, "not an url")); + .claims((claims) -> claims.put(OidcProviderMetadataClaimNames.END_SESSION_ENDPOINT, "not an url")); - assertThatIllegalArgumentException() - .isThrownBy(builder::build) - .withMessage("endSessionEndpoint must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(builder::build) + .withMessage("endSessionEndpoint must be a valid URL"); } @Test public void responseTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .responseType("should-be-removed") - .responseTypes(responseTypes -> { - responseTypes.clear(); - responseTypes.add("some-response-type"); - }) - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.responseType("should-be-removed") + .responseTypes(responseTypes -> { + responseTypes.clear(); + responseTypes.add("some-response-type"); + }) + .build(); assertThat(configuration.getResponseTypes()).containsExactly("some-response-type"); } @@ -444,22 +416,21 @@ public class OidcProviderConfigurationTests { @Test public void responseTypesWhenNotPresentAndAddingThenCorrectValues() { OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .claims(claims -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)) - .responseTypes(responseTypes -> responseTypes.add("some-response-type")) - .build(); + .claims(claims -> claims.remove(OidcProviderMetadataClaimNames.RESPONSE_TYPES_SUPPORTED)) + .responseTypes(responseTypes -> responseTypes.add("some-response-type")) + .build(); assertThat(configuration.getResponseTypes()).containsExactly("some-response-type"); } @Test public void subjectTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .subjectType("should-be-removed") - .subjectTypes(subjectTypes -> { - subjectTypes.clear(); - subjectTypes.add("some-subject-type"); - }) - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.subjectType("should-be-removed") + .subjectTypes(subjectTypes -> { + subjectTypes.clear(); + subjectTypes.add("some-subject-type"); + }) + .build(); assertThat(configuration.getSubjectTypes()).containsExactly("some-subject-type"); } @@ -467,38 +438,36 @@ public class OidcProviderConfigurationTests { @Test public void idTokenSigningAlgorithmsWhenAddingOrRemovingThenCorrectValues() { OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .idTokenSigningAlgorithm("should-be-removed") - .idTokenSigningAlgorithms(signingAlgorithms -> { - signingAlgorithms.clear(); - signingAlgorithms.add("ES256"); - }) - .build(); + .idTokenSigningAlgorithm("should-be-removed") + .idTokenSigningAlgorithms(signingAlgorithms -> { + signingAlgorithms.clear(); + signingAlgorithms.add("ES256"); + }) + .build(); assertThat(configuration.getIdTokenSigningAlgorithms()).containsExactly("ES256"); } @Test public void scopesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .scope("should-be-removed") - .scopes(scopes -> { - scopes.clear(); - scopes.add("some-scope"); - }) - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.scope("should-be-removed") + .scopes(scopes -> { + scopes.clear(); + scopes.add("some-scope"); + }) + .build(); assertThat(configuration.getScopes()).containsExactly("some-scope"); } @Test public void grantTypesWhenAddingOrRemovingThenCorrectValues() { - OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .grantType("should-be-removed") - .grantTypes(grantTypes -> { - grantTypes.clear(); - grantTypes.add("some-grant-type"); - }) - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.grantType("should-be-removed") + .grantTypes(grantTypes -> { + grantTypes.clear(); + grantTypes.add("some-grant-type"); + }) + .build(); assertThat(configuration.getGrantTypes()).containsExactly("some-grant-type"); } @@ -506,54 +475,51 @@ public class OidcProviderConfigurationTests { @Test public void tokenEndpointAuthenticationMethodsWhenAddingOrRemovingThenCorrectValues() { OidcProviderConfiguration configuration = this.minimalConfigurationBuilder - .tokenEndpointAuthenticationMethod("should-be-removed") - .tokenEndpointAuthenticationMethods(authMethods -> { - authMethods.clear(); - authMethods.add("some-authentication-method"); - }) - .build(); + .tokenEndpointAuthenticationMethod("should-be-removed") + .tokenEndpointAuthenticationMethods(authMethods -> { + authMethods.clear(); + authMethods.add("some-authentication-method"); + }) + .build(); assertThat(configuration.getTokenEndpointAuthenticationMethods()).containsExactly("some-authentication-method"); } @Test public void claimWhenNameIsNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> OidcProviderConfiguration.builder().claim(null, "value")) - .withMessage("name cannot be empty"); + assertThatIllegalArgumentException().isThrownBy(() -> OidcProviderConfiguration.builder().claim(null, "value")) + .withMessage("name cannot be empty"); } @Test public void claimWhenValueIsNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> OidcProviderConfiguration.builder().claim("claim-name", null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> OidcProviderConfiguration.builder().claim("claim-name", null)) + .withMessage("value cannot be null"); } @Test public void claimsWhenRemovingClaimThenNotPresent() { - OidcProviderConfiguration configuration = - this.minimalConfigurationBuilder - .grantType("some-grant-type") - .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED)) - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.grantType("some-grant-type") + .claims((claims) -> claims.remove(OidcProviderMetadataClaimNames.GRANT_TYPES_SUPPORTED)) + .build(); assertThat(configuration.getGrantTypes()).isNull(); } @Test public void claimsWhenAddingClaimThenPresent() { - OidcProviderConfiguration configuration = - this.minimalConfigurationBuilder - .claim("claim-name", "claim-value") - .build(); + OidcProviderConfiguration configuration = this.minimalConfigurationBuilder.claim("claim-name", "claim-value") + .build(); assertThat(configuration.hasClaim("claim-name")).isTrue(); } private static URL url(String urlString) { try { return new URL(urlString); - } catch (Exception ex) { + } + 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/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java index e66bbdd6..7d70a9de 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientConfigurationAuthenticationProviderTests.java @@ -72,9 +72,13 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OidcClientConfigurationAuthenticationProviderTests { + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private AuthorizationServerSettings authorizationServerSettings; + private OidcClientConfigurationAuthenticationProvider authenticationProvider; @BeforeEach @@ -82,9 +86,10 @@ public class OidcClientConfigurationAuthenticationProviderTests { this.registeredClientRepository = mock(RegisteredClientRepository.class); this.authorizationService = mock(OAuth2AuthorizationService.class); this.authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); - this.authenticationProvider = new OidcClientConfigurationAuthenticationProvider( - this.registeredClientRepository, this.authorizationService); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); + this.authenticationProvider = new OidcClientConfigurationAuthenticationProvider(this.registeredClientRepository, + this.authorizationService); } @AfterEach @@ -95,15 +100,15 @@ public class OidcClientConfigurationAuthenticationProviderTests { @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(null, this.authorizationService)) - .withMessage("registeredClientRepository cannot be null"); + .isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(null, this.authorizationService)) + .withMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(this.registeredClientRepository, null)) - .withMessage("authorizationService cannot be null"); + .isThrownBy(() -> new OidcClientConfigurationAuthenticationProvider(this.registeredClientRepository, null)) + .withMessage("authorizationService cannot be null"); } @Test @@ -126,9 +131,10 @@ public class OidcClientConfigurationAuthenticationProviderTests { principal, "client-id"); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); } @Test @@ -139,211 +145,214 @@ public class OidcClientConfigurationAuthenticationProviderTests { principal, "client-id"); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); } @Test public void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientConfiguration(); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, "client-id"); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); authorization = OidcAuthenticationProviderUtils.invalidate(authorization, jwtAccessToken); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwt(Collections.singleton("unauthorized.scope")); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.read", "scope1"))); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read", "SCOPE_scope1")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read", "SCOPE_scope1")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenRegisteredClientNotFoundThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); - verify(this.registeredClientRepository).findByClientId( - eq(registeredClient.getClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); + verify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId())); } @Test public void authenticateWhenClientIdNotEqualToAuthorizedClientThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); RegisteredClient authorizedRegisteredClient = TestRegisteredClients.registeredClient() - .id("registration-2").clientId("client-2").build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - authorizedRegisteredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + .id("registration-2") + .clientId("client-2") + .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(authorizedRegisteredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); - verify(this.registeredClientRepository).findByClientId( - eq(registeredClient.getClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_CLIENT); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); + verify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId())); } @Test public void authenticateWhenValidAccessTokenThenReturnClientRegistration() { Jwt jwt = createJwtClientConfiguration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .clientAuthenticationMethods((clientAuthenticationMethods) -> { - clientAuthenticationMethods.clear(); - clientAuthenticationMethods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT); - }) - .clientSettings( - ClientSettings.builder() - .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS512) - .jwkSetUrl("https://client.example.com/jwks") - .build() - ) - .build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + .clientAuthenticationMethods((clientAuthenticationMethods) -> { + clientAuthenticationMethods.clear(); + clientAuthenticationMethods.add(ClientAuthenticationMethod.PRIVATE_KEY_JWT); + }) + .clientSettings(ClientSettings.builder() + .tokenEndpointAuthenticationSigningAlgorithm(SignatureAlgorithm.RS512) + .jwkSetUrl("https://client.example.com/jwks") + .build()) + .build(); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); when(this.registeredClientRepository.findByClientId(eq(registeredClient.getClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, registeredClient.getClientId()); - OidcClientRegistrationAuthenticationToken authenticationResult = - (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); - verify(this.registeredClientRepository).findByClientId( - eq(registeredClient.getClientId())); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); + verify(this.registeredClientRepository).findByClientId(eq(registeredClient.getClientId())); // verify that the "registration" access token is not invalidated after it is used verify(this.authorizationService, never()).save(eq(authorization)); @@ -353,35 +362,39 @@ public class OidcClientConfigurationAuthenticationProviderTests { assertThat(clientRegistrationResult.getClientId()).isEqualTo(registeredClient.getClientId()); assertThat(clientRegistrationResult.getClientIdIssuedAt()).isEqualTo(registeredClient.getClientIdIssuedAt()); assertThat(clientRegistrationResult.getClientSecret()).isEqualTo(registeredClient.getClientSecret()); - assertThat(clientRegistrationResult.getClientSecretExpiresAt()).isEqualTo(registeredClient.getClientSecretExpiresAt()); + assertThat(clientRegistrationResult.getClientSecretExpiresAt()) + .isEqualTo(registeredClient.getClientSecretExpiresAt()); assertThat(clientRegistrationResult.getClientName()).isEqualTo(registeredClient.getClientName()); assertThat(clientRegistrationResult.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(registeredClient.getRedirectUris()); List grantTypes = new ArrayList<>(); - registeredClient.getAuthorizationGrantTypes().forEach(authorizationGrantType -> - grantTypes.add(authorizationGrantType.getValue())); + registeredClient.getAuthorizationGrantTypes() + .forEach(authorizationGrantType -> grantTypes.add(authorizationGrantType.getValue())); assertThat(clientRegistrationResult.getGrantTypes()).containsExactlyInAnyOrderElementsOf(grantTypes); assertThat(clientRegistrationResult.getResponseTypes()) - .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); + .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); assertThat(clientRegistrationResult.getScopes()) - .containsExactlyInAnyOrderElementsOf(registeredClient.getScopes()); + .containsExactlyInAnyOrderElementsOf(registeredClient.getScopes()); assertThat(clientRegistrationResult.getTokenEndpointAuthenticationMethod()) - .isEqualTo(registeredClient.getClientAuthenticationMethods().iterator().next().getValue()); + .isEqualTo(registeredClient.getClientAuthenticationMethods().iterator().next().getValue()); assertThat(clientRegistrationResult.getTokenEndpointAuthenticationSigningAlgorithm()) - .isEqualTo(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm().getName()); + .isEqualTo(registeredClient.getClientSettings().getTokenEndpointAuthenticationSigningAlgorithm().getName()); assertThat(clientRegistrationResult.getJwkSetUrl().toString()) - .isEqualTo(registeredClient.getClientSettings().getJwkSetUrl()); + .isEqualTo(registeredClient.getClientSettings().getJwkSetUrl()); assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); + .isEqualTo(registeredClient.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); - String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(authorizationServerContext.getIssuer()) - .path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint()) - .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()).toUriString(); + String expectedRegistrationClientUrl = UriComponentsBuilder + .fromUriString(authorizationServerContext.getIssuer()) + .path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint()) + .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()) + .toUriString(); - assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()).isEqualTo(expectedRegistrationClientUrl); + assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()) + .isEqualTo(expectedRegistrationClientUrl); assertThat(clientRegistrationResult.getRegistrationAccessToken()).isNull(); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java index a7357585..8124f01b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationProviderTests.java @@ -87,12 +87,19 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OidcClientRegistrationAuthenticationProviderTests { + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private JwtEncoder jwtEncoder; + private OAuth2TokenGenerator tokenGenerator; + private PasswordEncoder passwordEncoder; + private AuthorizationServerSettings authorizationServerSettings; + private OidcClientRegistrationAuthenticationProvider authenticationProvider; @BeforeEach @@ -119,9 +126,10 @@ public class OidcClientRegistrationAuthenticationProviderTests { } }); this.authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); - this.authenticationProvider = new OidcClientRegistrationAuthenticationProvider( - this.registeredClientRepository, this.authorizationService, this.tokenGenerator); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(this.authorizationServerSettings, null)); + this.authenticationProvider = new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, this.tokenGenerator); this.authenticationProvider.setPasswordEncoder(this.passwordEncoder); } @@ -133,29 +141,32 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(null, this.authorizationService, this.tokenGenerator)) - .withMessage("registeredClientRepository cannot be null"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(null, this.authorizationService, + this.tokenGenerator)) + .withMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, null, this.tokenGenerator)) - .withMessage("authorizationService cannot be null"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, null, + this.tokenGenerator)) + .withMessage("authorizationService cannot be null"); } @Test public void constructorWhenTokenGeneratorNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, this.authorizationService, null)) - .withMessage("tokenGenerator cannot be null"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, null)) + .withMessage("tokenGenerator cannot be null"); } @Test public void setRegisteredClientConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.authenticationProvider.setRegisteredClientConverter(null)) - .withMessage("registeredClientConverter cannot be null"); + .isThrownBy(() -> this.authenticationProvider.setRegisteredClientConverter(null)) + .withMessage("registeredClientConverter cannot be null"); } @Test @@ -168,8 +179,8 @@ public class OidcClientRegistrationAuthenticationProviderTests { @Test public void setPasswordEncoderWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.authenticationProvider.setPasswordEncoder(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("passwordEncoder cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("passwordEncoder cannot be null"); } @Test @@ -181,160 +192,165 @@ public class OidcClientRegistrationAuthenticationProviderTests { public void authenticateWhenPrincipalNotOAuth2TokenAuthenticationTokenThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); } @Test public void authenticateWhenPrincipalNotAuthenticatedThenThrowOAuth2AuthenticationException() { JwtAuthenticationToken principal = new JwtAuthenticationToken(createJwtClientRegistration()); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); } @Test public void authenticateWhenAccessTokenNotFoundThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwt.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenNotActiveThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); authorization = OidcAuthenticationProviderUtils.invalidate(authorization, jwtAccessToken); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwt(Collections.singleton("unauthorized.scope")); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_unauthorized.scope")); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenAccessTokenContainsRequiredScopeAndAdditionalScopeThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwt(new HashSet<>(Arrays.asList("client.create", "scope1"))); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create", "SCOPE_scope1")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create", "SCOPE_scope1")); OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com") - .build(); + .redirectUri("https://client.example.com") + .build(); OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()).extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenInvalidRedirectUriThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("invalid uri") @@ -345,31 +361,31 @@ public class OidcClientRegistrationAuthenticationProviderTests { principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI); - assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS); - }); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI); + assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS); + }); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com#fragment") @@ -380,31 +396,31 @@ public class OidcClientRegistrationAuthenticationProviderTests { principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI); - assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS); - }); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REDIRECT_URI); + assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.REDIRECT_URIS); + }); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenInvalidPostLogoutRedirectUriThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com") @@ -416,31 +432,31 @@ public class OidcClientRegistrationAuthenticationProviderTests { principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo("invalid_client_metadata"); - assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); - }); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo("invalid_client_metadata"); + assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); + }); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenPostLogoutRedirectUriContainsFragmentThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .redirectUri("https://client.example.com") @@ -452,31 +468,31 @@ public class OidcClientRegistrationAuthenticationProviderTests { principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo("invalid_client_metadata"); - assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); - }); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo("invalid_client_metadata"); + assertThat(error.getDescription()).contains(OidcClientMetadataClaimNames.POST_LOGOUT_REDIRECT_URIS); + }); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); } @Test public void authenticateWhenInvalidTokenEndpointAuthenticationMethodThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration.Builder builder = OidcClientRegistration.builder() .redirectUri("https://client.example.com"); @@ -527,36 +543,37 @@ public class OidcClientRegistrationAuthenticationProviderTests { } private void assertWhenClientRegistrationRequestInvalidThenThrowOAuth2AuthenticationException( - Authentication principal, OidcClientRegistration clientRegistration, String errorCode, String errorDescription) { + Authentication principal, OidcClientRegistration clientRegistration, String errorCode, + String errorDescription) { OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(errorCode); - assertThat(error.getDescription()).contains(errorDescription); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(errorCode); + assertThat(error.getDescription()).contains(errorDescription); + }); } @Test public void authenticateWhenTokenEndpointAuthenticationSigningAlgorithmNotProvidedThenDefaults() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); when(this.jwtEncoder.encode(any())).thenReturn(createJwtClientConfiguration()); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration.Builder builder = OidcClientRegistration.builder() .grantType(AuthorizationGrantType.AUTHORIZATION_CODE.getValue()) @@ -570,10 +587,10 @@ public class OidcClientRegistrationAuthenticationProviderTests { // @formatter:on OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, builder.build()); - OidcClientRegistrationAuthenticationToken authenticationResult = - (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getClientRegistration().getTokenEndpointAuthenticationSigningAlgorithm()) - .isEqualTo(MacAlgorithm.HS256.getName()); + .isEqualTo(MacAlgorithm.HS256.getName()); assertThat(authenticationResult.getClientRegistration().getClientSecret()).isNotNull(); verify(this.passwordEncoder).encode(any()); reset(this.passwordEncoder); @@ -584,9 +601,10 @@ public class OidcClientRegistrationAuthenticationProviderTests { .jwkSetUrl("https://client.example.com/jwks"); // @formatter:on authentication = new OidcClientRegistrationAuthenticationToken(principal, builder.build()); - authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getClientRegistration().getTokenEndpointAuthenticationSigningAlgorithm()) - .isEqualTo(SignatureAlgorithm.RS256.getName()); + .isEqualTo(SignatureAlgorithm.RS256.getName()); assertThat(authenticationResult.getClientRegistration().getClientSecret()).isNull(); verifyNoInteractions(this.passwordEncoder); } @@ -595,19 +613,19 @@ public class OidcClientRegistrationAuthenticationProviderTests { public void authenticateWhenRegistrationAccessTokenNotGeneratedThenThrowOAuth2AuthenticationException() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); doReturn(null).when(this.tokenGenerator).generate(any()); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .clientName("client-name") @@ -623,30 +641,31 @@ public class OidcClientRegistrationAuthenticationProviderTests { principal, clientRegistration); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); - assertThat(error.getDescription()).contains("The token generator failed to generate the registration access token."); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.SERVER_ERROR); + assertThat(error.getDescription()) + .contains("The token generator failed to generate the registration access token."); + }); } @Test public void authenticateWhenValidAccessTokenThenReturnClientRegistration() { Jwt jwt = createJwtClientRegistration(); OAuth2AccessToken jwtAccessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - jwt.getTokenValue(), jwt.getIssuedAt(), - jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); + jwt.getTokenValue(), jwt.getIssuedAt(), jwt.getExpiresAt(), jwt.getClaim(OAuth2ParameterNames.SCOPE)); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, jwtAccessToken, jwt.getClaims()).build(); - when(this.authorizationService.findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, jwtAccessToken, jwt.getClaims()) + .build(); + when(this.authorizationService.findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN))) + .thenReturn(authorization); when(this.jwtEncoder.encode(any())).thenReturn(createJwtClientConfiguration()); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); // @formatter:off OidcClientRegistration clientRegistration = OidcClientRegistration.builder() .clientName("client-name") @@ -661,23 +680,23 @@ public class OidcClientRegistrationAuthenticationProviderTests { OidcClientRegistrationAuthenticationToken authentication = new OidcClientRegistrationAuthenticationToken( principal, clientRegistration); - OidcClientRegistrationAuthenticationToken authenticationResult = - (OidcClientRegistrationAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OidcClientRegistrationAuthenticationToken authenticationResult = (OidcClientRegistrationAuthenticationToken) this.authenticationProvider + .authenticate(authentication); ArgumentCaptor registeredClientCaptor = ArgumentCaptor.forClass(RegisteredClient.class); ArgumentCaptor authorizationCaptor = ArgumentCaptor.forClass(OAuth2Authorization.class); - verify(this.authorizationService).findByToken( - eq(jwtAccessToken.getTokenValue()), eq(OAuth2TokenType.ACCESS_TOKEN)); + verify(this.authorizationService).findByToken(eq(jwtAccessToken.getTokenValue()), + eq(OAuth2TokenType.ACCESS_TOKEN)); verify(this.registeredClientRepository).save(registeredClientCaptor.capture()); verify(this.authorizationService, times(2)).save(authorizationCaptor.capture()); verify(this.jwtEncoder).encode(any()); verify(this.passwordEncoder).encode(any()); - // assert "registration" access token, which should be used for subsequent calls to client configuration endpoint + // assert "registration" access token, which should be used for subsequent calls + // to client configuration endpoint OAuth2Authorization authorizationResult = authorizationCaptor.getAllValues().get(0); - assertThat(authorizationResult.getAccessToken().getToken().getScopes()) - .containsExactly("client.read"); + assertThat(authorizationResult.getAccessToken().getToken().getScopes()).containsExactly("client.read"); assertThat(authorizationResult.getAccessToken().isActive()).isTrue(); assertThat(authorizationResult.getRefreshToken()).isNull(); @@ -694,47 +713,55 @@ public class OidcClientRegistrationAuthenticationProviderTests { assertThat(registeredClientResult.getClientIdIssuedAt()).isNotNull(); assertThat(registeredClientResult.getClientSecret()).isNotNull(); assertThat(registeredClientResult.getClientName()).isEqualTo(clientRegistration.getClientName()); - assertThat(registeredClientResult.getClientAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + assertThat(registeredClientResult.getClientAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); assertThat(registeredClientResult.getRedirectUris()).containsExactly("https://client.example.com"); - assertThat(registeredClientResult.getPostLogoutRedirectUris()).containsExactly("https://client.example.com/oidc-post-logout"); - assertThat(registeredClientResult.getAuthorizationGrantTypes()) - .containsExactlyInAnyOrder(AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); + assertThat(registeredClientResult.getPostLogoutRedirectUris()) + .containsExactly("https://client.example.com/oidc-post-logout"); + assertThat(registeredClientResult.getAuthorizationGrantTypes()).containsExactlyInAnyOrder( + AuthorizationGrantType.AUTHORIZATION_CODE, AuthorizationGrantType.CLIENT_CREDENTIALS); assertThat(registeredClientResult.getScopes()).containsExactlyInAnyOrder("scope1", "scope2"); assertThat(registeredClientResult.getClientSettings().isRequireProofKey()).isTrue(); assertThat(registeredClientResult.getClientSettings().isRequireAuthorizationConsent()).isTrue(); - assertThat(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm()).isEqualTo(SignatureAlgorithm.RS256); + assertThat(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm()) + .isEqualTo(SignatureAlgorithm.RS256); OidcClientRegistration clientRegistrationResult = authenticationResult.getClientRegistration(); assertThat(clientRegistrationResult.getClientId()).isEqualTo(registeredClientResult.getClientId()); - assertThat(clientRegistrationResult.getClientIdIssuedAt()).isEqualTo(registeredClientResult.getClientIdIssuedAt()); + assertThat(clientRegistrationResult.getClientIdIssuedAt()) + .isEqualTo(registeredClientResult.getClientIdIssuedAt()); assertThat(clientRegistrationResult.getClientSecret()).isEqualTo(registeredClientResult.getClientSecret()); - assertThat(clientRegistrationResult.getClientSecretExpiresAt()).isEqualTo(registeredClientResult.getClientSecretExpiresAt()); + assertThat(clientRegistrationResult.getClientSecretExpiresAt()) + .isEqualTo(registeredClientResult.getClientSecretExpiresAt()); assertThat(clientRegistrationResult.getClientName()).isEqualTo(registeredClientResult.getClientName()); assertThat(clientRegistrationResult.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(registeredClientResult.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(registeredClientResult.getRedirectUris()); assertThat(clientRegistrationResult.getPostLogoutRedirectUris()) - .containsExactlyInAnyOrderElementsOf(registeredClientResult.getPostLogoutRedirectUris()); + .containsExactlyInAnyOrderElementsOf(registeredClientResult.getPostLogoutRedirectUris()); List grantTypes = new ArrayList<>(); - registeredClientResult.getAuthorizationGrantTypes().forEach(authorizationGrantType -> - grantTypes.add(authorizationGrantType.getValue())); + registeredClientResult.getAuthorizationGrantTypes() + .forEach(authorizationGrantType -> grantTypes.add(authorizationGrantType.getValue())); assertThat(clientRegistrationResult.getGrantTypes()).containsExactlyInAnyOrderElementsOf(grantTypes); assertThat(clientRegistrationResult.getResponseTypes()) - .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); + .containsExactly(OAuth2AuthorizationResponseType.CODE.getValue()); assertThat(clientRegistrationResult.getScopes()) - .containsExactlyInAnyOrderElementsOf(registeredClientResult.getScopes()); + .containsExactlyInAnyOrderElementsOf(registeredClientResult.getScopes()); assertThat(clientRegistrationResult.getTokenEndpointAuthenticationMethod()) - .isEqualTo(registeredClientResult.getClientAuthenticationMethods().iterator().next().getValue()); + .isEqualTo(registeredClientResult.getClientAuthenticationMethods().iterator().next().getValue()); assertThat(clientRegistrationResult.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); + .isEqualTo(registeredClientResult.getTokenSettings().getIdTokenSignatureAlgorithm().getName()); AuthorizationServerContext authorizationServerContext = AuthorizationServerContextHolder.getContext(); - String expectedRegistrationClientUrl = UriComponentsBuilder.fromUriString(authorizationServerContext.getIssuer()) - .path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint()) - .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClientResult.getClientId()).toUriString(); + String expectedRegistrationClientUrl = UriComponentsBuilder + .fromUriString(authorizationServerContext.getIssuer()) + .path(authorizationServerContext.getAuthorizationServerSettings().getOidcClientRegistrationEndpoint()) + .queryParam(OAuth2ParameterNames.CLIENT_ID, registeredClientResult.getClientId()) + .toUriString(); - assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()).isEqualTo(expectedRegistrationClientUrl); + assertThat(clientRegistrationResult.getRegistrationClientUrl().toString()) + .isEqualTo(expectedRegistrationClientUrl); assertThat(clientRegistrationResult.getRegistrationAccessToken()).isEqualTo(jwt.getTokenValue()); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java index 83d5dcf7..83734326 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcClientRegistrationAuthenticationTokenTests.java @@ -29,36 +29,40 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Joe Grandja */ public class OidcClientRegistrationAuthenticationTokenTests { + private TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); + private OidcClientRegistration clientRegistration = OidcClientRegistration.builder() - .redirectUri("https://client.example.com").build(); + .redirectUri("https://client.example.com") + .build(); @Test public void constructorWhenPrincipalNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(null, this.clientRegistration)) - .withMessage("principal cannot be null"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(null, this.clientRegistration)) + .withMessage("principal cannot be null"); } @Test public void constructorWhenClientRegistrationNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (OidcClientRegistration) null)) - .withMessage("clientRegistration cannot be null"); + .isThrownBy( + () -> new OidcClientRegistrationAuthenticationToken(this.principal, (OidcClientRegistration) null)) + .withMessage("clientRegistration cannot be null"); } @Test public void constructorWhenClientIdNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (String) null)) - .withMessage("clientId cannot be empty"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, (String) null)) + .withMessage("clientId cannot be empty"); } @Test public void constructorWhenClientIdEmptyThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, "")) - .withMessage("clientId cannot be empty"); + .isThrownBy(() -> new OidcClientRegistrationAuthenticationToken(this.principal, "")) + .withMessage("clientId cannot be empty"); } @Test diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java index ceb4e011..99019391 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationProviderTests.java @@ -64,11 +64,17 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OidcLogoutAuthenticationProviderTests { + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private RegisteredClientRepository registeredClientRepository; + private OAuth2AuthorizationService authorizationService; + private SessionRegistry sessionRegistry; + private AuthorizationServerSettings authorizationServerSettings; + private OidcLogoutAuthenticationProvider authenticationProvider; @BeforeEach @@ -77,11 +83,11 @@ public class OidcLogoutAuthenticationProviderTests { this.authorizationService = mock(OAuth2AuthorizationService.class); this.sessionRegistry = mock(SessionRegistry.class); this.authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); - TestAuthorizationServerContext authorizationServerContext = - new TestAuthorizationServerContext(this.authorizationServerSettings, null); + TestAuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext( + this.authorizationServerSettings, null); AuthorizationServerContextHolder.setContext(authorizationServerContext); - this.authenticationProvider = new OidcLogoutAuthenticationProvider( - this.registeredClientRepository, this.authorizationService, this.sessionRegistry); + this.authenticationProvider = new OidcLogoutAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, this.sessionRegistry); } @AfterEach @@ -92,22 +98,24 @@ public class OidcLogoutAuthenticationProviderTests { @Test public void constructorWhenRegisteredClientRepositoryNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationProvider(null, this.authorizationService, this.sessionRegistry)) - .withMessage("registeredClientRepository cannot be null"); + .isThrownBy( + () -> new OidcLogoutAuthenticationProvider(null, this.authorizationService, this.sessionRegistry)) + .withMessage("registeredClientRepository cannot be null"); } @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository, null, this.sessionRegistry)) - .withMessage("authorizationService cannot be null"); + assertThatIllegalArgumentException().isThrownBy( + () -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository, null, this.sessionRegistry)) + .withMessage("authorizationService cannot be null"); } @Test public void constructorWhenSessionRegistryNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository, this.authorizationService, null)) - .withMessage("sessionRegistry cannot be null"); + .isThrownBy(() -> new OidcLogoutAuthenticationProvider(this.registeredClientRepository, + this.authorizationService, null)) + .withMessage("sessionRegistry cannot be null"); } @Test @@ -119,240 +127,227 @@ public class OidcLogoutAuthenticationProviderTests { public void authenticateWhenIdTokenNotFoundThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - "id-token", principal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken("id-token", principal, + "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("id_token_hint"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("id_token_hint"); + }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); } @Test public void authenticateWhenIdTokenInvalidatedThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, (metadata) -> { - metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()); - metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true); - }) - .build(); + .principalName(principal.getName()) + .token(idToken, (metadata) -> { + metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims()); + metadata.put(OAuth2Authorization.Token.INVALIDATED_METADATA_NAME, true); + }) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("id_token_hint"); - }); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("id_token_hint"); + }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); } @Test public void authenticateWhenMissingAudienceThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains(IdTokenClaimNames.AUD); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains(IdTokenClaimNames.AUD); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenInvalidAudienceThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId() + "-invalid")) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId() + "-invalid")) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains(IdTokenClaimNames.AUD); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains(IdTokenClaimNames.AUD); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenInvalidClientIdThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", registeredClient.getClientId() + "-invalid", null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", registeredClient.getClientId() + "-invalid", null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); - assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThat(error.getDescription()).contains(OAuth2ParameterNames.CLIENT_ID); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenInvalidPostLogoutRedirectUriThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", registeredClient.getClientId(), - "https://example.com/callback-1-invalid", null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", registeredClient.getClientId(), "https://example.com/callback-1-invalid", null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); - assertThat(error.getDescription()).contains("post_logout_redirect_uri"); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThat(error.getDescription()).contains("post_logout_redirect_uri"); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenMissingSubThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); principal.setAuthenticated(true); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("sub"); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("sub"); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } // gh-1235 @@ -360,133 +355,125 @@ public class OidcLogoutAuthenticationProviderTests { public void authenticateWhenInvalidPrincipalThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); principal.setAuthenticated(true); TestingAuthenticationToken otherPrincipal = new TestingAuthenticationToken("other-principal", "credentials"); otherPrincipal.setAuthenticated(true); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), otherPrincipal, "session-1", null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + otherPrincipal, "session-1", null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("sub"); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("sub"); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenMissingSidThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String sessionId = "session-1"; - List sessions = Collections.singletonList( - new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now()))); - when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))) - .thenReturn(sessions); + List sessions = Collections + .singletonList(new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now()))); + when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).thenReturn(sessions); principal.setAuthenticated(true); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, sessionId, null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, sessionId, null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("sid"); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("sid"); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test public void authenticateWhenInvalidSidThenThrowOAuth2AuthenticationException() { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .claim("sid", "other-session") - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .claim("sid", "other-session") + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); String sessionId = "session-1"; - List sessions = Collections.singletonList( - new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now()))); - when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))) - .thenReturn(sessions); + List sessions = Collections + .singletonList(new SessionInformation(principal.getPrincipal(), sessionId, Date.from(Instant.now()))); + when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).thenReturn(sessions); principal.setAuthenticated(true); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, sessionId, null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, sessionId, null, null, null); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); - assertThat(error.getDescription()).contains("sid"); - }); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + assertThat(error.getDescription()).contains("sid"); + }); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); } @Test @@ -494,14 +481,14 @@ public class OidcLogoutAuthenticationProviderTests { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); String sessionId = "session-1"; - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .claim("sid", createHash(sessionId)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().plusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .claim("sid", createHash(sessionId)) + .build(); authenticateValidIdToken(principal, registeredClient, sessionId, idToken); } @@ -511,49 +498,46 @@ public class OidcLogoutAuthenticationProviderTests { TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); String sessionId = "session-1"; - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject(principal.getName()) - .audience(Collections.singleton(registeredClient.getClientId())) - .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) - .expiresAt(Instant.now().minusSeconds(30).truncatedTo(ChronoUnit.MILLIS)) // Expired - .claim("sid", createHash(sessionId)) - .build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject(principal.getName()) + .audience(Collections.singleton(registeredClient.getClientId())) + .issuedAt(Instant.now().minusSeconds(60).truncatedTo(ChronoUnit.MILLIS)) + .expiresAt(Instant.now().minusSeconds(30).truncatedTo(ChronoUnit.MILLIS)) // Expired + .claim("sid", createHash(sessionId)) + .build(); authenticateValidIdToken(principal, registeredClient, sessionId, idToken); } - private void authenticateValidIdToken(Authentication principal, RegisteredClient registeredClient, - String sessionId, OidcIdToken idToken) { + private void authenticateValidIdToken(Authentication principal, RegisteredClient registeredClient, String sessionId, + OidcIdToken idToken) { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .principalName(principal.getName()) - .token(idToken, - (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) - .build(); + .principalName(principal.getName()) + .token(idToken, + (metadata) -> metadata.put(OAuth2Authorization.Token.CLAIMS_METADATA_NAME, idToken.getClaims())) + .build(); when(this.authorizationService.findByToken(eq(idToken.getTokenValue()), eq(ID_TOKEN_TOKEN_TYPE))) - .thenReturn(authorization); + .thenReturn(authorization); when(this.registeredClientRepository.findById(eq(authorization.getRegisteredClientId()))) - .thenReturn(registeredClient); + .thenReturn(registeredClient); - SessionInformation sessionInformation = new SessionInformation( - principal.getPrincipal(), sessionId, Date.from(Instant.now())); + SessionInformation sessionInformation = new SessionInformation(principal.getPrincipal(), sessionId, + Date.from(Instant.now())); List sessions = Collections.singletonList(sessionInformation); - when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))) - .thenReturn(sessions); + when(this.sessionRegistry.getAllSessions(eq(principal.getPrincipal()), eq(true))).thenReturn(sessions); principal.setAuthenticated(true); String postLogoutRedirectUri = registeredClient.getPostLogoutRedirectUris().toArray(new String[0])[0]; String state = "state"; - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - idToken.getTokenValue(), principal, sessionId, registeredClient.getClientId(), postLogoutRedirectUri, state); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(idToken.getTokenValue(), + principal, sessionId, registeredClient.getClientId(), postLogoutRedirectUri, state); - OidcLogoutAuthenticationToken authenticationResult = - (OidcLogoutAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OidcLogoutAuthenticationToken authenticationResult = (OidcLogoutAuthenticationToken) this.authenticationProvider + .authenticate(authentication); - verify(this.authorizationService).findByToken( - eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); - verify(this.registeredClientRepository).findById( - eq(authorization.getRegisteredClientId())); + verify(this.authorizationService).findByToken(eq(authentication.getIdTokenHint()), eq(ID_TOKEN_TOKEN_TYPE)); + verify(this.registeredClientRepository).findById(eq(authorization.getRegisteredClientId())); assertThat(authenticationResult.getPrincipal()).isEqualTo(principal); assertThat(authenticationResult.getCredentials().toString()).isEmpty(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationTokenTests.java index 3a336733..9b4a874b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcLogoutAuthenticationTokenTests.java @@ -31,55 +31,62 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Joe Grandja */ public class OidcLogoutAuthenticationTokenTests { + private final String idTokenHint = "id-token"; + private final OidcIdToken idToken = OidcIdToken.withTokenValue(this.idTokenHint) - .issuer("https://provider.com") - .subject("principal") - .issuedAt(Instant.now().minusSeconds(60)) - .expiresAt(Instant.now().plusSeconds(60)) - .build(); + .issuer("https://provider.com") + .subject("principal") + .issuedAt(Instant.now().minusSeconds(60)) + .expiresAt(Instant.now().plusSeconds(60)) + .build(); + private final TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "credentials"); + private final String sessionId = "session-1"; + private final String clientId = "client-1"; + private final String postLogoutRedirectUri = "https://example.com/oidc-post-logout"; + private final String state = "state-1"; @Test public void constructorWhenIdTokenHintEmptyThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationToken( - "", this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state)) - .withMessage("idTokenHint cannot be empty"); + .isThrownBy(() -> new OidcLogoutAuthenticationToken("", this.principal, this.sessionId, this.clientId, + this.postLogoutRedirectUri, this.state)) + .withMessage("idTokenHint cannot be empty"); assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationToken( - (String) null, this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state)) - .withMessage("idTokenHint cannot be empty"); + .isThrownBy(() -> new OidcLogoutAuthenticationToken((String) null, this.principal, this.sessionId, + this.clientId, this.postLogoutRedirectUri, this.state)) + .withMessage("idTokenHint cannot be empty"); } @Test public void constructorWhenIdTokenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationToken( - (OidcIdToken) null, this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state)) - .withMessage("idToken cannot be null"); + .isThrownBy(() -> new OidcLogoutAuthenticationToken((OidcIdToken) null, this.principal, this.sessionId, + this.clientId, this.postLogoutRedirectUri, this.state)) + .withMessage("idToken cannot be null"); } @Test public void constructorWhenPrincipalNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationToken( - this.idTokenHint, null, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state)) - .withMessage("principal cannot be null"); + .isThrownBy(() -> new OidcLogoutAuthenticationToken(this.idTokenHint, null, this.sessionId, this.clientId, + this.postLogoutRedirectUri, this.state)) + .withMessage("principal cannot be null"); assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcLogoutAuthenticationToken( - this.idToken, null, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state)) - .withMessage("principal cannot be null"); + .isThrownBy(() -> new OidcLogoutAuthenticationToken(this.idToken, null, this.sessionId, this.clientId, + this.postLogoutRedirectUri, this.state)) + .withMessage("principal cannot be null"); } @Test public void constructorWhenIdTokenHintProvidedThenCreated() { - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - this.idTokenHint, this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(this.idTokenHint, + this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state); assertThat(authentication.getPrincipal()).isEqualTo(this.principal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getIdTokenHint()).isEqualTo(this.idTokenHint); @@ -93,8 +100,8 @@ public class OidcLogoutAuthenticationTokenTests { @Test public void constructorWhenIdTokenProvidedThenCreated() { - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - this.idToken, this.principal, this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken(this.idToken, this.principal, + this.sessionId, this.clientId, this.postLogoutRedirectUri, this.state); assertThat(authentication.getPrincipal()).isEqualTo(this.principal); assertThat(authentication.getCredentials().toString()).isEmpty(); assertThat(authentication.getIdTokenHint()).isEqualTo(this.idToken.getTokenValue()); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProviderTests.java index 4ffd42b4..2cc36973 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProviderTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationProviderTests.java @@ -56,7 +56,9 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OidcUserInfoAuthenticationProviderTests { + private OAuth2AuthorizationService authorizationService; + private OidcUserInfoAuthenticationProvider authenticationProvider; @BeforeEach @@ -67,16 +69,14 @@ public class OidcUserInfoAuthenticationProviderTests { @Test public void constructorWhenAuthorizationServiceNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcUserInfoAuthenticationProvider(null)) - .withMessage("authorizationService cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoAuthenticationProvider(null)) + .withMessage("authorizationService cannot be null"); } @Test public void setUserInfoMapperWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.authenticationProvider.setUserInfoMapper(null)) - .withMessage("userInfoMapper cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.authenticationProvider.setUserInfoMapper(null)) + .withMessage("userInfoMapper cannot be null"); } @Test @@ -90,10 +90,10 @@ public class OidcUserInfoAuthenticationProviderTests { new UsernamePasswordAuthenticationToken(null, null)); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); verifyNoInteractions(this.authorizationService); } @@ -106,10 +106,10 @@ public class OidcUserInfoAuthenticationProviderTests { OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); verifyNoInteractions(this.authorizationService); } @@ -121,10 +121,10 @@ public class OidcUserInfoAuthenticationProviderTests { OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); verify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)); } @@ -136,16 +136,16 @@ public class OidcUserInfoAuthenticationProviderTests { authorization = OidcAuthenticationProviderUtils.invalidate(authorization, authorization.getAccessToken().getToken()); when(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); JwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue); OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); verify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)); } @@ -154,16 +154,16 @@ public class OidcUserInfoAuthenticationProviderTests { public void authenticateWhenAccessTokenNotAuthorizedThenThrowOAuth2AuthenticationException() { String tokenValue = "token"; when(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(TestOAuth2Authorizations.authorization().build()); + .thenReturn(TestOAuth2Authorizations.authorization().build()); JwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue); OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INSUFFICIENT_SCOPE); verify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)); } @@ -172,19 +172,19 @@ public class OidcUserInfoAuthenticationProviderTests { public void authenticateWhenIdTokenNullThenThrowOAuth2AuthenticationException() { String tokenValue = "token"; OAuth2Authorization authorization = TestOAuth2Authorizations.authorization() - .token(createAuthorization(tokenValue).getAccessToken().getToken()) - .build(); + .token(createAuthorization(tokenValue).getAccessToken().getToken()) + .build(); when(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(authorization); + .thenReturn(authorization); JwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue); OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); assertThatThrownBy(() -> this.authenticationProvider.authenticate(authentication)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); + .isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_TOKEN); verify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)); } @@ -193,12 +193,12 @@ public class OidcUserInfoAuthenticationProviderTests { public void authenticateWhenValidAccessTokenThenReturnUserInfo() { String tokenValue = "access-token"; when(this.authorizationService.findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN))) - .thenReturn(createAuthorization(tokenValue)); + .thenReturn(createAuthorization(tokenValue)); JwtAuthenticationToken principal = createJwtAuthenticationToken(tokenValue); OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal); - OidcUserInfoAuthenticationToken authenticationResult = - (OidcUserInfoAuthenticationToken) this.authenticationProvider.authenticate(authentication); + OidcUserInfoAuthenticationToken authenticationResult = (OidcUserInfoAuthenticationToken) this.authenticationProvider + .authenticate(authentication); assertThat(authenticationResult.getPrincipal()).isEqualTo(principal); assertThat(authenticationResult.getCredentials()).isEqualTo(""); @@ -225,7 +225,7 @@ public class OidcUserInfoAuthenticationProviderTests { assertThat(userInfo.getPhoneNumber()).isEqualTo("+1 (604) 555-1234;ext=5678"); assertThat(userInfo.getPhoneNumberVerified()).isEqualTo(false); assertThat(userInfo.getAddress().getFormatted()) - .isEqualTo("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance"); + .isEqualTo("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance"); assertThat(userInfo.getUpdatedAt()).isEqualTo(Instant.parse("1970-01-01T00:00:00Z")); verify(this.authorizationService).findByToken(eq(tokenValue), eq(OAuth2TokenType.ACCESS_TOKEN)); @@ -233,16 +233,13 @@ public class OidcUserInfoAuthenticationProviderTests { private static OAuth2Authorization createAuthorization(String tokenValue) { Instant now = Instant.now(); - Set scopes = new HashSet<>(Arrays.asList( - OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL, OidcScopes.PHONE, OidcScopes.PROFILE)); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, tokenValue, now, now.plusSeconds(300), scopes); + Set scopes = new HashSet<>(Arrays.asList(OidcScopes.OPENID, OidcScopes.ADDRESS, OidcScopes.EMAIL, + OidcScopes.PHONE, OidcScopes.PROFILE)); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, tokenValue, now, + now.plusSeconds(300), scopes); OidcIdToken idToken = new OidcIdToken("id-token", now, now.plusSeconds(900), createUserInfo().getClaims()); - return TestOAuth2Authorizations.authorization() - .token(accessToken) - .token(idToken) - .build(); + return TestOAuth2Authorizations.authorization().token(accessToken).token(idToken).build(); } private static JwtAuthenticationToken createJwtAuthenticationToken(String tokenValue) { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationTokenTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationTokenTests.java index f934394f..49756b3e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationTokenTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/authentication/OidcUserInfoAuthenticationTokenTests.java @@ -35,9 +35,8 @@ public class OidcUserInfoAuthenticationTokenTests { @Test public void constructorWhenPrincipalNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcUserInfoAuthenticationToken(null)) - .withMessage("principal cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoAuthenticationToken(null)) + .withMessage("principal cannot be null"); } @Test @@ -58,4 +57,5 @@ public class OidcUserInfoAuthenticationTokenTests { assertThat(authentication.getUserInfo()).isEqualTo(userInfo); assertThat(authentication.isAuthenticated()).isTrue(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java index 3799ca84..24821401 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcClientRegistrationHttpMessageConverterTests.java @@ -41,12 +41,13 @@ import static org.assertj.core.api.Assertions.assertThatThrownBy; /** * Tests for {@link OidcClientRegistrationHttpMessageConverter} - + * * @author Ovidiu Popa * @author Joe Grandja * @since 0.1.1 */ public class OidcClientRegistrationHttpMessageConverterTests { + private final OidcClientRegistrationHttpMessageConverter messageConverter = new OidcClientRegistrationHttpMessageConverter(); @Test @@ -57,15 +58,15 @@ public class OidcClientRegistrationHttpMessageConverterTests { @Test public void setClientRegistrationConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.messageConverter.setClientRegistrationConverter(null)) - .withMessageContaining("clientRegistrationConverter cannot be null"); + .isThrownBy(() -> this.messageConverter.setClientRegistrationConverter(null)) + .withMessageContaining("clientRegistrationConverter cannot be null"); } @Test public void setClientRegistrationParametersConverterWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> this.messageConverter.setClientRegistrationParametersConverter(null)) - .withMessageContaining("clientRegistrationParametersConverter cannot be null"); + .isThrownBy(() -> this.messageConverter.setClientRegistrationParametersConverter(null)) + .withMessageContaining("clientRegistrationParametersConverter cannot be null"); } @Test @@ -78,10 +79,10 @@ public class OidcClientRegistrationHttpMessageConverterTests { + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse( - clientRegistrationRequest.getBytes(), HttpStatus.OK); - OidcClientRegistration clientRegistration = this.messageConverter - .readInternal(OidcClientRegistration.class, response); + MockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(), + HttpStatus.OK); + OidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class, + response); assertThat(clientRegistration.getClaims()).hasSize(1); assertThat(clientRegistration.getRedirectUris()).containsOnly("https://client.example.com"); @@ -117,10 +118,10 @@ public class OidcClientRegistrationHttpMessageConverterTests { +" \"a-claim\": \"a-value\"\n" +"}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse( - clientRegistrationRequest.getBytes(), HttpStatus.OK); - OidcClientRegistration clientRegistration = this.messageConverter - .readInternal(OidcClientRegistration.class, response); + MockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(), + HttpStatus.OK); + OidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class, + response); assertThat(clientRegistration.getClientId()).isEqualTo("client-id"); assertThat(clientRegistration.getClientIdIssuedAt()).isEqualTo(Instant.ofEpochSecond(1607633867L)); @@ -128,10 +129,14 @@ public class OidcClientRegistrationHttpMessageConverterTests { assertThat(clientRegistration.getClientSecretExpiresAt()).isEqualTo(Instant.ofEpochSecond(1607637467L)); assertThat(clientRegistration.getClientName()).isEqualTo("client-name"); assertThat(clientRegistration.getRedirectUris()).containsOnly("https://client.example.com"); - assertThat(clientRegistration.getPostLogoutRedirectUris()).containsOnly("https://client.example.com/oidc-post-logout"); - assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); - assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()).isEqualTo(MacAlgorithm.HS256.getName()); - assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); + assertThat(clientRegistration.getPostLogoutRedirectUris()) + .containsOnly("https://client.example.com/oidc-post-logout"); + assertThat(clientRegistration.getTokenEndpointAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_JWT.getValue()); + assertThat(clientRegistration.getTokenEndpointAuthenticationSigningAlgorithm()) + .isEqualTo(MacAlgorithm.HS256.getName()); + assertThat(clientRegistration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); assertThat(clientRegistration.getResponseTypes()).containsOnly("code"); assertThat(clientRegistration.getScopes()).containsExactlyInAnyOrder("scope1", "scope2"); assertThat(clientRegistration.getJwkSetUrl()).isEqualTo(new URL("https://client.example.com/jwks")); @@ -151,10 +156,10 @@ public class OidcClientRegistrationHttpMessageConverterTests { + " ]\n" +"}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse( - clientRegistrationRequest.getBytes(), HttpStatus.OK); - OidcClientRegistration clientRegistration = this.messageConverter - .readInternal(OidcClientRegistration.class, response); + MockClientHttpResponse response = new MockClientHttpResponse(clientRegistrationRequest.getBytes(), + HttpStatus.OK); + OidcClientRegistration clientRegistration = this.messageConverter.readInternal(OidcClientRegistration.class, + response); assertThat(clientRegistration.getClaims()).hasSize(3); assertThat(clientRegistration.getClientId()).isEqualTo("client-id"); @@ -172,9 +177,9 @@ public class OidcClientRegistrationHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse("{}".getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OidcClientRegistration.class, response)) - .withMessageContaining("An error occurred reading the OpenID Client Registration") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.readInternal(OidcClientRegistration.class, response)) + .withMessageContaining("An error occurred reading the OpenID Client Registration") + .withMessageContaining(errorMessage); } @Test @@ -213,16 +218,19 @@ public class OidcClientRegistrationHttpMessageConverterTests { assertThat(clientRegistrationResponse).contains("\"client_secret_expires_at\":1607637467"); assertThat(clientRegistrationResponse).contains("\"client_name\":\"client-name\""); assertThat(clientRegistrationResponse).contains("\"redirect_uris\":[\"https://client.example.com\"]"); - assertThat(clientRegistrationResponse).contains("\"post_logout_redirect_uris\":[\"https://client.example.com/oidc-post-logout\"]"); + assertThat(clientRegistrationResponse) + .contains("\"post_logout_redirect_uris\":[\"https://client.example.com/oidc-post-logout\"]"); assertThat(clientRegistrationResponse).contains("\"token_endpoint_auth_method\":\"client_secret_jwt\""); assertThat(clientRegistrationResponse).contains("\"token_endpoint_auth_signing_alg\":\"HS256\""); - assertThat(clientRegistrationResponse).contains("\"grant_types\":[\"authorization_code\",\"client_credentials\"]"); + assertThat(clientRegistrationResponse) + .contains("\"grant_types\":[\"authorization_code\",\"client_credentials\"]"); assertThat(clientRegistrationResponse).contains("\"response_types\":[\"code\"]"); assertThat(clientRegistrationResponse).contains("\"scope\":\"scope1 scope2\""); assertThat(clientRegistrationResponse).contains("\"jwks_uri\":\"https://client.example.com/jwks\""); assertThat(clientRegistrationResponse).contains("\"id_token_signed_response_alg\":\"RS256\""); assertThat(clientRegistrationResponse).contains("\"registration_access_token\":\"registration-access-token\""); - assertThat(clientRegistrationResponse).contains("\"registration_client_uri\":\"https://auth-server.com/connect/register?client_id=1\""); + assertThat(clientRegistrationResponse) + .contains("\"registration_client_uri\":\"https://auth-server.com/connect/register?client_id=1\""); assertThat(clientRegistrationResponse).contains("\"a-claim\":\"a-value\""); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java index f3658f52..c5cfc17a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcProviderConfigurationHttpMessageConverterTests.java @@ -40,6 +40,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Daniel Garnier-Moiroux */ public class OidcProviderConfigurationHttpMessageConverterTests { + private final OidcProviderConfigurationHttpMessageConverter messageConverter = new OidcProviderConfigurationHttpMessageConverter(); @Test @@ -49,12 +50,14 @@ public class OidcProviderConfigurationHttpMessageConverterTests { @Test public void setProviderConfigurationParametersConverterWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setProviderConfigurationParametersConverter(null)); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.messageConverter.setProviderConfigurationParametersConverter(null)); } @Test public void setProviderConfigurationConverterWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setProviderConfigurationConverter(null)); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.messageConverter.setProviderConfigurationConverter(null)); } @Test @@ -70,12 +73,14 @@ public class OidcProviderConfigurationHttpMessageConverterTests { + " \"id_token_signing_alg_values_supported\": [\"RS256\"]\n" + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), + HttpStatus.OK); OidcProviderConfiguration providerConfiguration = this.messageConverter - .readInternal(OidcProviderConfiguration.class, response); + .readInternal(OidcProviderConfiguration.class, response); assertThat(providerConfiguration.getIssuer()).isEqualTo(new URL("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(new URL("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(new URL("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(new URL("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getResponseTypes()).containsExactly("code"); @@ -105,23 +110,28 @@ public class OidcProviderConfigurationHttpMessageConverterTests { + " \"custom_collection_claim\": [\"value1\", \"value2\"]\n" + "}\n"; // @formatter:on - MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), + HttpStatus.OK); OidcProviderConfiguration providerConfiguration = this.messageConverter - .readInternal(OidcProviderConfiguration.class, response); + .readInternal(OidcProviderConfiguration.class, response); assertThat(providerConfiguration.getIssuer()).isEqualTo(new URL("https://example.com")); - assertThat(providerConfiguration.getAuthorizationEndpoint()).isEqualTo(new URL("https://example.com/oauth2/authorize")); + assertThat(providerConfiguration.getAuthorizationEndpoint()) + .isEqualTo(new URL("https://example.com/oauth2/authorize")); assertThat(providerConfiguration.getTokenEndpoint()).isEqualTo(new URL("https://example.com/oauth2/token")); assertThat(providerConfiguration.getJwkSetUrl()).isEqualTo(new URL("https://example.com/oauth2/jwks")); assertThat(providerConfiguration.getUserInfoEndpoint()).isEqualTo(new URL("https://example.com/userinfo")); assertThat(providerConfiguration.getScopes()).containsExactly("openid"); assertThat(providerConfiguration.getResponseTypes()).containsExactly("code"); - assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", "client_credentials"); + assertThat(providerConfiguration.getGrantTypes()).containsExactlyInAnyOrder("authorization_code", + "client_credentials"); assertThat(providerConfiguration.getSubjectTypes()).containsExactly("public"); assertThat(providerConfiguration.getIdTokenSigningAlgorithms()).containsExactly("RS256"); - assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()).containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); + assertThat(providerConfiguration.getTokenEndpointAuthenticationMethods()) + .containsExactly(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()); assertThat(providerConfiguration.getClaim("custom_claim")).isEqualTo("value"); - assertThat(providerConfiguration.getClaimAsStringList("custom_collection_claim")).containsExactlyInAnyOrder("value1", "value2"); + assertThat(providerConfiguration.getClaimAsStringList("custom_collection_claim")) + .containsExactlyInAnyOrder("value1", "value2"); } @Test @@ -133,57 +143,60 @@ public class OidcProviderConfigurationHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse("{}".getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response)) - .withMessageContaining("An error occurred reading the OpenID Provider Configuration") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response)) + .withMessageContaining("An error occurred reading the OpenID Provider Configuration") + .withMessageContaining(errorMessage); } @Test public void readInternalWhenInvalidProviderConfigurationThenThrowException() { String providerConfigurationResponse = "{ \"issuer\": null }"; - MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), HttpStatus.OK); + MockClientHttpResponse response = new MockClientHttpResponse(providerConfigurationResponse.getBytes(), + HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response)) - .withMessageContaining("An error occurred reading the OpenID Provider Configuration") - .withMessageContaining("issuer cannot be null"); + .isThrownBy(() -> this.messageConverter.readInternal(OidcProviderConfiguration.class, response)) + .withMessageContaining("An error occurred reading the OpenID Provider Configuration") + .withMessageContaining("issuer cannot be null"); } @Test public void writeInternalWhenProviderConfigurationThenSuccess() { - OidcProviderConfiguration providerConfiguration = - OidcProviderConfiguration.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .jwkSetUrl("https://example.com/oauth2/jwks") - .userInfoEndpoint("https://example.com/userinfo") - .scope("openid") - .responseType("code") - .grantType("authorization_code") - .grantType("client_credentials") - .subjectType("public") - .idTokenSigningAlgorithm("RS256") - .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) - .claim("custom_claim", "value") - .claim("custom_collection_claim", Arrays.asList("value1", "value2")) - .build(); + OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder() + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .jwkSetUrl("https://example.com/oauth2/jwks") + .userInfoEndpoint("https://example.com/userinfo") + .scope("openid") + .responseType("code") + .grantType("authorization_code") + .grantType("client_credentials") + .subjectType("public") + .idTokenSigningAlgorithm("RS256") + .tokenEndpointAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_BASIC.getValue()) + .claim("custom_claim", "value") + .claim("custom_collection_claim", Arrays.asList("value1", "value2")) + .build(); MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); this.messageConverter.writeInternal(providerConfiguration, outputMessage); String providerConfigurationResponse = outputMessage.getBodyAsString(); assertThat(providerConfigurationResponse).contains("\"issuer\":\"https://example.com\""); - assertThat(providerConfigurationResponse).contains("\"authorization_endpoint\":\"https://example.com/oauth2/authorize\""); + assertThat(providerConfigurationResponse) + .contains("\"authorization_endpoint\":\"https://example.com/oauth2/authorize\""); assertThat(providerConfigurationResponse).contains("\"token_endpoint\":\"https://example.com/oauth2/token\""); assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/jwks\""); assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/userinfo\""); 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\"]"); assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]"); assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]"); - assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_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\"]"); } @@ -191,28 +204,27 @@ public class OidcProviderConfigurationHttpMessageConverterTests { @Test public void writeInternalWhenWriteFailsThenThrowsException() { String errorMessage = "this is not a valid converter"; - Converter> failingConverter = - source -> { - throw new RuntimeException(errorMessage); - }; + Converter> failingConverter = source -> { + throw new RuntimeException(errorMessage); + }; this.messageConverter.setProviderConfigurationParametersConverter(failingConverter); - OidcProviderConfiguration providerConfiguration = - OidcProviderConfiguration.builder() - .issuer("https://example.com") - .authorizationEndpoint("https://example.com/oauth2/authorize") - .tokenEndpoint("https://example.com/oauth2/token") - .jwkSetUrl("https://example.com/oauth2/jwks") - .responseType("code") - .subjectType("public") - .idTokenSigningAlgorithm("RS256") - .build(); + OidcProviderConfiguration providerConfiguration = OidcProviderConfiguration.builder() + .issuer("https://example.com") + .authorizationEndpoint("https://example.com/oauth2/authorize") + .tokenEndpoint("https://example.com/oauth2/token") + .jwkSetUrl("https://example.com/oauth2/jwks") + .responseType("code") + .subjectType("public") + .idTokenSigningAlgorithm("RS256") + .build(); MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); assertThatExceptionOfType(HttpMessageNotWritableException.class) - .isThrownBy(() -> this.messageConverter.writeInternal(providerConfiguration, outputMessage)) - .withMessageContaining("An error occurred writing the OpenID Provider Configuration") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.writeInternal(providerConfiguration, outputMessage)) + .withMessageContaining("An error occurred writing the OpenID Provider Configuration") + .withMessageContaining(errorMessage); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java index e9afbb11..b77bcf7d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/http/converter/OidcUserInfoHttpMessageConverterTests.java @@ -41,6 +41,7 @@ import static org.assertj.core.api.Assertions.assertThatIllegalArgumentException * @author Steve Riesenberg */ public class OidcUserInfoHttpMessageConverterTests { + private final OidcUserInfoHttpMessageConverter messageConverter = new OidcUserInfoHttpMessageConverter(); @Test @@ -55,7 +56,8 @@ public class OidcUserInfoHttpMessageConverterTests { @Test public void setUserInfoParametersConverterWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException().isThrownBy(() -> this.messageConverter.setUserInfoParametersConverter(null)); + assertThatIllegalArgumentException() + .isThrownBy(() -> this.messageConverter.setUserInfoParametersConverter(null)); } @Test @@ -112,7 +114,8 @@ public class OidcUserInfoHttpMessageConverterTests { assertThat(oidcUserInfo.getLocale()).isEqualTo("en-US"); assertThat(oidcUserInfo.getPhoneNumber()).isEqualTo("+1 (604) 555-1234;ext=5678"); assertThat(oidcUserInfo.getPhoneNumberVerified()).isFalse(); - assertThat(oidcUserInfo.getAddress().getFormatted()).isEqualTo("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance"); + assertThat(oidcUserInfo.getAddress().getFormatted()) + .isEqualTo("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance"); assertThat(oidcUserInfo.getAddress().getStreetAddress()).isEqualTo("Champ de Mars\n5 Av. Anatole France"); assertThat(oidcUserInfo.getAddress().getLocality()).isEqualTo("Paris"); assertThat(oidcUserInfo.getAddress().getPostalCode()).isEqualTo("75007"); @@ -129,9 +132,9 @@ public class OidcUserInfoHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse("{}".getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response)) - .withMessageContaining("An error occurred reading the UserInfo response") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response)) + .withMessageContaining("An error occurred reading the UserInfo response") + .withMessageContaining(errorMessage); } @Test @@ -140,9 +143,9 @@ public class OidcUserInfoHttpMessageConverterTests { MockClientHttpResponse response = new MockClientHttpResponse(userInfoResponse.getBytes(), HttpStatus.OK); assertThatExceptionOfType(HttpMessageNotReadableException.class) - .isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response)) - .withMessageContaining("An error occurred reading the UserInfo response") - .withMessageContaining("claims cannot be empty"); + .isThrownBy(() -> this.messageConverter.readInternal(OidcUserInfo.class, response)) + .withMessageContaining("An error occurred reading the UserInfo response") + .withMessageContaining("claims cannot be empty"); } @Test @@ -172,7 +175,8 @@ public class OidcUserInfoHttpMessageConverterTests { assertThat(userInfoResponse).contains("\"phone_number\":\"+1 (604) 555-1234;ext=5678\""); assertThat(userInfoResponse).contains("\"phone_number_verified\":false"); assertThat(userInfoResponse).contains("\"address\":"); - assertThat(userInfoResponse).contains("\"formatted\":\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\""); + assertThat(userInfoResponse) + .contains("\"formatted\":\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\""); assertThat(userInfoResponse).contains("\"updated_at\":1607633867"); assertThat(userInfoResponse).contains("\"custom_claim\":\"value\""); assertThat(userInfoResponse).contains("\"custom_collection_claim\":[\"value1\",\"value2\"]"); @@ -190,35 +194,37 @@ public class OidcUserInfoHttpMessageConverterTests { MockHttpOutputMessage outputMessage = new MockHttpOutputMessage(); assertThatExceptionOfType(HttpMessageNotWritableException.class) - .isThrownBy(() -> this.messageConverter.writeInternal(userInfo, outputMessage)) - .withMessageContaining("An error occurred writing the UserInfo response") - .withMessageContaining(errorMessage); + .isThrownBy(() -> this.messageConverter.writeInternal(userInfo, outputMessage)) + .withMessageContaining("An error occurred writing the UserInfo response") + .withMessageContaining(errorMessage); } private static OidcUserInfo createUserInfo() { return OidcUserInfo.builder() - .subject("user1") - .name("First Last") - .givenName("First") - .familyName("Last") - .middleName("Middle") - .nickname("User") - .preferredUsername("user") - .profile("https://example.com/user1") - .picture("https://example.com/user1.jpg") - .website("https://example.com") - .email("user1@example.com") - .emailVerified(true) - .gender("female") - .birthdate("1970-01-01") - .zoneinfo("Europe/Paris") - .locale("en-US") - .phoneNumber("+1 (604) 555-1234;ext=5678") - .claim("phone_number_verified", false) - .claim("address", Collections.singletonMap("formatted", "Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance")) - .claim(StandardClaimNames.UPDATED_AT, Instant.ofEpochSecond(1607633867)) - .claim("custom_claim", "value") - .claim("custom_collection_claim", Arrays.asList("value1", "value2")) - .build(); + .subject("user1") + .name("First Last") + .givenName("First") + .familyName("Last") + .middleName("Middle") + .nickname("User") + .preferredUsername("user") + .profile("https://example.com/user1") + .picture("https://example.com/user1.jpg") + .website("https://example.com") + .email("user1@example.com") + .emailVerified(true) + .gender("female") + .birthdate("1970-01-01") + .zoneinfo("Europe/Paris") + .locale("en-US") + .phoneNumber("+1 (604) 555-1234;ext=5678") + .claim("phone_number_verified", false) + .claim("address", + Collections.singletonMap("formatted", "Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance")) + .claim(StandardClaimNames.UPDATED_AT, Instant.ofEpochSecond(1607633867)) + .claim("custom_claim", "value") + .claim("custom_collection_claim", Arrays.asList("value1", "value2")) + .build(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java index 51208b45..95b43404 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcClientRegistrationEndpointFilterTests.java @@ -80,13 +80,16 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class OidcClientRegistrationEndpointFilterTests { + private static final String DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI = "/connect/register"; + private AuthenticationManager authenticationManager; + private OidcClientRegistrationEndpointFilter filter; - private final HttpMessageConverter clientRegistrationHttpMessageConverter = - new OidcClientRegistrationHttpMessageConverter(); - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter clientRegistrationHttpMessageConverter = new OidcClientRegistrationHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach public void setup() { @@ -101,37 +104,33 @@ public class OidcClientRegistrationEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationEndpointFilter(null)) - .withMessage("authenticationManager cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> new OidcClientRegistrationEndpointFilter(null)) + .withMessage("authenticationManager cannot be null"); } @Test public void constructorWhenClientRegistrationEndpointUriNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcClientRegistrationEndpointFilter(this.authenticationManager, null)) - .withMessage("clientRegistrationEndpointUri cannot be empty"); + .isThrownBy(() -> new OidcClientRegistrationEndpointFilter(this.authenticationManager, null)) + .withMessage("clientRegistrationEndpointUri cannot be empty"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .withMessage("authenticationConverter cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null)) + .withMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .withMessage("authenticationSuccessHandler cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) + .withMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .withMessage("authenticationFailureHandler cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) + .withMessage("authenticationFailureHandler cannot be null"); } @Test @@ -181,28 +180,26 @@ public class OidcClientRegistrationEndpointFilterTests { @Test public void doFilterWhenClientRegistrationRequestInvalidTokenThenUnauthorizedError() throws Exception { - doFilterWhenClientRegistrationRequestInvalidThenError( - OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED); + doFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED); } @Test public void doFilterWhenClientRegistrationRequestInsufficientTokenScopeThenForbiddenError() throws Exception { - doFilterWhenClientRegistrationRequestInvalidThenError( - OAuth2ErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN); + doFilterWhenClientRegistrationRequestInvalidThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, + HttpStatus.FORBIDDEN); } - private void doFilterWhenClientRegistrationRequestInvalidThenError( - String errorCode, HttpStatus status) throws Exception { + private void doFilterWhenClientRegistrationRequestInvalidThenError(String errorCode, HttpStatus status) + throws Exception { Jwt jwt = createJwt("client.create"); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(principal); SecurityContextHolder.setContext(securityContext); - when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(errorCode)); + when(this.authenticationManager.authenticate(any())).thenThrow(new OAuth2AuthenticationException(errorCode)); // @formatter:off OidcClientRegistration clientRegistrationRequest = OidcClientRegistration.builder() @@ -245,11 +242,11 @@ public class OidcClientRegistrationEndpointFilterTests { // @formatter:on Jwt jwt = createJwt("client.create"); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.create")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.create")); - OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = - new OidcClientRegistrationAuthenticationToken(principal, expectedClientRegistrationResponse); + OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OidcClientRegistrationAuthenticationToken( + principal, expectedClientRegistrationResponse); when(this.authenticationManager.authenticate(any())).thenReturn(clientRegistrationAuthenticationResult); @@ -271,29 +268,33 @@ public class OidcClientRegistrationEndpointFilterTests { assertThat(response.getStatus()).isEqualTo(HttpStatus.CREATED.value()); OidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(response); - assertThat(clientRegistrationResponse.getClientId()).isEqualTo(expectedClientRegistrationResponse.getClientId()); + assertThat(clientRegistrationResponse.getClientId()) + .isEqualTo(expectedClientRegistrationResponse.getClientId()); assertThat(clientRegistrationResponse.getClientIdIssuedAt()).isBetween( expectedClientRegistrationResponse.getClientIdIssuedAt().minusSeconds(1), expectedClientRegistrationResponse.getClientIdIssuedAt().plusSeconds(1)); - assertThat(clientRegistrationResponse.getClientSecret()).isEqualTo(expectedClientRegistrationResponse.getClientSecret()); - assertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt()); - assertThat(clientRegistrationResponse.getClientName()).isEqualTo(expectedClientRegistrationResponse.getClientName()); + assertThat(clientRegistrationResponse.getClientSecret()) + .isEqualTo(expectedClientRegistrationResponse.getClientSecret()); + assertThat(clientRegistrationResponse.getClientSecretExpiresAt()) + .isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt()); + assertThat(clientRegistrationResponse.getClientName()) + .isEqualTo(expectedClientRegistrationResponse.getClientName()); assertThat(clientRegistrationResponse.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris()); assertThat(clientRegistrationResponse.getGrantTypes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes()); assertThat(clientRegistrationResponse.getResponseTypes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes()); assertThat(clientRegistrationResponse.getScopes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes()); assertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod()) - .isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod()); + .isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod()); assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); + .isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); assertThat(clientRegistrationResponse.getRegistrationAccessToken()) - .isEqualTo(expectedClientRegistrationResponse.getRegistrationAccessToken()); + .isEqualTo(expectedClientRegistrationResponse.getRegistrationAccessToken()); assertThat(clientRegistrationResponse.getRegistrationClientUrl()) - .isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); + .isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); } @Test @@ -358,34 +359,32 @@ public class OidcClientRegistrationEndpointFilterTests { @Test public void doFilterWhenClientConfigurationRequestInvalidTokenThenUnauthorizedError() throws Exception { - doFilterWhenClientConfigurationRequestInvalidThenError( - OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED); + doFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_TOKEN, HttpStatus.UNAUTHORIZED); } @Test public void doFilterWhenClientConfigurationRequestInsufficientScopeThenForbiddenError() throws Exception { - doFilterWhenClientConfigurationRequestInvalidThenError( - OAuth2ErrorCodes.INSUFFICIENT_SCOPE, HttpStatus.FORBIDDEN); + doFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INSUFFICIENT_SCOPE, + HttpStatus.FORBIDDEN); } @Test public void doFilterWhenClientConfigurationRequestInvalidClientThenUnauthorizedError() throws Exception { - doFilterWhenClientConfigurationRequestInvalidThenError( - OAuth2ErrorCodes.INVALID_CLIENT, HttpStatus.UNAUTHORIZED); + doFilterWhenClientConfigurationRequestInvalidThenError(OAuth2ErrorCodes.INVALID_CLIENT, + HttpStatus.UNAUTHORIZED); } - private void doFilterWhenClientConfigurationRequestInvalidThenError( - String errorCode, HttpStatus status) throws Exception { + private void doFilterWhenClientConfigurationRequestInvalidThenError(String errorCode, HttpStatus status) + throws Exception { Jwt jwt = createJwt("client.read"); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); SecurityContext securityContext = SecurityContextHolder.createEmptyContext(); securityContext.setAuthentication(principal); SecurityContextHolder.setContext(securityContext); - when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(errorCode)); + when(this.authenticationManager.authenticate(any())).thenThrow(new OAuth2AuthenticationException(errorCode)); String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -409,11 +408,11 @@ public class OidcClientRegistrationEndpointFilterTests { OidcClientRegistration expectedClientRegistrationResponse = createClientRegistration(); Jwt jwt = createJwt("client.read"); - JwtAuthenticationToken principal = new JwtAuthenticationToken( - jwt, AuthorityUtils.createAuthorityList("SCOPE_client.read")); + JwtAuthenticationToken principal = new JwtAuthenticationToken(jwt, + AuthorityUtils.createAuthorityList("SCOPE_client.read")); - OidcClientRegistrationAuthenticationToken clientConfigurationAuthenticationResult = - new OidcClientRegistrationAuthenticationToken(principal, expectedClientRegistrationResponse); + OidcClientRegistrationAuthenticationToken clientConfigurationAuthenticationResult = new OidcClientRegistrationAuthenticationToken( + principal, expectedClientRegistrationResponse); when(this.authenticationManager.authenticate(any())).thenReturn(clientConfigurationAuthenticationResult); @@ -436,27 +435,31 @@ public class OidcClientRegistrationEndpointFilterTests { assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); OidcClientRegistration clientRegistrationResponse = readClientRegistrationResponse(response); - assertThat(clientRegistrationResponse.getClientId()).isEqualTo(expectedClientRegistrationResponse.getClientId()); + assertThat(clientRegistrationResponse.getClientId()) + .isEqualTo(expectedClientRegistrationResponse.getClientId()); assertThat(clientRegistrationResponse.getClientIdIssuedAt()).isBetween( expectedClientRegistrationResponse.getClientIdIssuedAt().minusSeconds(1), expectedClientRegistrationResponse.getClientIdIssuedAt().plusSeconds(1)); - assertThat(clientRegistrationResponse.getClientSecret()).isEqualTo(expectedClientRegistrationResponse.getClientSecret()); - assertThat(clientRegistrationResponse.getClientSecretExpiresAt()).isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt()); - assertThat(clientRegistrationResponse.getClientName()).isEqualTo(expectedClientRegistrationResponse.getClientName()); + assertThat(clientRegistrationResponse.getClientSecret()) + .isEqualTo(expectedClientRegistrationResponse.getClientSecret()); + assertThat(clientRegistrationResponse.getClientSecretExpiresAt()) + .isEqualTo(expectedClientRegistrationResponse.getClientSecretExpiresAt()); + assertThat(clientRegistrationResponse.getClientName()) + .isEqualTo(expectedClientRegistrationResponse.getClientName()); assertThat(clientRegistrationResponse.getRedirectUris()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getRedirectUris()); assertThat(clientRegistrationResponse.getGrantTypes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getGrantTypes()); assertThat(clientRegistrationResponse.getResponseTypes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getResponseTypes()); assertThat(clientRegistrationResponse.getScopes()) - .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes()); + .containsExactlyInAnyOrderElementsOf(expectedClientRegistrationResponse.getScopes()); assertThat(clientRegistrationResponse.getTokenEndpointAuthenticationMethod()) - .isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod()); + .isEqualTo(expectedClientRegistrationResponse.getTokenEndpointAuthenticationMethod()); assertThat(clientRegistrationResponse.getIdTokenSignedResponseAlgorithm()) - .isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); + .isEqualTo(expectedClientRegistrationResponse.getIdTokenSignedResponseAlgorithm()); assertThat(clientRegistrationResponse.getRegistrationClientUrl()) - .isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); + .isEqualTo(expectedClientRegistrationResponse.getRegistrationClientUrl()); } @Test @@ -483,8 +486,8 @@ public class OidcClientRegistrationEndpointFilterTests { OidcClientRegistration expectedClientRegistrationResponse = createClientRegistration(); Authentication principal = new TestingAuthenticationToken("principal", "Credentials"); - OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = - new OidcClientRegistrationAuthenticationToken(principal, expectedClientRegistrationResponse); + OidcClientRegistrationAuthenticationToken clientRegistrationAuthenticationResult = new OidcClientRegistrationAuthenticationToken( + principal, expectedClientRegistrationResponse); when(this.authenticationManager.authenticate(any())).thenReturn(clientRegistrationAuthenticationResult); AuthenticationSuccessHandler successHandler = mock(AuthenticationSuccessHandler.class); @@ -514,7 +517,7 @@ public class OidcClientRegistrationEndpointFilterTests { this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN)); + .thenThrow(new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN)); String requestUri = DEFAULT_OIDC_CLIENT_REGISTRATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -543,8 +546,8 @@ public class OidcClientRegistrationEndpointFilterTests { } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } @@ -556,8 +559,8 @@ public class OidcClientRegistrationEndpointFilterTests { } private OidcClientRegistration readClientRegistrationResponse(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.clientRegistrationHttpMessageConverter.read(OidcClientRegistration.class, httpResponse); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilterTests.java index a678823e..2b5417c4 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcLogoutEndpointFilterTests.java @@ -62,9 +62,13 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OidcLogoutEndpointFilterTests { + private static final String DEFAULT_OIDC_LOGOUT_ENDPOINT_URI = "/connect/logout"; + private AuthenticationManager authenticationManager; + private OidcLogoutEndpointFilter filter; + private TestingAuthenticationToken principal; @BeforeEach @@ -85,37 +89,36 @@ public class OidcLogoutEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new OidcLogoutEndpointFilter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + assertThatThrownBy(() -> new OidcLogoutEndpointFilter(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenLogoutEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OidcLogoutEndpointFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("logoutEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("logoutEndpointUri cannot be empty"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test @@ -134,26 +137,21 @@ public class OidcLogoutEndpointFilterTests { @Test public void doFilterWhenLogoutRequestMissingIdTokenHintThenInvalidRequestError() throws Exception { doFilterWhenRequestInvalidParameterThenError( - createLogoutRequest(TestRegisteredClients.registeredClient().build()), - "id_token_hint", - OAuth2ErrorCodes.INVALID_REQUEST, - request -> request.removeParameter("id_token_hint")); + createLogoutRequest(TestRegisteredClients.registeredClient().build()), "id_token_hint", + OAuth2ErrorCodes.INVALID_REQUEST, request -> request.removeParameter("id_token_hint")); } @Test public void doFilterWhenLogoutRequestMultipleIdTokenHintThenInvalidRequestError() throws Exception { doFilterWhenRequestInvalidParameterThenError( - createLogoutRequest(TestRegisteredClients.registeredClient().build()), - "id_token_hint", - OAuth2ErrorCodes.INVALID_REQUEST, - request -> request.addParameter("id_token_hint", "id-token-2")); + createLogoutRequest(TestRegisteredClients.registeredClient().build()), "id_token_hint", + OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter("id_token_hint", "id-token-2")); } @Test public void doFilterWhenLogoutRequestMultipleClientIdThenInvalidRequestError() throws Exception { doFilterWhenRequestInvalidParameterThenError( - createLogoutRequest(TestRegisteredClients.registeredClient().build()), - OAuth2ParameterNames.CLIENT_ID, + createLogoutRequest(TestRegisteredClients.registeredClient().build()), OAuth2ParameterNames.CLIENT_ID, OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-2")); } @@ -161,8 +159,7 @@ public class OidcLogoutEndpointFilterTests { @Test public void doFilterWhenLogoutRequestMultiplePostLogoutRedirectUriThenInvalidRequestError() throws Exception { doFilterWhenRequestInvalidParameterThenError( - createLogoutRequest(TestRegisteredClients.registeredClient().build()), - "post_logout_redirect_uri", + createLogoutRequest(TestRegisteredClients.registeredClient().build()), "post_logout_redirect_uri", OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter("post_logout_redirect_uri", "https://example.com/callback-4")); } @@ -170,14 +167,13 @@ public class OidcLogoutEndpointFilterTests { @Test public void doFilterWhenLogoutRequestMultipleStateThenInvalidRequestError() throws Exception { doFilterWhenRequestInvalidParameterThenError( - createLogoutRequest(TestRegisteredClients.registeredClient().build()), - OAuth2ParameterNames.STATE, + createLogoutRequest(TestRegisteredClients.registeredClient().build()), OAuth2ParameterNames.STATE, OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter(OAuth2ParameterNames.STATE, "state-2")); } - private void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, - String parameterName, String errorCode, Consumer requestConsumer) throws Exception { + private void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, String parameterName, + String errorCode, Consumer requestConsumer) throws Exception { requestConsumer.accept(request); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -188,14 +184,14 @@ public class OidcLogoutEndpointFilterTests { verifyNoInteractions(filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.BAD_REQUEST.value()); - assertThat(response.getErrorMessage()).isEqualTo("[" + errorCode + "] OpenID Connect 1.0 Logout Request Parameter: " + parameterName); + assertThat(response.getErrorMessage()) + .isEqualTo("[" + errorCode + "] OpenID Connect 1.0 Logout Request Parameter: " + parameterName); } @Test public void doFilterWhenLogoutRequestAuthenticationExceptionThenErrorResponse() throws Exception { OAuth2Error error = new OAuth2Error("errorCode", "errorDescription", "errorUri"); - when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(error)); + when(this.authenticationManager.authenticate(any())).thenThrow(new OAuth2AuthenticationException(error)); MockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build()); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -213,15 +209,14 @@ public class OidcLogoutEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - "id-token", this.principal, null, null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken("id-token", this.principal, + null, null, null, null); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(authentication); this.filter.setAuthenticationConverter(authenticationConverter); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authentication); MockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build()); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -236,14 +231,13 @@ public class OidcLogoutEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - "id-token", this.principal, null, null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken("id-token", this.principal, + null, null, null, null); AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authentication); MockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build()); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -262,7 +256,7 @@ public class OidcLogoutEndpointFilterTests { this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); when(this.authenticationManager.authenticate(any())) - .thenThrow(new AuthenticationServiceException("AuthenticationServiceException")); + .thenThrow(new AuthenticationServiceException("AuthenticationServiceException")); MockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build()); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -270,18 +264,19 @@ public class OidcLogoutEndpointFilterTests { this.filter.doFilter(request, response, filterChain); - ArgumentCaptor authenticationExceptionCaptor = ArgumentCaptor.forClass(AuthenticationException.class); + ArgumentCaptor authenticationExceptionCaptor = ArgumentCaptor + .forClass(AuthenticationException.class); verify(this.authenticationManager).authenticate(any()); - verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), authenticationExceptionCaptor.capture()); + verify(authenticationFailureHandler).onAuthenticationFailure(any(), any(), + authenticationExceptionCaptor.capture()); verifyNoInteractions(filterChain); - assertThat(authenticationExceptionCaptor.getValue()) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .satisfies(error -> { - assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); - assertThat(error.getDescription()).contains("AuthenticationServiceException"); - }); + assertThat(authenticationExceptionCaptor.getValue()).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .satisfies(error -> { + assertThat(error.getErrorCode()).isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThat(error.getDescription()).contains("AuthenticationServiceException"); + }); } @Test @@ -289,11 +284,10 @@ public class OidcLogoutEndpointFilterTests { MockHttpServletRequest request = createLogoutRequest(TestRegisteredClients.registeredClient().build()); MockHttpSession session = (MockHttpSession) request.getSession(true); - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - "id-token", this.principal, session.getId(), null, null, null); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken("id-token", this.principal, + session.getId(), null, null, null); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authentication); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -310,20 +304,19 @@ public class OidcLogoutEndpointFilterTests { } @Test - public void doFilterWhenLogoutRequestAuthenticatedWithPostLogoutRedirectUriThenPostLogoutRedirect() throws Exception { + public void doFilterWhenLogoutRequestAuthenticatedWithPostLogoutRedirectUriThenPostLogoutRedirect() + throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); MockHttpServletRequest request = createLogoutRequest(registeredClient); MockHttpSession session = (MockHttpSession) request.getSession(true); String postLogoutRedirectUri = registeredClient.getPostLogoutRedirectUris().iterator().next(); String state = "state-1"; - OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken( - "id-token", this.principal, session.getId(), - registeredClient.getClientId(), postLogoutRedirectUri, state); + OidcLogoutAuthenticationToken authentication = new OidcLogoutAuthenticationToken("id-token", this.principal, + session.getId(), registeredClient.getClientId(), postLogoutRedirectUri, state); authentication.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authentication); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -346,7 +339,8 @@ public class OidcLogoutEndpointFilterTests { request.addParameter("id_token_hint", "id-token"); request.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); - request.addParameter("post_logout_redirect_uri", registeredClient.getPostLogoutRedirectUris().iterator().next()); + request.addParameter("post_logout_redirect_uri", + registeredClient.getPostLogoutRedirectUris().iterator().next()); request.addParameter(OAuth2ParameterNames.STATE, "state"); return request; diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java index 323b0650..96ed9f9b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcProviderConfigurationEndpointFilterTests.java @@ -44,7 +44,9 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Joe Grandja */ public class OidcProviderConfigurationEndpointFilterTests { + private static final String DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI = "/.well-known/openid-configuration"; + private final OidcProviderConfigurationEndpointFilter filter = new OidcProviderConfigurationEndpointFilter(); @AfterEach @@ -55,8 +57,8 @@ public class OidcProviderConfigurationEndpointFilterTests { @Test public void setProviderConfigurationCustomizerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setProviderConfigurationCustomizer(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("providerConfigurationCustomizer cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("providerConfigurationCustomizer cannot be null"); } @Test @@ -97,16 +99,17 @@ public class OidcProviderConfigurationEndpointFilterTests { String tokenIntrospectionEndpoint = "/oauth2/v1/introspect"; AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .issuer(issuer) - .authorizationEndpoint(authorizationEndpoint) - .tokenEndpoint(tokenEndpoint) - .jwkSetEndpoint(jwkSetEndpoint) - .oidcUserInfoEndpoint(userInfoEndpoint) - .oidcLogoutEndpoint(logoutEndpoint) - .tokenRevocationEndpoint(tokenRevocationEndpoint) - .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) - .build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + .issuer(issuer) + .authorizationEndpoint(authorizationEndpoint) + .tokenEndpoint(tokenEndpoint) + .jwkSetEndpoint(jwkSetEndpoint) + .oidcUserInfoEndpoint(userInfoEndpoint) + .oidcLogoutEndpoint(logoutEndpoint) + .tokenRevocationEndpoint(tokenRevocationEndpoint) + .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); String requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -121,8 +124,10 @@ public class OidcProviderConfigurationEndpointFilterTests { assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE); String providerConfigurationResponse = response.getContentAsString(); assertThat(providerConfigurationResponse).contains("\"issuer\":\"https://example.com\""); - assertThat(providerConfigurationResponse).contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\""); - assertThat(providerConfigurationResponse).contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\""); + assertThat(providerConfigurationResponse) + .contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\""); + assertThat(providerConfigurationResponse) + .contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\""); assertThat(providerConfigurationResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\""); assertThat(providerConfigurationResponse).contains("\"scopes_supported\":[\"openid\"]"); assertThat(providerConfigurationResponse).contains("\"response_types_supported\":[\"code\"]"); @@ -135,16 +140,19 @@ public class OidcProviderConfigurationEndpointFilterTests { assertThat(providerConfigurationResponse).contains("\"subject_types_supported\":[\"public\"]"); assertThat(providerConfigurationResponse).contains("\"id_token_signing_alg_values_supported\":[\"RS256\"]"); assertThat(providerConfigurationResponse).contains("\"userinfo_endpoint\":\"https://example.com/userinfo\""); - assertThat(providerConfigurationResponse).contains("\"end_session_endpoint\":\"https://example.com/connect/logout\""); - assertThat(providerConfigurationResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); + assertThat(providerConfigurationResponse) + .contains("\"end_session_endpoint\":\"https://example.com/connect/logout\""); + assertThat(providerConfigurationResponse).contains( + "\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); } @Test public void doFilterWhenAuthorizationServerSettingsWithInvalidIssuerThenThrowIllegalArgumentException() { AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .issuer("https://this is an invalid URL") - .build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + .issuer("https://this is an invalid URL") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); String requestUri = DEFAULT_OIDC_PROVIDER_CONFIGURATION_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -152,9 +160,8 @@ public class OidcProviderConfigurationEndpointFilterTests { MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.doFilter(request, response, filterChain)) - .withMessage("issuer must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.doFilter(request, response, filterChain)) + .withMessage("issuer must be a valid URL"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilterTests.java index dae6781e..323595e1 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/oidc/web/OidcUserInfoEndpointFilterTests.java @@ -62,9 +62,13 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OidcUserInfoEndpointFilterTests { + private static final String DEFAULT_OIDC_USER_INFO_ENDPOINT_URI = "/userinfo"; + private AuthenticationManager authenticationManager; + private OidcUserInfoEndpointFilter filter; + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach @@ -75,37 +79,33 @@ public class OidcUserInfoEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcUserInfoEndpointFilter(null)) - .withMessage("authenticationManager cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> new OidcUserInfoEndpointFilter(null)) + .withMessage("authenticationManager cannot be null"); } @Test public void constructorWhenUserInfoEndpointUriIsEmptyThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> new OidcUserInfoEndpointFilter(this.authenticationManager, "")) - .withMessage("userInfoEndpointUri cannot be empty"); + .isThrownBy(() -> new OidcUserInfoEndpointFilter(this.authenticationManager, "")) + .withMessage("userInfoEndpointUri cannot be empty"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .withMessage("authenticationConverter cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationConverter(null)) + .withMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .withMessage("authenticationSuccessHandler cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) + .withMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .withMessage("authenticationFailureHandler cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) + .withMessage("authenticationFailureHandler cannot be null"); } @Test @@ -149,7 +149,8 @@ public class OidcUserInfoEndpointFilterTests { JwtAuthenticationToken principal = createJwtAuthenticationToken(); SecurityContextHolder.getContext().setAuthentication(principal); - OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal, createUserInfo()); + OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal, + createUserInfo()); when(this.authenticationManager.authenticate(any())).thenReturn(authentication); String requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI; @@ -183,7 +184,7 @@ public class OidcUserInfoEndpointFilterTests { SecurityContextHolder.getContext().setAuthentication(principal); when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthenticationException(oauth2ErrorCode)); + .thenThrow(new OAuth2AuthenticationException(oauth2ErrorCode)); String requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -208,9 +209,8 @@ public class OidcUserInfoEndpointFilterTests { this.filter.setAuthenticationConverter(authenticationConverter); when(authenticationConverter.convert(any())).thenReturn(authentication); - when(this.authenticationManager.authenticate(any())).thenReturn( - new OidcUserInfoAuthenticationToken(principal, createUserInfo()) - ); + when(this.authenticationManager.authenticate(any())) + .thenReturn(new OidcUserInfoAuthenticationToken(principal, createUserInfo())); String requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -234,7 +234,8 @@ public class OidcUserInfoEndpointFilterTests { Authentication principal = new TestingAuthenticationToken("principal", "credentials"); SecurityContextHolder.getContext().setAuthentication(principal); - OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal, createUserInfo()); + OidcUserInfoAuthenticationToken authentication = new OidcUserInfoAuthenticationToken(principal, + createUserInfo()); when(this.authenticationManager.authenticate(any())).thenReturn(authentication); String requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI; @@ -257,8 +258,8 @@ public class OidcUserInfoEndpointFilterTests { Authentication principal = new TestingAuthenticationToken("principal", "credentials"); SecurityContextHolder.getContext().setAuthentication(principal); - OAuth2AuthenticationException authenticationException = - new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_TOKEN); + OAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException( + OAuth2ErrorCodes.INVALID_TOKEN); when(this.authenticationManager.authenticate(any())).thenThrow(authenticationException); String requestUri = DEFAULT_OIDC_USER_INFO_ENDPOINT_URI; @@ -274,8 +275,8 @@ public class OidcUserInfoEndpointFilterTests { } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } @@ -294,27 +295,27 @@ public class OidcUserInfoEndpointFilterTests { private static OidcUserInfo createUserInfo() { return OidcUserInfo.builder() - .subject("user1") - .name("First Last") - .givenName("First") - .familyName("Last") - .middleName("Middle") - .nickname("User") - .preferredUsername("user") - .profile("https://example.com/user1") - .picture("https://example.com/user1.jpg") - .website("https://example.com") - .email("user1@example.com") - .emailVerified(true) - .gender("female") - .birthdate("1970-01-01") - .zoneinfo("Europe/Paris") - .locale("en-US") - .phoneNumber("+1 (604) 555-1234;ext=5678") - .phoneNumberVerified(false) - .address("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance") - .updatedAt("1970-01-01T00:00:00Z") - .build(); + .subject("user1") + .name("First Last") + .givenName("First") + .familyName("Last") + .middleName("Middle") + .nickname("User") + .preferredUsername("user") + .profile("https://example.com/user1") + .picture("https://example.com/user1.jpg") + .website("https://example.com") + .email("user1@example.com") + .emailVerified(true) + .gender("female") + .birthdate("1970-01-01") + .zoneinfo("Europe/Paris") + .locale("en-US") + .phoneNumber("+1 (604) 555-1234;ext=5678") + .phoneNumberVerified(false) + .address("Champ de Mars\n5 Av. Anatole France\n75007 Paris\nFrance") + .updatedAt("1970-01-01T00:00:00Z") + .build(); } private static void assertUserInfoResponse(String userInfoResponse) { @@ -336,7 +337,8 @@ public class OidcUserInfoEndpointFilterTests { assertThat(userInfoResponse).contains("\"locale\":\"en-US\""); assertThat(userInfoResponse).contains("\"phone_number\":\"+1 (604) 555-1234;ext=5678\""); assertThat(userInfoResponse).contains("\"phone_number_verified\":false"); - assertThat(userInfoResponse).contains("\"address\":\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\""); + assertThat(userInfoResponse) + .contains("\"address\":\"Champ de Mars\\n5 Av. Anatole France\\n75007 Paris\\nFrance\""); assertThat(userInfoResponse).contains("\"updated_at\":\"1970-01-01T00:00:00Z\""); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettingsTests.java index 9eb902aa..caf6b536 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettingsTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/AuthorizationServerSettingsTests.java @@ -56,17 +56,17 @@ public class AuthorizationServerSettingsTests { String issuer = "https://example.com:9000"; AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .issuer(issuer) - .authorizationEndpoint(authorizationEndpoint) - .tokenEndpoint(tokenEndpoint) - .jwkSetEndpoint(jwkSetEndpoint) - .tokenRevocationEndpoint(tokenRevocationEndpoint) - .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) - .tokenRevocationEndpoint(tokenRevocationEndpoint) - .oidcClientRegistrationEndpoint(oidcClientRegistrationEndpoint) - .oidcUserInfoEndpoint(oidcUserInfoEndpoint) - .oidcLogoutEndpoint(oidcLogoutEndpoint) - .build(); + .issuer(issuer) + .authorizationEndpoint(authorizationEndpoint) + .tokenEndpoint(tokenEndpoint) + .jwkSetEndpoint(jwkSetEndpoint) + .tokenRevocationEndpoint(tokenRevocationEndpoint) + .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) + .tokenRevocationEndpoint(tokenRevocationEndpoint) + .oidcClientRegistrationEndpoint(oidcClientRegistrationEndpoint) + .oidcUserInfoEndpoint(oidcUserInfoEndpoint) + .oidcLogoutEndpoint(oidcLogoutEndpoint) + .build(); assertThat(authorizationServerSettings.getIssuer()).isEqualTo(issuer); assertThat(authorizationServerSettings.getAuthorizationEndpoint()).isEqualTo(authorizationEndpoint); @@ -74,7 +74,8 @@ public class AuthorizationServerSettingsTests { assertThat(authorizationServerSettings.getJwkSetEndpoint()).isEqualTo(jwkSetEndpoint); assertThat(authorizationServerSettings.getTokenRevocationEndpoint()).isEqualTo(tokenRevocationEndpoint); assertThat(authorizationServerSettings.getTokenIntrospectionEndpoint()).isEqualTo(tokenIntrospectionEndpoint); - assertThat(authorizationServerSettings.getOidcClientRegistrationEndpoint()).isEqualTo(oidcClientRegistrationEndpoint); + assertThat(authorizationServerSettings.getOidcClientRegistrationEndpoint()) + .isEqualTo(oidcClientRegistrationEndpoint); assertThat(authorizationServerSettings.getOidcUserInfoEndpoint()).isEqualTo(oidcUserInfoEndpoint); assertThat(authorizationServerSettings.getOidcLogoutEndpoint()).isEqualTo(oidcLogoutEndpoint); } @@ -82,9 +83,9 @@ public class AuthorizationServerSettingsTests { @Test public void settingWhenCustomThenSet() { AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .setting("name1", "value1") - .settings(settings -> settings.put("name2", "value2")) - .build(); + .setting("name1", "value1") + .settings(settings -> settings.put("name2", "value2")) + .build(); assertThat(authorizationServerSettings.getSettings()).hasSize(12); assertThat(authorizationServerSettings.getSetting("name1")).isEqualTo("value1"); @@ -93,65 +94,63 @@ public class AuthorizationServerSettingsTests { @Test public void issuerWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().issuer(null)) - .withMessage("value cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> AuthorizationServerSettings.builder().issuer(null)) + .withMessage("value cannot be null"); } @Test public void authorizationEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().authorizationEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().authorizationEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void tokenEndpointWhenNullThenThrowIllegalArgumentException() { - assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().tokenEndpoint(null)) - .withMessage("value cannot be null"); + assertThatIllegalArgumentException().isThrownBy(() -> AuthorizationServerSettings.builder().tokenEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void tokenRevocationEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().tokenRevocationEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().tokenRevocationEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void tokenIntrospectionEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().tokenIntrospectionEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().tokenIntrospectionEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void oidcClientRegistrationEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().oidcClientRegistrationEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().oidcClientRegistrationEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void oidcUserInfoEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().oidcUserInfoEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().oidcUserInfoEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void jwksEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().jwkSetEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().jwkSetEndpoint(null)) + .withMessage("value cannot be null"); } @Test public void oidcLogoutEndpointWhenNullThenThrowIllegalArgumentException() { assertThatIllegalArgumentException() - .isThrownBy(() -> AuthorizationServerSettings.builder().oidcLogoutEndpoint(null)) - .withMessage("value cannot be null"); + .isThrownBy(() -> AuthorizationServerSettings.builder().oidcLogoutEndpoint(null)) + .withMessage("value cannot be null"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettingsTests.java index 06362275..20a20445 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettingsTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/ClientSettingsTests.java @@ -38,42 +38,36 @@ public class ClientSettingsTests { @Test public void requireProofKeyWhenTrueThenSet() { - ClientSettings clientSettings = ClientSettings.builder() - .requireProofKey(true) - .build(); + ClientSettings clientSettings = ClientSettings.builder().requireProofKey(true).build(); assertThat(clientSettings.isRequireProofKey()).isTrue(); } @Test public void requireAuthorizationConsentWhenTrueThenSet() { - ClientSettings clientSettings = ClientSettings.builder() - .requireAuthorizationConsent(true) - .build(); + ClientSettings clientSettings = ClientSettings.builder().requireAuthorizationConsent(true).build(); assertThat(clientSettings.isRequireAuthorizationConsent()).isTrue(); } @Test public void tokenEndpointAuthenticationSigningAlgorithmWhenHS256ThenSet() { ClientSettings clientSettings = ClientSettings.builder() - .tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256) - .build(); + .tokenEndpointAuthenticationSigningAlgorithm(MacAlgorithm.HS256) + .build(); assertThat(clientSettings.getTokenEndpointAuthenticationSigningAlgorithm()).isEqualTo(MacAlgorithm.HS256); } @Test public void jwkSetUrlWhenProvidedThenSet() { - ClientSettings clientSettings = ClientSettings.builder() - .jwkSetUrl("https://client.example.com/jwks") - .build(); + ClientSettings clientSettings = ClientSettings.builder().jwkSetUrl("https://client.example.com/jwks").build(); assertThat(clientSettings.getJwkSetUrl()).isEqualTo("https://client.example.com/jwks"); } @Test public void settingWhenCustomThenSet() { ClientSettings clientSettings = ClientSettings.builder() - .setting("name1", "value1") - .settings(settings -> settings.put("name2", "value2")) - .build(); + .setting("name1", "value1") + .settings(settings -> settings.put("name2", "value2")) + .build(); assertThat(clientSettings.getSettings()).hasSize(4); assertThat(clientSettings.getSetting("name1")).isEqualTo("value1"); assertThat(clientSettings.getSetting("name2")).isEqualTo("value2"); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java index d1552dfe..a3e1b97a 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/settings/TokenSettingsTests.java @@ -48,122 +48,114 @@ public class TokenSettingsTests { public void authorizationCodeTimeToLiveWhenProvidedThenSet() { Duration authorizationCodeTimeToLive = Duration.ofMinutes(10); TokenSettings tokenSettings = TokenSettings.builder() - .authorizationCodeTimeToLive(authorizationCodeTimeToLive) - .build(); + .authorizationCodeTimeToLive(authorizationCodeTimeToLive) + .build(); assertThat(tokenSettings.getAuthorizationCodeTimeToLive()).isEqualTo(authorizationCodeTimeToLive); } @Test public void authorizationCodeTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() { assertThatThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(null)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("authorizationCodeTimeToLive cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("authorizationCodeTimeToLive cannot be null"); assertThatThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(Duration.ZERO)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("authorizationCodeTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("authorizationCodeTimeToLive must be greater than Duration.ZERO"); assertThatThrownBy(() -> TokenSettings.builder().authorizationCodeTimeToLive(Duration.ofSeconds(-10))) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("authorizationCodeTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("authorizationCodeTimeToLive must be greater than Duration.ZERO"); } @Test public void accessTokenTimeToLiveWhenProvidedThenSet() { Duration accessTokenTimeToLive = Duration.ofMinutes(10); - TokenSettings tokenSettings = TokenSettings.builder() - .accessTokenTimeToLive(accessTokenTimeToLive) - .build(); + TokenSettings tokenSettings = TokenSettings.builder().accessTokenTimeToLive(accessTokenTimeToLive).build(); assertThat(tokenSettings.getAccessTokenTimeToLive()).isEqualTo(accessTokenTimeToLive); } @Test public void accessTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() { assertThatThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(null)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("accessTokenTimeToLive cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("accessTokenTimeToLive cannot be null"); assertThatThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(Duration.ZERO)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO"); assertThatThrownBy(() -> TokenSettings.builder().accessTokenTimeToLive(Duration.ofSeconds(-10))) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("accessTokenTimeToLive must be greater than Duration.ZERO"); } @Test public void accessTokenFormatWhenProvidedThenSet() { - TokenSettings tokenSettings = TokenSettings.builder() - .accessTokenFormat(OAuth2TokenFormat.REFERENCE) - .build(); + TokenSettings tokenSettings = TokenSettings.builder().accessTokenFormat(OAuth2TokenFormat.REFERENCE).build(); assertThat(tokenSettings.getAccessTokenFormat()).isEqualTo(OAuth2TokenFormat.REFERENCE); } @Test public void accessTokenFormatWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> TokenSettings.builder().accessTokenFormat(null)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("accessTokenFormat cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("accessTokenFormat cannot be null"); } @Test public void reuseRefreshTokensWhenFalseThenSet() { - TokenSettings tokenSettings = TokenSettings.builder() - .reuseRefreshTokens(false) - .build(); + TokenSettings tokenSettings = TokenSettings.builder().reuseRefreshTokens(false).build(); assertThat(tokenSettings.isReuseRefreshTokens()).isFalse(); } @Test public void refreshTokenTimeToLiveWhenProvidedThenSet() { Duration refreshTokenTimeToLive = Duration.ofDays(10); - TokenSettings tokenSettings = TokenSettings.builder() - .refreshTokenTimeToLive(refreshTokenTimeToLive) - .build(); + TokenSettings tokenSettings = TokenSettings.builder().refreshTokenTimeToLive(refreshTokenTimeToLive).build(); assertThat(tokenSettings.getRefreshTokenTimeToLive()).isEqualTo(refreshTokenTimeToLive); } @Test public void refreshTokenTimeToLiveWhenNullOrZeroOrNegativeThenThrowIllegalArgumentException() { assertThatThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(null)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("refreshTokenTimeToLive cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("refreshTokenTimeToLive cannot be null"); assertThatThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(Duration.ZERO)) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); assertThatThrownBy(() -> TokenSettings.builder().refreshTokenTimeToLive(Duration.ofSeconds(-10))) - .isInstanceOf(IllegalArgumentException.class) - .extracting(Throwable::getMessage) - .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); + .isInstanceOf(IllegalArgumentException.class) + .extracting(Throwable::getMessage) + .isEqualTo("refreshTokenTimeToLive must be greater than Duration.ZERO"); } @Test public void idTokenSignatureAlgorithmWhenProvidedThenSet() { SignatureAlgorithm idTokenSignatureAlgorithm = SignatureAlgorithm.RS512; TokenSettings tokenSettings = TokenSettings.builder() - .idTokenSignatureAlgorithm(idTokenSignatureAlgorithm) - .build(); + .idTokenSignatureAlgorithm(idTokenSignatureAlgorithm) + .build(); assertThat(tokenSettings.getIdTokenSignatureAlgorithm()).isEqualTo(idTokenSignatureAlgorithm); } @Test public void settingWhenCustomThenSet() { TokenSettings tokenSettings = TokenSettings.builder() - .setting("name1", "value1") - .settings(settings -> settings.put("name2", "value2")) - .build(); + .setting("name1", "value1") + .settings(settings -> settings.put("name2", "value2")) + .build(); assertThat(tokenSettings.getSettings()).hasSize(9); assertThat(tokenSettings.getSetting("name1")).isEqualTo("value1"); assertThat(tokenSettings.getSetting("name2")).isEqualTo("value2"); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/test/SpringTestContext.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/test/SpringTestContext.java index 1e8cd476..06b377e5 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/test/SpringTestContext.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/test/SpringTestContext.java @@ -46,6 +46,7 @@ import static org.springframework.security.test.web.servlet.setup.SecurityMockMv * @author Rob Winch */ public class SpringTestContext implements Closeable { + private Object test; private ConfigurableWebApplicationContext context; @@ -60,7 +61,9 @@ public class SpringTestContext implements Closeable { public void close() { try { this.context.close(); - } catch(Exception e) {} + } + catch (Exception e) { + } } public SpringTestContext context(ConfigurableWebApplicationContext context) { @@ -77,8 +80,7 @@ public class SpringTestContext implements Closeable { public SpringTestContext testConfigLocations(String... configLocations) { GenericXmlWebContextLoader loader = new GenericXmlWebContextLoader(); - String[] locations = loader.processLocations(this.test.getClass(), - configLocations); + String[] locations = loader.processLocations(this.test.getClass(), configLocations); return configLocations(locations); } @@ -92,8 +94,8 @@ public class SpringTestContext implements Closeable { public SpringTestContext mockMvcAfterSpringSecurityOk() { return addFilter(new OncePerRequestFilter() { @Override - protected void doFilterInternal(HttpServletRequest request, - HttpServletResponse response, FilterChain filterChain) { + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, + FilterChain filterChain) { response.setStatus(HttpServletResponse.SC_OK); } }); @@ -121,9 +123,9 @@ public class SpringTestContext implements Closeable { if (this.context.containsBean(SPRING_SECURITY_FILTER_CHAIN)) { MockMvc mockMvc = MockMvcBuilders.webAppContextSetup(this.context) .apply(springSecurity()) - .apply(new AddFilter()).build(); - this.context.getBeanFactory() - .registerResolvableDependency(MockMvc.class, mockMvc); + .apply(new AddFilter()) + .build(); + this.context.getBeanFactory().registerResolvableDependency(MockMvc.class, mockMvc); } AutowiredAnnotationBeanPostProcessor bpp = new AutowiredAnnotationBeanPostProcessor(); @@ -132,10 +134,13 @@ public class SpringTestContext implements Closeable { } private class AddFilter implements MockMvcConfigurer { - public RequestPostProcessor beforeMockMvcCreated( - ConfigurableMockMvcBuilder builder, WebApplicationContext context) { + + public RequestPostProcessor beforeMockMvcCreated(ConfigurableMockMvcBuilder builder, + WebApplicationContext context) { builder.addFilters(SpringTestContext.this.filters.toArray(new Filter[0])); return null; } + } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGeneratorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGeneratorTests.java index fe8dfe37..03267b35 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGeneratorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/DelegatingOAuth2TokenGeneratorTests.java @@ -40,15 +40,15 @@ public class DelegatingOAuth2TokenGeneratorTests { public void constructorWhenTokenGeneratorsEmptyThenThrowIllegalArgumentException() { OAuth2TokenGenerator[] tokenGenerators = new OAuth2TokenGenerator[0]; assertThatThrownBy(() -> new DelegatingOAuth2TokenGenerator(tokenGenerators)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenGenerators cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenGenerators cannot be empty"); } @Test public void constructorWhenTokenGeneratorsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new DelegatingOAuth2TokenGenerator(null, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenGenerator cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenGenerator cannot be null"); } @Test @@ -58,12 +58,12 @@ public class DelegatingOAuth2TokenGeneratorTests { OAuth2TokenGenerator tokenGenerator2 = mock(OAuth2TokenGenerator.class); OAuth2TokenGenerator tokenGenerator3 = mock(OAuth2TokenGenerator.class); - OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, - "access-token", Instant.now(), Instant.now().plusSeconds(300)); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "access-token", + Instant.now(), Instant.now().plusSeconds(300)); when(tokenGenerator3.generate(any())).thenReturn(accessToken); - DelegatingOAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(tokenGenerator1, tokenGenerator2, tokenGenerator3); + DelegatingOAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(tokenGenerator1, + tokenGenerator2, tokenGenerator3); OAuth2Token token = delegatingTokenGenerator.generate(DefaultOAuth2TokenContext.builder().build()); assertThat(token).isEqualTo(accessToken); @@ -76,8 +76,8 @@ public class DelegatingOAuth2TokenGeneratorTests { OAuth2TokenGenerator tokenGenerator2 = mock(OAuth2TokenGenerator.class); OAuth2TokenGenerator tokenGenerator3 = mock(OAuth2TokenGenerator.class); - DelegatingOAuth2TokenGenerator delegatingTokenGenerator = - new DelegatingOAuth2TokenGenerator(tokenGenerator1, tokenGenerator2, tokenGenerator3); + DelegatingOAuth2TokenGenerator delegatingTokenGenerator = new DelegatingOAuth2TokenGenerator(tokenGenerator1, + tokenGenerator2, tokenGenerator3); OAuth2Token token = delegatingTokenGenerator.generate(DefaultOAuth2TokenContext.builder().build()); assertThat(token).isNull(); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContextTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContextTests.java index e5b5a3a5..c40b7199 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContextTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtEncodingContextTests.java @@ -48,35 +48,28 @@ public class JwtEncodingContextTests { @Test public void withWhenJwsHeaderNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> JwtEncodingContext.with(null, TestJwtClaimsSets.jwtClaimsSet())) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwsHeaderBuilder cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwsHeaderBuilder cannot be null"); } @Test public void withWhenClaimsNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> JwtEncodingContext.with(TestJwsHeaders.jwsHeader(), null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("claimsBuilder cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("claimsBuilder cannot be null"); } @Test public void setWhenValueNullThenThrowIllegalArgumentException() { - JwtEncodingContext.Builder builder = JwtEncodingContext - .with(TestJwsHeaders.jwsHeader(), TestJwtClaimsSets.jwtClaimsSet()); - assertThatThrownBy(() -> builder.registeredClient(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.principal(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.authorization(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.tokenType(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.authorizationGrantType(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.authorizationGrant(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.put(null, "")) - .isInstanceOf(IllegalArgumentException.class); + JwtEncodingContext.Builder builder = JwtEncodingContext.with(TestJwsHeaders.jwsHeader(), + TestJwtClaimsSets.jwtClaimsSet()); + assertThatThrownBy(() -> builder.registeredClient(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.principal(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorization(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.tokenType(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorizationGrantType(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.authorizationGrant(null)).isInstanceOf(IllegalArgumentException.class); + assertThatThrownBy(() -> builder.put(null, "")).isInstanceOf(IllegalArgumentException.class); } @Test @@ -86,24 +79,23 @@ public class JwtEncodingContextTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); TestingAuthenticationToken principal = new TestingAuthenticationToken("principal", "password"); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization().build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = - new OAuth2AuthorizationCodeAuthenticationToken( - "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); JwtEncodingContext context = JwtEncodingContext.with(headers, claims) - .registeredClient(registeredClient) - .principal(principal) - .authorization(authorization) - .tokenType(OAuth2TokenType.ACCESS_TOKEN) - .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) - .authorizationGrant(authorizationGrant) - .put("custom-key-1", "custom-value-1") - .context(ctx -> ctx.put("custom-key-2", "custom-value-2")) - .build(); + .registeredClient(registeredClient) + .principal(principal) + .authorization(authorization) + .tokenType(OAuth2TokenType.ACCESS_TOKEN) + .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) + .authorizationGrant(authorizationGrant) + .put("custom-key-1", "custom-value-1") + .context(ctx -> ctx.put("custom-key-2", "custom-value-2")) + .build(); assertThat(context.getJwsHeader()).isEqualTo(headers); assertThat(context.getClaims()).isEqualTo(claims); @@ -112,7 +104,8 @@ public class JwtEncodingContextTests { assertThat(context.getAuthorization()).isEqualTo(authorization); assertThat(context.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(context.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(context.getAuthorizationGrant()).isEqualTo(authorizationGrant); + assertThat(context.getAuthorizationGrant()) + .isEqualTo(authorizationGrant); assertThat(context.get("custom-key-1")).isEqualTo("custom-value-1"); assertThat(context.get("custom-key-2")).isEqualTo("custom-value-2"); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java index 07deda57..c457cf7d 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/JwtGeneratorTests.java @@ -67,10 +67,15 @@ import static org.mockito.Mockito.verify; * @author Joe Grandja */ public class JwtGeneratorTests { + private static final OAuth2TokenType ID_TOKEN_TOKEN_TYPE = new OAuth2TokenType(OidcParameterNames.ID_TOKEN); + private JwtEncoder jwtEncoder; + private OAuth2TokenCustomizer jwtCustomizer; + private JwtGenerator jwtGenerator; + private TestAuthorizationServerContext authorizationServerContext; @BeforeEach @@ -79,22 +84,22 @@ public class JwtGeneratorTests { this.jwtCustomizer = mock(OAuth2TokenCustomizer.class); this.jwtGenerator = new JwtGenerator(this.jwtEncoder); this.jwtGenerator.setJwtCustomizer(this.jwtCustomizer); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); this.authorizationServerContext = new TestAuthorizationServerContext(authorizationServerSettings, null); } @Test public void constructorWhenJwtEncoderNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new JwtGenerator(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwtEncoder cannot be null"); + assertThatThrownBy(() -> new JwtGenerator(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwtEncoder cannot be null"); } @Test public void setJwtCustomizerWhenNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> this.jwtGenerator.setJwtCustomizer(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwtCustomizer cannot be null"); + assertThatThrownBy(() -> this.jwtGenerator.setJwtCustomizer(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwtCustomizer cannot be null"); } @Test @@ -131,12 +136,12 @@ public class JwtGeneratorTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken("code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); // @formatter:off OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder() @@ -157,24 +162,25 @@ public class JwtGeneratorTests { @Test public void generateWhenIdTokenTypeAndAuthorizationCodeGrantThenReturnJwt() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scope(OidcScopes.OPENID) - .tokenSettings(TokenSettings.builder().idTokenSignatureAlgorithm(SignatureAlgorithm.ES256).build()) - .build(); + .scope(OidcScopes.OPENID) + .tokenSettings(TokenSettings.builder().idTokenSignatureAlgorithm(SignatureAlgorithm.ES256).build()) + .build(); Map authenticationRequestAdditionalParameters = new HashMap<>(); authenticationRequestAdditionalParameters.put(OidcParameterNames.NONCE, "nonce"); - OAuth2Authorization authorization = TestOAuth2Authorizations.authorization( - registeredClient, authenticationRequestAdditionalParameters).build(); + OAuth2Authorization authorization = TestOAuth2Authorizations + .authorization(registeredClient, authenticationRequestAdditionalParameters) + .build(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken("code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); Authentication principal = authorization.getAttribute(Principal.class.getName()); - SessionInformation sessionInformation = new SessionInformation( - principal.getPrincipal(), "session1", Date.from(Instant.now().minus(2, ChronoUnit.HOURS))); + SessionInformation sessionInformation = new SessionInformation(principal.getPrincipal(), "session1", + Date.from(Instant.now().minus(2, ChronoUnit.HOURS))); // @formatter:off OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder() @@ -196,24 +202,22 @@ public class JwtGeneratorTests { // gh-1224 @Test public void generateWhenIdTokenTypeAndRefreshTokenGrantThenReturnJwt() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scope(OidcScopes.OPENID) - .build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject("subject") - .issuedAt(Instant.now()) - .expiresAt(Instant.now().plusSeconds(60)) - .claim("sid", "sessionId-1234") - .claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now())) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject("subject") + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(60)) + .claim("sid", "sessionId-1234") + .claim(IdTokenClaimNames.AUTH_TIME, Date.from(Instant.now())) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(idToken) - .build(); + .token(idToken) + .build(); OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( refreshToken.getTokenValue(), clientPrincipal, null, null); @@ -239,22 +243,20 @@ public class JwtGeneratorTests { // gh-1283 @Test public void generateWhenIdTokenTypeWithoutSidAndRefreshTokenGrantThenReturnJwt() { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scope(OidcScopes.OPENID) - .build(); - OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") - .issuer("https://provider.com") - .subject("subject") - .issuedAt(Instant.now()) - .expiresAt(Instant.now().plusSeconds(60)) - .build(); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scope(OidcScopes.OPENID).build(); + OidcIdToken idToken = OidcIdToken.withTokenValue("id-token") + .issuer("https://provider.com") + .subject("subject") + .issuedAt(Instant.now()) + .expiresAt(Instant.now().plusSeconds(60)) + .build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient) - .token(idToken) - .build(); + .token(idToken) + .build(); OAuth2RefreshToken refreshToken = authorization.getRefreshToken().getToken(); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); OAuth2RefreshTokenAuthenticationToken authentication = new OAuth2RefreshTokenAuthenticationToken( refreshToken.getTokenValue(), clientPrincipal, null, null); @@ -292,20 +294,25 @@ public class JwtGeneratorTests { assertThat(jwtEncodingContext.getAuthorizedScopes()).isEqualTo(tokenContext.getAuthorizedScopes()); assertThat(jwtEncodingContext.getTokenType()).isEqualTo(tokenContext.getTokenType()); assertThat(jwtEncodingContext.getAuthorizationGrantType()).isEqualTo(tokenContext.getAuthorizationGrantType()); - assertThat(jwtEncodingContext.getAuthorizationGrant()).isEqualTo(tokenContext.getAuthorizationGrant()); + assertThat(jwtEncodingContext.getAuthorizationGrant()) + .isEqualTo(tokenContext.getAuthorizationGrant()); - ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor.forClass(JwtEncoderParameters.class); + ArgumentCaptor jwtEncoderParametersCaptor = ArgumentCaptor + .forClass(JwtEncoderParameters.class); verify(this.jwtEncoder).encode(jwtEncoderParametersCaptor.capture()); JwsHeader jwsHeader = jwtEncoderParametersCaptor.getValue().getJwsHeader(); if (OidcParameterNames.ID_TOKEN.equals(tokenContext.getTokenType().getValue())) { - assertThat(jwsHeader.getAlgorithm()).isEqualTo(tokenContext.getRegisteredClient().getTokenSettings().getIdTokenSignatureAlgorithm()); - } else { + assertThat(jwsHeader.getAlgorithm()) + .isEqualTo(tokenContext.getRegisteredClient().getTokenSettings().getIdTokenSignatureAlgorithm()); + } + else { assertThat(jwsHeader.getAlgorithm()).isEqualTo(SignatureAlgorithm.RS256); } JwtClaimsSet jwtClaimsSet = jwtEncoderParametersCaptor.getValue().getClaims(); - assertThat(jwtClaimsSet.getIssuer().toExternalForm()).isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer()); + assertThat(jwtClaimsSet.getIssuer().toExternalForm()) + .isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer()); assertThat(jwtClaimsSet.getSubject()).isEqualTo(tokenContext.getAuthorization().getPrincipalName()); assertThat(jwtClaimsSet.getAudience()).containsExactly(tokenContext.getRegisteredClient().getClientId()); @@ -313,7 +320,8 @@ public class JwtGeneratorTests { Instant expiresAt; if (tokenContext.getTokenType().equals(OAuth2TokenType.ACCESS_TOKEN)) { expiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive()); - } else { + } + else { expiresAt = issuedAt.plus(30, ChronoUnit.MINUTES); } assertThat(jwtClaimsSet.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1)); @@ -325,21 +333,26 @@ public class JwtGeneratorTests { Set scopes = jwtClaimsSet.getClaim(OAuth2ParameterNames.SCOPE); assertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes()); - } else { - assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AZP)).isEqualTo(tokenContext.getRegisteredClient().getClientId()); + } + else { + assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AZP)) + .isEqualTo(tokenContext.getRegisteredClient().getClientId()); if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.AUTHORIZATION_CODE)) { - OAuth2AuthorizationRequest authorizationRequest = tokenContext.getAuthorization().getAttribute( - OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationRequest authorizationRequest = tokenContext.getAuthorization() + .getAttribute(OAuth2AuthorizationRequest.class.getName()); String nonce = (String) authorizationRequest.getAdditionalParameters().get(OidcParameterNames.NONCE); assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.NONCE)).isEqualTo(nonce); SessionInformation sessionInformation = tokenContext.get(SessionInformation.class); assertThat(jwtClaimsSet.getClaim("sid")).isEqualTo(sessionInformation.getSessionId()); - assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AUTH_TIME)).isEqualTo(sessionInformation.getLastRequest()); - } else if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.REFRESH_TOKEN)) { + assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AUTH_TIME)) + .isEqualTo(sessionInformation.getLastRequest()); + } + else if (tokenContext.getAuthorizationGrantType().equals(AuthorizationGrantType.REFRESH_TOKEN)) { OidcIdToken currentIdToken = tokenContext.getAuthorization().getToken(OidcIdToken.class).getToken(); assertThat(jwtClaimsSet.getClaim("sid")).isEqualTo(currentIdToken.getClaim("sid")); - assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AUTH_TIME)).isEqualTo(currentIdToken.getClaim(IdTokenClaimNames.AUTH_TIME)); + assertThat(jwtClaimsSet.getClaim(IdTokenClaimNames.AUTH_TIME)) + .isEqualTo(currentIdToken.getClaim(IdTokenClaimNames.AUTH_TIME)); } } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGeneratorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGeneratorTests.java index 9b23496f..227c2784 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGeneratorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2AccessTokenGeneratorTests.java @@ -55,8 +55,11 @@ import static org.mockito.Mockito.verify; * @author Joe Grandja */ public class OAuth2AccessTokenGeneratorTests { + private OAuth2TokenCustomizer accessTokenCustomizer; + private OAuth2AccessTokenGenerator accessTokenGenerator; + private AuthorizationServerContext authorizationServerContext; @BeforeEach @@ -64,15 +67,17 @@ public class OAuth2AccessTokenGeneratorTests { this.accessTokenCustomizer = mock(OAuth2TokenCustomizer.class); this.accessTokenGenerator = new OAuth2AccessTokenGenerator(); this.accessTokenGenerator.setAccessTokenCustomizer(this.accessTokenCustomizer); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer("https://provider.com").build(); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer("https://provider.com") + .build(); this.authorizationServerContext = new TestAuthorizationServerContext(authorizationServerSettings, null); } @Test public void setAccessTokenCustomizerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.accessTokenGenerator.setAccessTokenCustomizer(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("accessTokenCustomizer cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("accessTokenCustomizer cannot be null"); } @Test @@ -124,12 +129,12 @@ public class OAuth2AccessTokenGeneratorTests { OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); Authentication principal = authorization.getAttribute(Principal.class.getName()); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authentication = - new OAuth2AuthorizationCodeAuthenticationToken("code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authentication = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); // @formatter:off OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder() @@ -148,7 +153,8 @@ public class OAuth2AccessTokenGeneratorTests { assertThat(accessToken).isNotNull(); Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive()); + Instant expiresAt = issuedAt + .plus(tokenContext.getRegisteredClient().getTokenSettings().getAccessTokenTimeToLive()); assertThat(accessToken.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1)); assertThat(accessToken.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1)); assertThat(accessToken.getScopes()).isEqualTo(tokenContext.getAuthorizedScopes()); @@ -157,10 +163,11 @@ public class OAuth2AccessTokenGeneratorTests { OAuth2TokenClaimAccessor accessTokenClaims = ((ClaimAccessor) accessToken)::getClaims; assertThat(accessTokenClaims.getClaims()).isNotEmpty(); - assertThat(accessTokenClaims.getIssuer().toExternalForm()).isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer()); + assertThat(accessTokenClaims.getIssuer().toExternalForm()) + .isEqualTo(tokenContext.getAuthorizationServerContext().getIssuer()); assertThat(accessTokenClaims.getSubject()).isEqualTo(tokenContext.getPrincipal().getName()); - assertThat(accessTokenClaims.getAudience()).isEqualTo( - Collections.singletonList(tokenContext.getRegisteredClient().getClientId())); + assertThat(accessTokenClaims.getAudience()) + .isEqualTo(Collections.singletonList(tokenContext.getRegisteredClient().getClientId())); assertThat(accessTokenClaims.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1)); assertThat(accessTokenClaims.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1)); assertThat(accessTokenClaims.getNotBefore()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1)); @@ -169,19 +176,22 @@ public class OAuth2AccessTokenGeneratorTests { Set scopes = accessTokenClaims.getClaim(OAuth2ParameterNames.SCOPE); assertThat(scopes).isEqualTo(tokenContext.getAuthorizedScopes()); - ArgumentCaptor tokenClaimsContextCaptor = ArgumentCaptor.forClass(OAuth2TokenClaimsContext.class); + ArgumentCaptor tokenClaimsContextCaptor = ArgumentCaptor + .forClass(OAuth2TokenClaimsContext.class); verify(this.accessTokenCustomizer).customize(tokenClaimsContextCaptor.capture()); OAuth2TokenClaimsContext tokenClaimsContext = tokenClaimsContextCaptor.getValue(); assertThat(tokenClaimsContext.getClaims()).isNotNull(); assertThat(tokenClaimsContext.getRegisteredClient()).isEqualTo(tokenContext.getRegisteredClient()); assertThat(tokenClaimsContext.getPrincipal()).isEqualTo(tokenContext.getPrincipal()); - assertThat(tokenClaimsContext.getAuthorizationServerContext()).isEqualTo(tokenContext.getAuthorizationServerContext()); + assertThat(tokenClaimsContext.getAuthorizationServerContext()) + .isEqualTo(tokenContext.getAuthorizationServerContext()); assertThat(tokenClaimsContext.getAuthorization()).isEqualTo(tokenContext.getAuthorization()); assertThat(tokenClaimsContext.getAuthorizedScopes()).isEqualTo(tokenContext.getAuthorizedScopes()); assertThat(tokenClaimsContext.getTokenType()).isEqualTo(tokenContext.getTokenType()); assertThat(tokenClaimsContext.getAuthorizationGrantType()).isEqualTo(tokenContext.getAuthorizationGrantType()); - assertThat(tokenClaimsContext.getAuthorizationGrant()).isEqualTo(tokenContext.getAuthorizationGrant()); + assertThat(tokenClaimsContext.getAuthorizationGrant()) + .isEqualTo(tokenContext.getAuthorizationGrant()); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGeneratorTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGeneratorTests.java index 96fb3e5f..ac32db3e 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGeneratorTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2RefreshTokenGeneratorTests.java @@ -32,6 +32,7 @@ import static org.assertj.core.api.Assertions.assertThat; * @author Joe Grandja */ public class OAuth2RefreshTokenGeneratorTests { + private final OAuth2RefreshTokenGenerator tokenGenerator = new OAuth2RefreshTokenGenerator(); @Test @@ -60,7 +61,8 @@ public class OAuth2RefreshTokenGeneratorTests { assertThat(refreshToken).isNotNull(); Instant issuedAt = Instant.now(); - Instant expiresAt = issuedAt.plus(tokenContext.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive()); + Instant expiresAt = issuedAt + .plus(tokenContext.getRegisteredClient().getTokenSettings().getRefreshTokenTimeToLive()); assertThat(refreshToken.getIssuedAt()).isBetween(issuedAt.minusSeconds(1), issuedAt.plusSeconds(1)); assertThat(refreshToken.getExpiresAt()).isBetween(expiresAt.minusSeconds(1), expiresAt.plusSeconds(1)); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContextTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContextTests.java index 9b131fc0..78ff3dfa 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContextTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsContextTests.java @@ -50,9 +50,8 @@ public class OAuth2TokenClaimsContextTests { @Test public void withWhenClaimsNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> OAuth2TokenClaimsContext.with(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("claimsBuilder cannot be null"); + assertThatThrownBy(() -> OAuth2TokenClaimsContext.with(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("claimsBuilder cannot be null"); } @Test @@ -75,15 +74,17 @@ public class OAuth2TokenClaimsContextTests { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(registeredClient).build(); Authentication principal = authorization.getAttribute(Principal.class.getName()); - AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder().issuer(issuer).build(); - AuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext(authorizationServerSettings, null); - OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationRequest authorizationRequest = authorization.getAttribute( - OAuth2AuthorizationRequest.class.getName()); - OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = - new OAuth2AuthorizationCodeAuthenticationToken( - "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); + AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() + .issuer(issuer) + .build(); + AuthorizationServerContext authorizationServerContext = new TestAuthorizationServerContext( + authorizationServerSettings, null); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AuthorizationRequest authorizationRequest = authorization + .getAttribute(OAuth2AuthorizationRequest.class.getName()); + OAuth2AuthorizationCodeAuthenticationToken authorizationGrant = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, authorizationRequest.getRedirectUri(), null); // @formatter:off OAuth2TokenClaimsContext context = OAuth2TokenClaimsContext.with(claims) @@ -106,7 +107,8 @@ public class OAuth2TokenClaimsContextTests { assertThat(context.getAuthorization()).isEqualTo(authorization); assertThat(context.getTokenType()).isEqualTo(OAuth2TokenType.ACCESS_TOKEN); assertThat(context.getAuthorizationGrantType()).isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE); - assertThat(context.getAuthorizationGrant()).isEqualTo(authorizationGrant); + assertThat(context.getAuthorizationGrant()) + .isEqualTo(authorizationGrant); assertThat(context.get("custom-key-1")).isEqualTo("custom-value-1"); assertThat(context.get("custom-key-2")).isEqualTo("custom-value-2"); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSetTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSetTests.java index 6ed96282..e51616dc 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSetTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/token/OAuth2TokenClaimsSetTests.java @@ -33,9 +33,8 @@ public class OAuth2TokenClaimsSetTests { @Test public void buildWhenClaimsEmptyThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> OAuth2TokenClaimsSet.builder().build()) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("claims cannot be empty"); + assertThatThrownBy(() -> OAuth2TokenClaimsSet.builder().build()).isInstanceOf(IllegalArgumentException.class) + .hasMessage("claims cannot be empty"); } @Test @@ -83,15 +82,15 @@ public class OAuth2TokenClaimsSetTests { @Test public void claimWhenNameNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OAuth2TokenClaimsSet.builder().claim(null, "value")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("name cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("name cannot be empty"); } @Test public void claimWhenValueNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> OAuth2TokenClaimsSet.builder().claim("name", null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("value cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("value cannot be null"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilterTests.java index 88ae700f..baf17936 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/NimbusJwkSetEndpointFilterTests.java @@ -51,9 +51,13 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Joe Grandja */ public class NimbusJwkSetEndpointFilterTests { + private static final String DEFAULT_JWK_SET_ENDPOINT_URI = "/oauth2/jwks"; + private List jwkList; + private JWKSource jwkSource; + private NimbusJwkSetEndpointFilter filter; @BeforeEach @@ -65,16 +69,15 @@ public class NimbusJwkSetEndpointFilterTests { @Test public void constructorWhenJwkSourceNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> new NimbusJwkSetEndpointFilter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwkSource cannot be null"); + assertThatThrownBy(() -> new NimbusJwkSetEndpointFilter(null)).isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwkSource cannot be null"); } @Test public void constructorWhenJwkSetEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new NimbusJwkSetEndpointFilter(this.jwkSource, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("jwkSetEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("jwkSetEndpointUri cannot be empty"); } @Test @@ -158,4 +161,5 @@ public class NimbusJwkSetEndpointFilterTests { JWKSet jwkSet = JWKSet.parse(response.getContentAsString()); assertThat(jwkSet.getKeys()).isEmpty(); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java index 9419de80..8fc31afd 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationEndpointFilterTests.java @@ -84,13 +84,21 @@ import static org.mockito.Mockito.when; * @since 0.0.1 */ public class OAuth2AuthorizationEndpointFilterTests { + private static final String DEFAULT_AUTHORIZATION_ENDPOINT_URI = "/oauth2/authorize"; + private static final String AUTHORIZATION_URI = "https://provider.com/oauth2/authorize"; + private static final String STATE = "state"; + private static final String REMOTE_ADDRESS = "remote-address"; + private AuthenticationManager authenticationManager; + private OAuth2AuthorizationEndpointFilter filter; + private TestingAuthenticationToken principal; + private OAuth2AuthorizationCode authorizationCode; @BeforeEach @@ -115,50 +123,50 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2AuthorizationEndpointFilter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenAuthorizationEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2AuthorizationEndpointFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationEndpointUri cannot be empty"); } @Test public void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationDetailsSource(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationDetailsSource cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationDetailsSource cannot be null"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test public void setSessionAuthenticationStrategyWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setSessionAuthenticationStrategy(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("sessionAuthenticationStrategy cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("sessionAuthenticationStrategy cannot be null"); } @Test @@ -176,11 +184,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMissingResponseTypeThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.RESPONSE_TYPE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.removeParameter(OAuth2ParameterNames.RESPONSE_TYPE); updateQueryString(request); }); @@ -188,11 +193,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleResponseTypeThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.RESPONSE_TYPE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(OAuth2ParameterNames.RESPONSE_TYPE, "id_token"); updateQueryString(request); }); @@ -200,11 +202,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestInvalidResponseTypeThenUnsupportedResponseTypeError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.RESPONSE_TYPE, - OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.RESPONSE_TYPE, OAuth2ErrorCodes.UNSUPPORTED_RESPONSE_TYPE, request -> { request.setParameter(OAuth2ParameterNames.RESPONSE_TYPE, "id_token"); updateQueryString(request); }); @@ -212,11 +211,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMissingClientIdThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.CLIENT_ID, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.CLIENT_ID, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.removeParameter(OAuth2ParameterNames.CLIENT_ID); updateQueryString(request); }); @@ -224,11 +220,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleClientIdThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.CLIENT_ID, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.CLIENT_ID, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-2"); updateQueryString(request); }); @@ -236,11 +229,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleRedirectUriThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.REDIRECT_URI, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.REDIRECT_URI, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "https://example2.com"); updateQueryString(request); }); @@ -248,11 +238,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleScopeThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.SCOPE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(OAuth2ParameterNames.SCOPE, "scope2"); updateQueryString(request); }); @@ -260,11 +247,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleStateThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.STATE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + OAuth2ParameterNames.STATE, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(OAuth2ParameterNames.STATE, "state2"); updateQueryString(request); }); @@ -273,28 +257,22 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationConsentRequestMissingStateThenInvalidRequestError() throws Exception { doFilterWhenAuthorizationConsentRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.STATE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> request.removeParameter(OAuth2ParameterNames.STATE)); + TestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.STATE, + OAuth2ErrorCodes.INVALID_REQUEST, request -> request.removeParameter(OAuth2ParameterNames.STATE)); } @Test public void doFilterWhenAuthorizationConsentRequestMultipleStateThenInvalidRequestError() throws Exception { doFilterWhenAuthorizationConsentRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - OAuth2ParameterNames.STATE, + TestRegisteredClients.registeredClient().build(), OAuth2ParameterNames.STATE, OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter(OAuth2ParameterNames.STATE, "state2")); } @Test public void doFilterWhenAuthorizationRequestMultipleCodeChallengeThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - PkceParameterNames.CODE_CHALLENGE, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + PkceParameterNames.CODE_CHALLENGE, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(PkceParameterNames.CODE_CHALLENGE, "code-challenge"); request.addParameter(PkceParameterNames.CODE_CHALLENGE, "another-code-challenge"); updateQueryString(request); @@ -303,11 +281,8 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestMultipleCodeChallengeMethodThenInvalidRequestError() throws Exception { - doFilterWhenAuthorizationRequestInvalidParameterThenError( - TestRegisteredClients.registeredClient().build(), - PkceParameterNames.CODE_CHALLENGE_METHOD, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> { + doFilterWhenAuthorizationRequestInvalidParameterThenError(TestRegisteredClients.registeredClient().build(), + PkceParameterNames.CODE_CHALLENGE_METHOD, OAuth2ErrorCodes.INVALID_REQUEST, request -> { request.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); request.addParameter(PkceParameterNames.CODE_CHALLENGE_METHOD, "S256"); updateQueryString(request); @@ -316,19 +291,18 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestAuthenticationExceptionThenErrorResponse() throws Exception { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUris(redirectUris -> { - redirectUris.clear(); - redirectUris.add("https://example.com?param=encoded%20parameter%20value"); - }) - .build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - registeredClient.getRedirectUris().iterator().next(), "client state", registeredClient.getScopes(), null); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris(redirectUris -> { + redirectUris.clear(); + redirectUris.add("https://example.com?param=encoded%20parameter%20value"); + }).build(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, + registeredClient.getRedirectUris().iterator().next(), "client state", registeredClient.getScopes(), + null); OAuth2Error error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST, "error description", "error uri"); when(this.authenticationManager.authenticate(any())) - .thenThrow(new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthentication)); + .thenThrow(new OAuth2AuthorizationCodeRequestAuthenticationException(error, + authorizationCodeRequestAuthentication)); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -348,17 +322,15 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(authorizationCodeRequestAuthentication); this.filter.setAuthenticationConverter(authenticationConverter); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthentication); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -374,13 +346,11 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); @@ -393,21 +363,20 @@ public class OAuth2AuthorizationEndpointFilterTests { verify(this.authenticationManager).authenticate(any()); verifyNoInteractions(filterChain); - verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), same(authorizationCodeRequestAuthenticationResult)); + verify(authenticationSuccessHandler).onAuthenticationSuccess(any(), any(), + same(authorizationCodeRequestAuthenticationResult)); } @Test public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); OAuth2Error error = new OAuth2Error("errorCode", "errorDescription", "errorUri"); - OAuth2AuthorizationCodeRequestAuthenticationException authenticationException = - new OAuth2AuthorizationCodeRequestAuthenticationException(error, authorizationCodeRequestAuthentication); - when(this.authenticationManager.authenticate(any())) - .thenThrow(authenticationException); + OAuth2AuthorizationCodeRequestAuthenticationException authenticationException = new OAuth2AuthorizationCodeRequestAuthenticationException( + error, authorizationCodeRequestAuthentication); + when(this.authenticationManager.authenticate(any())).thenThrow(authenticationException); AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class); this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); @@ -426,13 +395,11 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenCustomSessionAuthenticationStrategyThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); SessionAuthenticationStrategy sessionAuthenticationStrategy = mock(SessionAuthenticationStrategy.class); this.filter.setSessionAuthenticationStrategy(sessionAuthenticationStrategy); @@ -445,26 +412,25 @@ public class OAuth2AuthorizationEndpointFilterTests { verify(this.authenticationManager).authenticate(any()); verifyNoInteractions(filterChain); - verify(sessionAuthenticationStrategy).onAuthentication(same(authorizationCodeRequestAuthenticationResult), any(), any()); + verify(sessionAuthenticationStrategy).onAuthentication(same(authorizationCodeRequestAuthenticationResult), + any(), any()); } @Test public void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthentication = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); - AuthenticationDetailsSource authenticationDetailsSource = - mock(AuthenticationDetailsSource.class); + AuthenticationDetailsSource authenticationDetailsSource = mock( + AuthenticationDetailsSource.class); WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request); when(authenticationDetailsSource.buildDetails(request)).thenReturn(webAuthenticationDetails); this.filter.setAuthenticationDetailsSource(authenticationDetailsSource); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthentication); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthentication); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -480,13 +446,11 @@ public class OAuth2AuthorizationEndpointFilterTests { public void doFilterWhenAuthorizationRequestPrincipalNotAuthenticatedThenCommenceAuthentication() throws Exception { this.principal.setAuthenticated(false); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes(), null); authorizationCodeRequestAuthenticationResult.setAuthenticated(false); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -499,21 +463,18 @@ public class OAuth2AuthorizationEndpointFilterTests { } @Test - public void doFilterWhenAuthorizationRequestConsentRequiredWithCustomConsentUriThenRedirectConsentResponse() throws Exception { + public void doFilterWhenAuthorizationRequestConsentRequiredWithCustomConsentUriThenRedirectConsentResponse() + throws Exception { Set requestedScopes = new HashSet<>(Arrays.asList("scope1", "scope2")); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.addAll(requestedScopes); - }) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, new HashSet<>(), null); // No scopes previously approved + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.addAll(requestedScopes); + }).build(); + // No scopes previously approved + OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, new HashSet<>(), null); authorizationConsentAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationConsentAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationConsentAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -526,25 +487,22 @@ public class OAuth2AuthorizationEndpointFilterTests { verifyNoInteractions(filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); - assertThat(response.getRedirectedUrl()).isEqualTo("http://localhost/oauth2/custom-consent?scope=scope1%20scope2&client_id=client-1&state=state"); + assertThat(response.getRedirectedUrl()) + .isEqualTo("http://localhost/oauth2/custom-consent?scope=scope1%20scope2&client_id=client-1&state=state"); } @Test public void doFilterWhenAuthorizationRequestConsentRequiredThenConsentResponse() throws Exception { Set requestedScopes = new HashSet<>(Arrays.asList("scope1", "scope2")); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.addAll(requestedScopes); - }) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, new HashSet<>(), null); // No scopes previously approved + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.addAll(requestedScopes); + }).build(); + // No scopes previously approved + OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, new HashSet<>(), null); authorizationConsentAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationConsentAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationConsentAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -563,23 +521,19 @@ public class OAuth2AuthorizationEndpointFilterTests { } @Test - public void doFilterWhenAuthorizationRequestConsentRequiredWithPreviouslyApprovedThenConsentResponse() throws Exception { + public void doFilterWhenAuthorizationRequestConsentRequiredWithPreviouslyApprovedThenConsentResponse() + throws Exception { Set approvedScopes = new HashSet<>(Arrays.asList("scope1", "scope2")); Set requestedScopes = new HashSet<>(Arrays.asList("scope3", "scope4")); - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.addAll(approvedScopes); - scopes.addAll(requestedScopes); - }) - .build(); - OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = - new OAuth2AuthorizationConsentAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, - STATE, approvedScopes, null); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.addAll(approvedScopes); + scopes.addAll(requestedScopes); + }).build(); + OAuth2AuthorizationConsentAuthenticationToken authorizationConsentAuthenticationResult = new OAuth2AuthorizationConsentAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, STATE, approvedScopes, null); authorizationConsentAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationConsentAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationConsentAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -602,19 +556,15 @@ public class OAuth2AuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthorizationRequestAuthenticatedThenAuthorizationResponse() throws Exception { - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .redirectUris(redirectUris -> { - redirectUris.clear(); - redirectUris.add("https://example.com?param=encoded%20parameter%20value"); - }) - .build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, - registeredClient.getRedirectUris().iterator().next(), "client state", registeredClient.getScopes()); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().redirectUris(redirectUris -> { + redirectUris.clear(); + redirectUris.add("https://example.com?param=encoded%20parameter%20value"); + }).build(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, + registeredClient.getRedirectUris().iterator().next(), "client state", registeredClient.getScopes()); authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); request.addParameter("custom-param", "custom-value-1", "custom-value-2"); @@ -625,45 +575,41 @@ public class OAuth2AuthorizationEndpointFilterTests { this.filter.doFilter(request, response, filterChain); - ArgumentCaptor authorizationCodeRequestAuthenticationCaptor = - ArgumentCaptor.forClass(OAuth2AuthorizationCodeRequestAuthenticationToken.class); + ArgumentCaptor authorizationCodeRequestAuthenticationCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationCodeRequestAuthenticationToken.class); verify(this.authenticationManager).authenticate(authorizationCodeRequestAuthenticationCaptor.capture()); verifyNoInteractions(filterChain); assertThat(authorizationCodeRequestAuthenticationCaptor.getValue().getDetails()) - .asInstanceOf(type(WebAuthenticationDetails.class)) - .extracting(WebAuthenticationDetails::getRemoteAddress) - .isEqualTo(REMOTE_ADDRESS); + .asInstanceOf(type(WebAuthenticationDetails.class)) + .extracting(WebAuthenticationDetails::getRemoteAddress) + .isEqualTo(REMOTE_ADDRESS); // Assert that multi-valued request parameters are preserved assertThat(authorizationCodeRequestAuthenticationCaptor.getValue().getAdditionalParameters()) - .extracting(params -> params.get("custom-param")) - .asInstanceOf(type(String[].class)) - .isEqualTo(new String[] { "custom-value-1", "custom-value-2" }); + .extracting(params -> params.get("custom-param")) + .asInstanceOf(type(String[].class)) + .isEqualTo(new String[] { "custom-value-1", "custom-value-2" }); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); - assertThat(response.getRedirectedUrl()).isEqualTo( - "https://example.com?param=encoded%20parameter%20value&code=code&state=client%20state"); + assertThat(response.getRedirectedUrl()) + .isEqualTo("https://example.com?param=encoded%20parameter%20value&code=code&state=client%20state"); } @Test public void doFilterWhenAuthenticationRequestAuthenticatedThenAuthorizationResponse() throws Exception { // Setup OpenID Connect request - RegisteredClient registeredClient = TestRegisteredClients.registeredClient() - .scopes(scopes -> { - scopes.clear(); - scopes.add(OidcScopes.OPENID); - }) - .build(); - OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2AuthorizationCodeRequestAuthenticationToken( - AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, - registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); + RegisteredClient registeredClient = TestRegisteredClients.registeredClient().scopes(scopes -> { + scopes.clear(); + scopes.add(OidcScopes.OPENID); + }).build(); + OAuth2AuthorizationCodeRequestAuthenticationToken authorizationCodeRequestAuthenticationResult = new OAuth2AuthorizationCodeRequestAuthenticationToken( + AUTHORIZATION_URI, registeredClient.getClientId(), principal, this.authorizationCode, + registeredClient.getRedirectUris().iterator().next(), STATE, registeredClient.getScopes()); authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - when(this.authenticationManager.authenticate(any())) - .thenReturn(authorizationCodeRequestAuthenticationResult); + when(this.authenticationManager.authenticate(any())).thenReturn(authorizationCodeRequestAuthenticationResult); MockHttpServletRequest request = createAuthorizationRequest(registeredClient); - request.setMethod("POST"); // OpenID Connect supports POST method + request.setMethod("POST"); // OpenID Connect supports POST method request.setQueryString(null); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -674,26 +620,26 @@ public class OAuth2AuthorizationEndpointFilterTests { verifyNoInteractions(filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); - assertThat(response.getRedirectedUrl()).isEqualTo( - request.getParameter(OAuth2ParameterNames.REDIRECT_URI) + "?code=code&state=state"); + assertThat(response.getRedirectedUrl()) + .isEqualTo(request.getParameter(OAuth2ParameterNames.REDIRECT_URI) + "?code=code&state=state"); } private void doFilterWhenAuthorizationRequestInvalidParameterThenError(RegisteredClient registeredClient, String parameterName, String errorCode, Consumer requestConsumer) throws Exception { - doFilterWhenRequestInvalidParameterThenError(createAuthorizationRequest(registeredClient), - parameterName, errorCode, requestConsumer); + doFilterWhenRequestInvalidParameterThenError(createAuthorizationRequest(registeredClient), parameterName, + errorCode, requestConsumer); } private void doFilterWhenAuthorizationConsentRequestInvalidParameterThenError(RegisteredClient registeredClient, String parameterName, String errorCode, Consumer requestConsumer) throws Exception { - doFilterWhenRequestInvalidParameterThenError(createAuthorizationConsentRequest(registeredClient), - parameterName, errorCode, requestConsumer); + doFilterWhenRequestInvalidParameterThenError(createAuthorizationConsentRequest(registeredClient), parameterName, + errorCode, requestConsumer); } - private void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, - String parameterName, String errorCode, Consumer requestConsumer) throws Exception { + private void doFilterWhenRequestInvalidParameterThenError(MockHttpServletRequest request, String parameterName, + String errorCode, Consumer requestConsumer) throws Exception { requestConsumer.accept(request); MockHttpServletResponse response = new MockHttpServletResponse(); @@ -751,16 +697,13 @@ public class OAuth2AuthorizationEndpointFilterTests { private static String scopeCheckbox(String scope) { return MessageFormat.format( - "", - scope - ); + "", scope); } private static String disabledScopeCheckbox(String scope) { return MessageFormat.format( "", - scope - ); + scope); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java index 5c0b0968..f3e5dbc0 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2AuthorizationServerMetadataEndpointFilterTests.java @@ -44,7 +44,9 @@ import static org.mockito.Mockito.verifyNoInteractions; * @author Joe Grandja */ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { + private static final String DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI = "/.well-known/oauth-authorization-server"; + private final OAuth2AuthorizationServerMetadataEndpointFilter filter = new OAuth2AuthorizationServerMetadataEndpointFilter(); @AfterEach @@ -55,8 +57,8 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { @Test public void setAuthorizationServerMetadataCustomizerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthorizationServerMetadataCustomizer(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authorizationServerMetadataCustomizer cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authorizationServerMetadataCustomizer cannot be null"); } @Test @@ -95,14 +97,15 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { String tokenIntrospectionEndpoint = "/oauth2/v1/introspect"; AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .issuer(issuer) - .authorizationEndpoint(authorizationEndpoint) - .tokenEndpoint(tokenEndpoint) - .jwkSetEndpoint(jwkSetEndpoint) - .tokenRevocationEndpoint(tokenRevocationEndpoint) - .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) - .build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + .issuer(issuer) + .authorizationEndpoint(authorizationEndpoint) + .tokenEndpoint(tokenEndpoint) + .jwkSetEndpoint(jwkSetEndpoint) + .tokenRevocationEndpoint(tokenRevocationEndpoint) + .tokenIntrospectionEndpoint(tokenIntrospectionEndpoint) + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); String requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -117,25 +120,34 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { assertThat(response.getContentType()).isEqualTo(MediaType.APPLICATION_JSON_VALUE); String authorizationServerMetadataResponse = response.getContentAsString(); assertThat(authorizationServerMetadataResponse).contains("\"issuer\":\"https://example.com\""); - assertThat(authorizationServerMetadataResponse).contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\""); - assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\""); - assertThat(authorizationServerMetadataResponse).contains("\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"authorization_endpoint\":\"https://example.com/oauth2/v1/authorize\""); + assertThat(authorizationServerMetadataResponse) + .contains("\"token_endpoint\":\"https://example.com/oauth2/v1/token\""); + assertThat(authorizationServerMetadataResponse).contains( + "\"token_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); assertThat(authorizationServerMetadataResponse).contains("\"jwks_uri\":\"https://example.com/oauth2/v1/jwks\""); assertThat(authorizationServerMetadataResponse).contains("\"response_types_supported\":[\"code\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\""); - assertThat(authorizationServerMetadataResponse).contains("\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); - assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\""); - assertThat(authorizationServerMetadataResponse).contains("\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); + assertThat(authorizationServerMetadataResponse).contains( + "\"grant_types_supported\":[\"authorization_code\",\"client_credentials\",\"refresh_token\",\"urn:ietf:params:oauth:grant-type:device_code\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"revocation_endpoint\":\"https://example.com/oauth2/v1/revoke\""); + assertThat(authorizationServerMetadataResponse).contains( + "\"revocation_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); + assertThat(authorizationServerMetadataResponse) + .contains("\"introspection_endpoint\":\"https://example.com/oauth2/v1/introspect\""); + assertThat(authorizationServerMetadataResponse).contains( + "\"introspection_endpoint_auth_methods_supported\":[\"client_secret_basic\",\"client_secret_post\",\"client_secret_jwt\",\"private_key_jwt\"]"); assertThat(authorizationServerMetadataResponse).contains("\"code_challenge_methods_supported\":[\"S256\"]"); } @Test public void doFilterWhenAuthorizationServerSettingsWithInvalidIssuerThenThrowIllegalArgumentException() { AuthorizationServerSettings authorizationServerSettings = AuthorizationServerSettings.builder() - .issuer("https://this is an invalid URL") - .build(); - AuthorizationServerContextHolder.setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); + .issuer("https://this is an invalid URL") + .build(); + AuthorizationServerContextHolder + .setContext(new TestAuthorizationServerContext(authorizationServerSettings, null)); String requestUri = DEFAULT_OAUTH2_AUTHORIZATION_SERVER_METADATA_ENDPOINT_URI; MockHttpServletRequest request = new MockHttpServletRequest("GET", requestUri); @@ -143,10 +155,8 @@ public class OAuth2AuthorizationServerMetadataEndpointFilterTests { MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); - - assertThatIllegalArgumentException() - .isThrownBy(() -> this.filter.doFilter(request, response, filterChain)) - .withMessage("issuer must be a valid URL"); + assertThatIllegalArgumentException().isThrownBy(() -> this.filter.doFilter(request, response, filterChain)) + .withMessage("issuer must be a valid URL"); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilterTests.java index 5ed5829a..918b9d6b 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2ClientAuthenticationFilterTests.java @@ -65,13 +65,18 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2ClientAuthenticationFilterTests { + private String filterProcessesUrl = "/oauth2/token"; + private AuthenticationManager authenticationManager; + private RequestMatcher requestMatcher; + private AuthenticationConverter authenticationConverter; + private OAuth2ClientAuthenticationFilter filter; - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach public void setUp() { @@ -90,36 +95,36 @@ public class OAuth2ClientAuthenticationFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2ClientAuthenticationFilter(null, this.requestMatcher)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenRequestMatcherNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2ClientAuthenticationFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("requestMatcher cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("requestMatcher cannot be null"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test @@ -171,7 +176,8 @@ public class OAuth2ClientAuthenticationFilterTests { // gh-889 @Test - public void doFilterWhenRequestMatchesAndClientIdContainsNonPrintableASCIIThenInvalidRequestError() throws Exception { + public void doFilterWhenRequestMatchesAndClientIdContainsNonPrintableASCIIThenInvalidRequestError() + throws Exception { // Hex 00 -> null String clientId = new String(Hex.decode("00"), StandardCharsets.UTF_8); assertWhenInvalidClientIdThenInvalidRequestError(clientId); @@ -237,10 +243,12 @@ public class OAuth2ClientAuthenticationFilterTests { final String remoteAddress = "remote-address"; RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - when(this.authenticationConverter.convert(any(HttpServletRequest.class))).thenReturn( - new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null)); - when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn( - new OAuth2ClientAuthenticationToken(registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret())); + when(this.authenticationConverter.convert(any(HttpServletRequest.class))) + .thenReturn(new OAuth2ClientAuthenticationToken(registeredClient.getClientId(), + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret(), null)); + when(this.authenticationManager.authenticate(any(Authentication.class))) + .thenReturn(new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret())); MockHttpServletRequest request = new MockHttpServletRequest("POST", this.filterProcessesUrl); request.setServletPath(this.filterProcessesUrl); @@ -254,22 +262,23 @@ public class OAuth2ClientAuthenticationFilterTests { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); assertThat(authentication).isInstanceOf(OAuth2ClientAuthenticationToken.class); - assertThat(((OAuth2ClientAuthenticationToken) authentication).getRegisteredClient()).isEqualTo(registeredClient); + assertThat(((OAuth2ClientAuthenticationToken) authentication).getRegisteredClient()) + .isEqualTo(registeredClient); - ArgumentCaptor authenticationRequestCaptor = - ArgumentCaptor.forClass(OAuth2ClientAuthenticationToken.class); + ArgumentCaptor authenticationRequestCaptor = ArgumentCaptor + .forClass(OAuth2ClientAuthenticationToken.class); verify(this.authenticationManager).authenticate(authenticationRequestCaptor.capture()); - assertThat(authenticationRequestCaptor) - .extracting(ArgumentCaptor::getValue) - .extracting(OAuth2ClientAuthenticationToken::getDetails) - .asInstanceOf(type(WebAuthenticationDetails.class)) - .extracting(WebAuthenticationDetails::getRemoteAddress) - .isEqualTo(remoteAddress); + assertThat(authenticationRequestCaptor).extracting(ArgumentCaptor::getValue) + .extracting(OAuth2ClientAuthenticationToken::getDetails) + .asInstanceOf(type(WebAuthenticationDetails.class)) + .extracting(WebAuthenticationDetails::getRemoteAddress) + .isEqualTo(remoteAddress); } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java index 7f8a5448..3a27e2b6 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceAuthorizationEndpointFilterTests.java @@ -72,21 +72,28 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OAuth2DeviceAuthorizationEndpointFilterTests { + private static final String ISSUER_URI = "https://provider.com"; + private static final String REMOTE_ADDRESS = "remote-address"; + private static final String AUTHORIZATION_URI = "/oauth2/device_authorization"; + private static final String VERIFICATION_URI = "/oauth2/device_verification"; + private static final String CLIENT_ID = "client-1"; + private static final String DEVICE_CODE = "EfYu_0jEL"; + private static final String USER_CODE = "BCDF-GHJK"; private AuthenticationManager authenticationManager; + private OAuth2DeviceAuthorizationEndpointFilter filter; - private final HttpMessageConverter deviceAuthorizationHttpResponseConverter = - new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + private final HttpMessageConverter deviceAuthorizationHttpResponseConverter = new OAuth2DeviceAuthorizationResponseHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach public void setUp() { @@ -201,19 +208,19 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { this.filter.doFilter(request, response, filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - ArgumentCaptor deviceAuthorizationRequestAuthenticationCaptor = - ArgumentCaptor.forClass(OAuth2DeviceAuthorizationRequestAuthenticationToken.class); + ArgumentCaptor deviceAuthorizationRequestAuthenticationCaptor = ArgumentCaptor + .forClass(OAuth2DeviceAuthorizationRequestAuthenticationToken.class); verify(this.authenticationManager).authenticate(deviceAuthorizationRequestAuthenticationCaptor.capture()); verifyNoInteractions(filterChain); - OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = - deviceAuthorizationRequestAuthenticationCaptor.getValue(); + OAuth2DeviceAuthorizationRequestAuthenticationToken deviceAuthorizationRequestAuthentication = deviceAuthorizationRequestAuthenticationCaptor + .getValue(); assertThat(deviceAuthorizationRequestAuthentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI); assertThat(deviceAuthorizationRequestAuthentication.getPrincipal()).isEqualTo(clientPrincipal); assertThat(deviceAuthorizationRequestAuthentication.getScopes()).isEmpty(); - assertThat(deviceAuthorizationRequestAuthentication.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(deviceAuthorizationRequestAuthentication.getAdditionalParameters()).containsExactly( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); // @formatter:off assertThat(deviceAuthorizationRequestAuthentication.getDetails()) .asInstanceOf(type(WebAuthenticationDetails.class)) @@ -225,7 +232,7 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { String verificationUri = ISSUER_URI + VERIFICATION_URI; assertThat(deviceAuthorizationResponse.getVerificationUri()).isEqualTo(verificationUri); assertThat(deviceAuthorizationResponse.getVerificationUriComplete()) - .isEqualTo("%s?%s=%s".formatted(verificationUri, OAuth2ParameterNames.USER_CODE, USER_CODE)); + .isEqualTo("%s?%s=%s".formatted(verificationUri, OAuth2ParameterNames.USER_CODE, USER_CODE)); OAuth2DeviceCode deviceCode = deviceAuthorizationResponse.getDeviceCode(); assertThat(deviceCode.getTokenValue()).isEqualTo(DEVICE_CODE); assertThat(deviceCode.getExpiresAt()).isAfter(deviceCode.getIssuedAt()); @@ -287,8 +294,8 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { mockSecurityContext(clientPrincipal); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); - OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationRequest = - new OAuth2DeviceAuthorizationRequestAuthenticationToken(clientPrincipal, AUTHORIZATION_URI, null, null); + OAuth2DeviceAuthorizationRequestAuthenticationToken authenticationRequest = new OAuth2DeviceAuthorizationRequestAuthenticationToken( + clientPrincipal, AUTHORIZATION_URI, null, null); when(authenticationConverter.convert(any(HttpServletRequest.class))).thenReturn(authenticationRequest); this.filter.setAuthenticationConverter(authenticationConverter); @@ -316,8 +323,10 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { FilterChain filterChain = mock(FilterChain.class); @SuppressWarnings("unchecked") - AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class); - when(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class))).thenReturn(new WebAuthenticationDetails(request)); + AuthenticationDetailsSource authenticationDetailsSource = mock( + AuthenticationDetailsSource.class); + when(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class))) + .thenReturn(new WebAuthenticationDetails(request)); this.filter.setAuthenticationDetailsSource(authenticationDetailsSource); this.filter.doFilter(request, response, filterChain); @@ -352,8 +361,8 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { @Test public void doFilterWhenAuthenticationFailureHandlerSetThenUsed() throws Exception { - OAuth2AuthenticationException authenticationException = - new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); + OAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException( + OAuth2ErrorCodes.INVALID_REQUEST); when(this.authenticationManager.authenticate(any(Authentication.class))).thenThrow(authenticationException); Authentication clientPrincipal = (Authentication) createAuthentication().getPrincipal(); @@ -373,15 +382,17 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { verifyNoInteractions(filterChain); } - private OAuth2DeviceAuthorizationResponse readDeviceAuthorizationResponse(MockHttpServletResponse response) throws IOException { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); - return this.deviceAuthorizationHttpResponseConverter.read(OAuth2DeviceAuthorizationResponse.class, httpResponse); + private OAuth2DeviceAuthorizationResponse readDeviceAuthorizationResponse(MockHttpServletResponse response) + throws IOException { + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); + return this.deviceAuthorizationHttpResponseConverter.read(OAuth2DeviceAuthorizationResponse.class, + httpResponse); } private OAuth2Error readError(MockHttpServletResponse response) throws IOException { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } @@ -422,4 +433,5 @@ public class OAuth2DeviceAuthorizationEndpointFilterTests { Instant issuedAt = Instant.now(); return new OAuth2UserCode(USER_CODE, issuedAt, issuedAt.plus(30, ChronoUnit.MINUTES)); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java index 524112b5..fcabda39 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2DeviceVerificationEndpointFilterTests.java @@ -70,15 +70,23 @@ import static org.mockito.Mockito.when; * @author Steve Riesenberg */ public class OAuth2DeviceVerificationEndpointFilterTests { + private static final String ISSUER_URI = "https://provider.com"; + private static final String REMOTE_ADDRESS = "remote-address"; + private static final String AUTHORIZATION_URI = "/oauth2/device_authorization"; + private static final String VERIFICATION_URI = "/oauth2/device_verification"; + private static final String CLIENT_ID = "client-1"; + private static final String STATE = "12345"; + private static final String USER_CODE = "BCDF-GHJK"; private AuthenticationManager authenticationManager; + private OAuth2DeviceVerificationEndpointFilter filter; @BeforeEach @@ -196,22 +204,22 @@ public class OAuth2DeviceVerificationEndpointFilterTests { assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); assertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo("/?success"); - ArgumentCaptor authenticationCaptor = - ArgumentCaptor.forClass(OAuth2DeviceAuthorizationConsentAuthenticationToken.class); + ArgumentCaptor authenticationCaptor = ArgumentCaptor + .forClass(OAuth2DeviceAuthorizationConsentAuthenticationToken.class); verify(this.authenticationManager).authenticate(authenticationCaptor.capture()); verifyNoInteractions(filterChain); - OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = - authenticationCaptor.getValue(); + OAuth2DeviceAuthorizationConsentAuthenticationToken deviceAuthorizationConsentAuthentication = authenticationCaptor + .getValue(); assertThat(deviceAuthorizationConsentAuthentication.getAuthorizationUri()).endsWith(VERIFICATION_URI); assertThat(deviceAuthorizationConsentAuthentication.getClientId()).isEqualTo(CLIENT_ID); assertThat(deviceAuthorizationConsentAuthentication.getPrincipal()) - .isInstanceOf(TestingAuthenticationToken.class); + .isInstanceOf(TestingAuthenticationToken.class); assertThat(deviceAuthorizationConsentAuthentication.getUserCode()).isEqualTo(USER_CODE); assertThat(deviceAuthorizationConsentAuthentication.getScopes()).containsExactly("scope-1", "scope-2"); - assertThat(deviceAuthorizationConsentAuthentication.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(deviceAuthorizationConsentAuthentication.getAdditionalParameters()).containsExactly( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); } @Test @@ -232,8 +240,8 @@ public class OAuth2DeviceVerificationEndpointFilterTests { assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); assertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo("/?success"); - ArgumentCaptor authenticationCaptor = - ArgumentCaptor.forClass(OAuth2DeviceVerificationAuthenticationToken.class); + ArgumentCaptor authenticationCaptor = ArgumentCaptor + .forClass(OAuth2DeviceVerificationAuthenticationToken.class); verify(this.authenticationManager).authenticate(authenticationCaptor.capture()); verifyNoInteractions(filterChain); @@ -241,7 +249,7 @@ public class OAuth2DeviceVerificationEndpointFilterTests { assertThat(deviceVerificationAuthentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(deviceVerificationAuthentication.getUserCode()).isEqualTo(USER_CODE); assertThat(deviceVerificationAuthentication.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1")); + .containsExactly(entry("custom-param-1", "custom-value-1")); } @Test @@ -257,7 +265,7 @@ public class OAuth2DeviceVerificationEndpointFilterTests { this.filter.doFilter(request, response, filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getContentType()) - .isEqualTo(new MediaType("text", "html", StandardCharsets.UTF_8).toString()); + .isEqualTo(new MediaType("text", "html", StandardCharsets.UTF_8).toString()); assertThat(response.getContentAsString()).contains(scopeCheckbox("scope-1")); assertThat(response.getContentAsString()).contains(scopeCheckbox("scope-2")); @@ -266,7 +274,8 @@ public class OAuth2DeviceVerificationEndpointFilterTests { } @Test - public void doFilterWhenDeviceVerificationRequestAndConsentRequiredWithPreviouslyApprovedThenConsentScreen() throws Exception { + public void doFilterWhenDeviceVerificationRequestAndConsentRequiredWithPreviouslyApprovedThenConsentScreen() + throws Exception { Authentication authenticationResult = createDeviceAuthorizationConsentAuthenticationWithAuthorizedScopes(); when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(authenticationResult); @@ -278,7 +287,7 @@ public class OAuth2DeviceVerificationEndpointFilterTests { this.filter.doFilter(request, response, filterChain); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); assertThat(response.getContentType()) - .isEqualTo(new MediaType("text", "html", StandardCharsets.UTF_8).toString()); + .isEqualTo(new MediaType("text", "html", StandardCharsets.UTF_8).toString()); assertThat(response.getContentAsString()).contains(disabledScopeCheckbox("scope-1")); assertThat(response.getContentAsString()).contains(scopeCheckbox("scope-2")); @@ -287,7 +296,8 @@ public class OAuth2DeviceVerificationEndpointFilterTests { } @Test - public void doFilterWhenDeviceVerificationRequestAndConsentRequiredAndConsentPageSetThenRedirect() throws Exception { + public void doFilterWhenDeviceVerificationRequestAndConsentRequiredAndConsentPageSetThenRedirect() + throws Exception { Authentication authentication = createDeviceAuthorizationConsentAuthentication(); when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(authentication); @@ -302,11 +312,11 @@ public class OAuth2DeviceVerificationEndpointFilterTests { this.filter.setConsentPage("/consent"); this.filter.doFilter(request, response, filterChain); String redirectUri = UriComponentsBuilder.fromUriString("https://provider.com/consent") - .queryParam(OAuth2ParameterNames.SCOPE, "scope-1 scope-2") - .queryParam(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID) - .queryParam(OAuth2ParameterNames.STATE, STATE) - .queryParam(OAuth2ParameterNames.USER_CODE, USER_CODE) - .toUriString(); + .queryParam(OAuth2ParameterNames.SCOPE, "scope-1 scope-2") + .queryParam(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID) + .queryParam(OAuth2ParameterNames.STATE, STATE) + .queryParam(OAuth2ParameterNames.USER_CODE, USER_CODE) + .toUriString(); assertThat(response.getStatus()).isEqualTo(HttpStatus.FOUND.value()); assertThat(response.getHeader(HttpHeaders.LOCATION)).isEqualTo(redirectUri); @@ -320,10 +330,10 @@ public class OAuth2DeviceVerificationEndpointFilterTests { when(this.authenticationManager.authenticate(any(Authentication.class))).thenReturn(authenticationResult); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); - OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = - new OAuth2DeviceVerificationAuthenticationToken((Authentication) authenticationResult.getPrincipal(), - USER_CODE, Collections.emptyMap()); - when(authenticationConverter.convert(any(HttpServletRequest.class))).thenReturn(deviceVerificationAuthentication); + OAuth2DeviceVerificationAuthenticationToken deviceVerificationAuthentication = new OAuth2DeviceVerificationAuthenticationToken( + (Authentication) authenticationResult.getPrincipal(), USER_CODE, Collections.emptyMap()); + when(authenticationConverter.convert(any(HttpServletRequest.class))) + .thenReturn(deviceVerificationAuthentication); this.filter.setAuthenticationConverter(authenticationConverter); MockHttpServletRequest request = createRequest(); @@ -352,8 +362,10 @@ public class OAuth2DeviceVerificationEndpointFilterTests { FilterChain filterChain = mock(FilterChain.class); @SuppressWarnings("unchecked") - AuthenticationDetailsSource authenticationDetailsSource = mock(AuthenticationDetailsSource.class); - when(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class))).thenReturn(new WebAuthenticationDetails(request)); + AuthenticationDetailsSource authenticationDetailsSource = mock( + AuthenticationDetailsSource.class); + when(authenticationDetailsSource.buildDetails(any(HttpServletRequest.class))) + .thenReturn(new WebAuthenticationDetails(request)); this.filter.setAuthenticationDetailsSource(authenticationDetailsSource); this.filter.doFilter(request, response, filterChain); @@ -388,8 +400,8 @@ public class OAuth2DeviceVerificationEndpointFilterTests { @Test public void doFilterWhenAuthenticationFailureHandlerSetThenUsed() throws Exception { - OAuth2AuthenticationException authenticationException = - new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_REQUEST); + OAuth2AuthenticationException authenticationException = new OAuth2AuthenticationException( + OAuth2ErrorCodes.INVALID_REQUEST); when(this.authenticationManager.authenticate(any(Authentication.class))).thenThrow(authenticationException); AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class); @@ -469,15 +481,13 @@ public class OAuth2DeviceVerificationEndpointFilterTests { private static String scopeCheckbox(String scope) { return MessageFormat.format( - "", - scope - ); + "", scope); } private static String disabledScopeCheckbox(String scope) { return MessageFormat.format( "", - scope - ); + scope); } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java index 824b9f10..21cc8f56 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenEndpointFilterTests.java @@ -82,14 +82,18 @@ import static org.mockito.Mockito.when; * @author Daniel Garnier-Moiroux */ public class OAuth2TokenEndpointFilterTests { + private static final String DEFAULT_TOKEN_ENDPOINT_URI = "/oauth2/token"; + private static final String REMOTE_ADDRESS = "remote-address"; + private AuthenticationManager authenticationManager; + private OAuth2TokenEndpointFilter filter; - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); - private final HttpMessageConverter accessTokenHttpResponseConverter = - new OAuth2AccessTokenResponseHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter accessTokenHttpResponseConverter = new OAuth2AccessTokenResponseHttpMessageConverter(); @BeforeEach public void setUp() { @@ -105,43 +109,43 @@ public class OAuth2TokenEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenEndpointFilter(null, "tokenEndpointUri")) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenTokenEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenEndpointFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenEndpointUri cannot be empty"); } @Test public void setAuthenticationDetailsSourceWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationDetailsSource(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationDetailsSource cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationDetailsSource cannot be null"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test @@ -176,8 +180,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.removeParameter(OAuth2ParameterNames.GRANT_TYPE); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.GRANT_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test @@ -186,8 +190,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.GRANT_TYPE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test @@ -196,8 +200,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.setParameter(OAuth2ParameterNames.GRANT_TYPE, "invalid-grant-type"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.GRANT_TYPE, OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.GRANT_TYPE, + OAuth2ErrorCodes.UNSUPPORTED_GRANT_TYPE, request); } @Test @@ -206,8 +210,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.removeParameter(OAuth2ParameterNames.CODE); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST, + request); } @Test @@ -216,8 +220,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.addParameter(OAuth2ParameterNames.CODE, "code-2"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.CODE, OAuth2ErrorCodes.INVALID_REQUEST, + request); } @Test @@ -226,24 +230,22 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.addParameter(OAuth2ParameterNames.REDIRECT_URI, "https://example2.com"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.REDIRECT_URI, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REDIRECT_URI, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test public void doFilterWhenAuthorizationCodeTokenRequestThenAccessTokenResponse() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2RefreshToken refreshToken = new OAuth2RefreshToken( - "refresh-token", Instant.now(), Instant.now().plus(Duration.ofDays(1))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken( - registeredClient, clientPrincipal, accessToken, refreshToken); + OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now(), + Instant.now().plus(Duration.ofDays(1))); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken, refreshToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -259,24 +261,23 @@ public class OAuth2TokenEndpointFilterTests { verifyNoInteractions(filterChain); - ArgumentCaptor authorizationCodeAuthenticationCaptor = - ArgumentCaptor.forClass(OAuth2AuthorizationCodeAuthenticationToken.class); + ArgumentCaptor authorizationCodeAuthenticationCaptor = ArgumentCaptor + .forClass(OAuth2AuthorizationCodeAuthenticationToken.class); verify(this.authenticationManager).authenticate(authorizationCodeAuthenticationCaptor.capture()); - OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = - authorizationCodeAuthenticationCaptor.getValue(); - assertThat(authorizationCodeAuthentication.getCode()).isEqualTo( - request.getParameter(OAuth2ParameterNames.CODE)); + OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = authorizationCodeAuthenticationCaptor + .getValue(); + assertThat(authorizationCodeAuthentication.getCode()) + .isEqualTo(request.getParameter(OAuth2ParameterNames.CODE)); assertThat(authorizationCodeAuthentication.getPrincipal()).isEqualTo(clientPrincipal); - assertThat(authorizationCodeAuthentication.getRedirectUri()).isEqualTo( - request.getParameter(OAuth2ParameterNames.REDIRECT_URI)); - assertThat(authorizationCodeAuthentication.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); - assertThat(authorizationCodeAuthentication.getDetails()) - .asInstanceOf(type(WebAuthenticationDetails.class)) - .extracting(WebAuthenticationDetails::getRemoteAddress) - .isEqualTo(REMOTE_ADDRESS); + assertThat(authorizationCodeAuthentication.getRedirectUri()) + .isEqualTo(request.getParameter(OAuth2ParameterNames.REDIRECT_URI)); + assertThat(authorizationCodeAuthentication.getAdditionalParameters()).containsExactly( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(authorizationCodeAuthentication.getDetails()).asInstanceOf(type(WebAuthenticationDetails.class)) + .extracting(WebAuthenticationDetails::getRemoteAddress) + .isEqualTo(REMOTE_ADDRESS); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response); @@ -284,10 +285,10 @@ public class OAuth2TokenEndpointFilterTests { OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken(); assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType()); assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue()); - assertThat(accessTokenResult.getIssuedAt()).isBetween( - accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1)); - assertThat(accessTokenResult.getExpiresAt()).isBetween( - accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1)); + assertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1), + accessToken.getIssuedAt().plusSeconds(1)); + assertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1), + accessToken.getExpiresAt().plusSeconds(1)); assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes()); assertThat(accessTokenResponse.getRefreshToken().getTokenValue()).isEqualTo(refreshToken.getTokenValue()); } @@ -298,22 +299,20 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient2().build()); request.addParameter(OAuth2ParameterNames.SCOPE, "profile"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, + request); } @Test public void doFilterWhenClientCredentialsTokenRequestThenAccessTokenResponse() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken( - registeredClient, clientPrincipal, accessToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -329,35 +328,34 @@ public class OAuth2TokenEndpointFilterTests { verifyNoInteractions(filterChain); - ArgumentCaptor clientCredentialsAuthenticationCaptor = - ArgumentCaptor.forClass(OAuth2ClientCredentialsAuthenticationToken.class); + ArgumentCaptor clientCredentialsAuthenticationCaptor = ArgumentCaptor + .forClass(OAuth2ClientCredentialsAuthenticationToken.class); verify(this.authenticationManager).authenticate(clientCredentialsAuthenticationCaptor.capture()); - OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = - clientCredentialsAuthenticationCaptor.getValue(); + OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = clientCredentialsAuthenticationCaptor + .getValue(); assertThat(clientCredentialsAuthentication.getPrincipal()).isEqualTo(clientPrincipal); assertThat(clientCredentialsAuthentication.getScopes()).isEqualTo(registeredClient.getScopes()); - assertThat(clientCredentialsAuthentication.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); - assertThat(clientCredentialsAuthentication.getDetails()) - .asInstanceOf(type(WebAuthenticationDetails.class)) - .extracting(WebAuthenticationDetails::getRemoteAddress) - .isEqualTo(REMOTE_ADDRESS); + assertThat(clientCredentialsAuthentication.getAdditionalParameters()).containsExactly( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(clientCredentialsAuthentication.getDetails()).asInstanceOf(type(WebAuthenticationDetails.class)) + .extracting(WebAuthenticationDetails::getRemoteAddress) + .isEqualTo(REMOTE_ADDRESS); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); // For gh-281, check that expires_in is a number - assertThat(new ObjectMapper().readValue(response.getContentAsByteArray(), Map.class).get(OAuth2ParameterNames.EXPIRES_IN)) - .isInstanceOf(Number.class); + assertThat(new ObjectMapper().readValue(response.getContentAsByteArray(), Map.class) + .get(OAuth2ParameterNames.EXPIRES_IN)).isInstanceOf(Number.class); OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response); OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken(); assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType()); assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue()); - assertThat(accessTokenResult.getIssuedAt()).isBetween( - accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1)); - assertThat(accessTokenResult.getExpiresAt()).isBetween( - accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1)); + assertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1), + accessToken.getIssuedAt().plusSeconds(1)); + assertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1), + accessToken.getExpiresAt().plusSeconds(1)); assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes()); } @@ -367,8 +365,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.removeParameter(OAuth2ParameterNames.REFRESH_TOKEN); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REFRESH_TOKEN, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test @@ -377,8 +375,8 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.addParameter(OAuth2ParameterNames.REFRESH_TOKEN, "refresh-token-2"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.REFRESH_TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.REFRESH_TOKEN, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test @@ -387,23 +385,21 @@ public class OAuth2TokenEndpointFilterTests { TestRegisteredClients.registeredClient().build()); request.addParameter(OAuth2ParameterNames.SCOPE, "profile"); - doFilterWhenTokenRequestInvalidParameterThenError( - OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenRequestInvalidParameterThenError(OAuth2ParameterNames.SCOPE, OAuth2ErrorCodes.INVALID_REQUEST, + request); } @Test public void doFilterWhenRefreshTokenRequestThenAccessTokenResponse() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); OAuth2RefreshToken refreshToken = new OAuth2RefreshToken("refresh-token", Instant.now()); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken( - registeredClient, clientPrincipal, accessToken, refreshToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken, refreshToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -419,23 +415,21 @@ public class OAuth2TokenEndpointFilterTests { verifyNoInteractions(filterChain); - ArgumentCaptor refreshTokenAuthenticationCaptor = - ArgumentCaptor.forClass(OAuth2RefreshTokenAuthenticationToken.class); + ArgumentCaptor refreshTokenAuthenticationCaptor = ArgumentCaptor + .forClass(OAuth2RefreshTokenAuthenticationToken.class); verify(this.authenticationManager).authenticate(refreshTokenAuthenticationCaptor.capture()); - OAuth2RefreshTokenAuthenticationToken refreshTokenAuthenticationToken = - refreshTokenAuthenticationCaptor.getValue(); + OAuth2RefreshTokenAuthenticationToken refreshTokenAuthenticationToken = refreshTokenAuthenticationCaptor + .getValue(); assertThat(refreshTokenAuthenticationToken.getRefreshToken()).isEqualTo(refreshToken.getTokenValue()); assertThat(refreshTokenAuthenticationToken.getPrincipal()).isEqualTo(clientPrincipal); assertThat(refreshTokenAuthenticationToken.getScopes()).isEqualTo(registeredClient.getScopes()); - assertThat(refreshTokenAuthenticationToken.getAdditionalParameters()) - .containsExactly(entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); - assertThat(refreshTokenAuthenticationToken.getDetails()) - .asInstanceOf(type(WebAuthenticationDetails.class)) - .extracting(WebAuthenticationDetails::getRemoteAddress) - .isEqualTo(REMOTE_ADDRESS); - + assertThat(refreshTokenAuthenticationToken.getAdditionalParameters()).containsExactly( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(refreshTokenAuthenticationToken.getDetails()).asInstanceOf(type(WebAuthenticationDetails.class)) + .extracting(WebAuthenticationDetails::getRemoteAddress) + .isEqualTo(REMOTE_ADDRESS); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); OAuth2AccessTokenResponse accessTokenResponse = readAccessTokenResponse(response); @@ -443,10 +437,10 @@ public class OAuth2TokenEndpointFilterTests { OAuth2AccessToken accessTokenResult = accessTokenResponse.getAccessToken(); assertThat(accessTokenResult.getTokenType()).isEqualTo(accessToken.getTokenType()); assertThat(accessTokenResult.getTokenValue()).isEqualTo(accessToken.getTokenValue()); - assertThat(accessTokenResult.getIssuedAt()).isBetween( - accessToken.getIssuedAt().minusSeconds(1), accessToken.getIssuedAt().plusSeconds(1)); - assertThat(accessTokenResult.getExpiresAt()).isBetween( - accessToken.getExpiresAt().minusSeconds(1), accessToken.getExpiresAt().plusSeconds(1)); + assertThat(accessTokenResult.getIssuedAt()).isBetween(accessToken.getIssuedAt().minusSeconds(1), + accessToken.getIssuedAt().plusSeconds(1)); + assertThat(accessTokenResult.getExpiresAt()).isBetween(accessToken.getExpiresAt().minusSeconds(1), + accessToken.getExpiresAt().plusSeconds(1)); assertThat(accessTokenResult.getScopes()).isEqualTo(accessToken.getScopes()); OAuth2RefreshToken refreshTokenResult = accessTokenResponse.getRefreshToken(); @@ -456,23 +450,22 @@ public class OAuth2TokenEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationDetailsSourceThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); MockHttpServletRequest request = createAuthorizationCodeTokenRequest(registeredClient); - AuthenticationDetailsSource authenticationDetailsSource = - mock(AuthenticationDetailsSource.class); + AuthenticationDetailsSource authenticationDetailsSource = mock( + AuthenticationDetailsSource.class); WebAuthenticationDetails webAuthenticationDetails = new WebAuthenticationDetails(request); when(authenticationDetailsSource.buildDetails(any())).thenReturn(webAuthenticationDetails); this.filter.setAuthenticationDetailsSource(authenticationDetailsSource); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -491,22 +484,21 @@ public class OAuth2TokenEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = - new OAuth2AuthorizationCodeAuthenticationToken("code", clientPrincipal, null, null); + OAuth2AuthorizationCodeAuthenticationToken authorizationCodeAuthentication = new OAuth2AuthorizationCodeAuthenticationToken( + "code", clientPrincipal, null, null); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(authorizationCodeAuthentication); this.filter.setAuthenticationConverter(authenticationConverter); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -529,14 +521,13 @@ public class OAuth2TokenEndpointFilterTests { this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = - new OAuth2AccessTokenAuthenticationToken(registeredClient, clientPrincipal, accessToken); + OAuth2AccessTokenAuthenticationToken accessTokenAuthentication = new OAuth2AccessTokenAuthenticationToken( + registeredClient, clientPrincipal, accessToken); when(this.authenticationManager.authenticate(any())).thenReturn(accessTokenAuthentication); @@ -588,14 +579,14 @@ public class OAuth2TokenEndpointFilterTests { } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } private OAuth2AccessTokenResponse readAccessTokenResponse(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.accessTokenHttpResponseConverter.read(OAuth2AccessTokenResponse.class, httpResponse); } @@ -610,7 +601,8 @@ public class OAuth2TokenEndpointFilterTests { request.addParameter(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()); request.addParameter(OAuth2ParameterNames.CODE, "code"); request.addParameter(OAuth2ParameterNames.REDIRECT_URI, redirectUris[0]); - // The client does not need to send the client ID param, but we are resilient in case they do + // The client does not need to send the client ID param, but we are resilient in + // case they do request.addParameter(OAuth2ParameterNames.CLIENT_ID, registeredClient.getClientId()); request.addParameter("custom-param-1", "custom-value-1"); request.addParameter("custom-param-2", "custom-value-1", "custom-value-2"); @@ -648,4 +640,5 @@ public class OAuth2TokenEndpointFilterTests { return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilterTests.java index 41a67755..4a90ed80 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenIntrospectionEndpointFilterTests.java @@ -72,13 +72,16 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2TokenIntrospectionEndpointFilterTests { + private static final String DEFAULT_TOKEN_INTROSPECTION_ENDPOINT_URI = "/oauth2/introspect"; + private AuthenticationManager authenticationManager; + private OAuth2TokenIntrospectionEndpointFilter filter; - private final HttpMessageConverter tokenIntrospectionHttpResponseConverter = - new OAuth2TokenIntrospectionHttpMessageConverter(); - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter tokenIntrospectionHttpResponseConverter = new OAuth2TokenIntrospectionHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach public void setUp() { @@ -94,36 +97,36 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenIntrospectionEndpointFilter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenTokenIntrospectionEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenIntrospectionEndpointFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenIntrospectionEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenIntrospectionEndpointUri cannot be empty"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test @@ -154,41 +157,40 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { @Test public void doFilterWhenTokenIntrospectionRequestMissingTokenThenInvalidRequestError() throws Exception { - MockHttpServletRequest request = createTokenIntrospectionRequest( - "token", OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest("token", + OAuth2TokenType.ACCESS_TOKEN.getValue()); request.removeParameter(OAuth2ParameterNames.TOKEN); - doFilterWhenTokenIntrospectionRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test public void doFilterWhenTokenIntrospectionRequestMultipleTokenThenInvalidRequestError() throws Exception { - MockHttpServletRequest request = createTokenIntrospectionRequest( - "token", OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest("token", + OAuth2TokenType.ACCESS_TOKEN.getValue()); request.addParameter(OAuth2ParameterNames.TOKEN, "other-token"); - doFilterWhenTokenIntrospectionRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test public void doFilterWhenTokenIntrospectionRequestMultipleTokenTypeHintThenInvalidRequestError() throws Exception { - MockHttpServletRequest request = createTokenIntrospectionRequest( - "token", OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest("token", + OAuth2TokenType.ACCESS_TOKEN.getValue()); request.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2TokenType.ACCESS_TOKEN.getValue()); - doFilterWhenTokenIntrospectionRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2ErrorCodes.INVALID_REQUEST, request); + doFilterWhenTokenIntrospectionRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN_TYPE_HINT, + OAuth2ErrorCodes.INVALID_REQUEST, request); } @Test public void doFilterWhenTokenIntrospectionRequestValidThenSuccessResponse() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); // @formatter:off @@ -206,9 +208,8 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { .id("jti") .build(); // @formatter:on - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthenticationResult = - new OAuth2TokenIntrospectionAuthenticationToken( - accessToken.getTokenValue(), clientPrincipal, tokenClaims); + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthenticationResult = new OAuth2TokenIntrospectionAuthenticationToken( + accessToken.getTokenValue(), clientPrincipal, tokenClaims); when(this.authenticationManager.authenticate(any())).thenReturn(tokenIntrospectionAuthenticationResult); @@ -216,8 +217,8 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { securityContext.setAuthentication(clientPrincipal); SecurityContextHolder.setContext(securityContext); - MockHttpServletRequest request = createTokenIntrospectionRequest( - accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN.getValue()); request.addParameter("custom-param-1", "custom-value-1"); request.addParameter("custom-param-2", "custom-value-1", "custom-value-2"); @@ -226,32 +227,32 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { this.filter.doFilter(request, response, filterChain); - ArgumentCaptor tokenIntrospectionAuthentication = - ArgumentCaptor.forClass(OAuth2TokenIntrospectionAuthenticationToken.class); + ArgumentCaptor tokenIntrospectionAuthentication = ArgumentCaptor + .forClass(OAuth2TokenIntrospectionAuthenticationToken.class); verifyNoInteractions(filterChain); verify(this.authenticationManager).authenticate(tokenIntrospectionAuthentication.capture()); assertThat(response.getStatus()).isEqualTo(HttpStatus.OK.value()); - assertThat(tokenIntrospectionAuthentication.getValue().getAdditionalParameters()) - .contains( - entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] {"custom-value-1", "custom-value-2"})); + assertThat(tokenIntrospectionAuthentication.getValue().getAdditionalParameters()).contains( + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); OAuth2TokenIntrospection tokenIntrospectionResponse = readTokenIntrospectionResponse(response); assertThat(tokenIntrospectionResponse.isActive()).isEqualTo(tokenClaims.isActive()); assertThat(tokenIntrospectionResponse.getClientId()).isEqualTo(tokenClaims.getClientId()); assertThat(tokenIntrospectionResponse.getUsername()).isEqualTo(tokenClaims.getUsername()); - assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween( - tokenClaims.getIssuedAt().minusSeconds(1), tokenClaims.getIssuedAt().plusSeconds(1)); - assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween( - tokenClaims.getExpiresAt().minusSeconds(1), tokenClaims.getExpiresAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getIssuedAt()).isBetween(tokenClaims.getIssuedAt().minusSeconds(1), + tokenClaims.getIssuedAt().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getExpiresAt()).isBetween(tokenClaims.getExpiresAt().minusSeconds(1), + tokenClaims.getExpiresAt().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getScopes()).containsExactlyInAnyOrderElementsOf(tokenClaims.getScopes()); assertThat(tokenIntrospectionResponse.getTokenType()).isEqualTo(tokenClaims.getTokenType()); - assertThat(tokenIntrospectionResponse.getNotBefore()).isBetween( - tokenClaims.getNotBefore().minusSeconds(1), tokenClaims.getNotBefore().plusSeconds(1)); + assertThat(tokenIntrospectionResponse.getNotBefore()).isBetween(tokenClaims.getNotBefore().minusSeconds(1), + tokenClaims.getNotBefore().plusSeconds(1)); assertThat(tokenIntrospectionResponse.getSubject()).isEqualTo(tokenClaims.getSubject()); - assertThat(tokenIntrospectionResponse.getAudience()).containsExactlyInAnyOrderElementsOf(tokenClaims.getAudience()); + assertThat(tokenIntrospectionResponse.getAudience()) + .containsExactlyInAnyOrderElementsOf(tokenClaims.getAudience()); assertThat(tokenIntrospectionResponse.getIssuer()).isEqualTo(tokenClaims.getIssuer()); assertThat(tokenIntrospectionResponse.getId()).isEqualTo(tokenClaims.getId()); } @@ -259,15 +260,13 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = - new OAuth2TokenIntrospectionAuthenticationToken( - accessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null); + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken( + accessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(tokenIntrospectionAuthentication); @@ -279,8 +278,8 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { securityContext.setAuthentication(clientPrincipal); SecurityContextHolder.setContext(securityContext); - MockHttpServletRequest request = createTokenIntrospectionRequest( - accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN.getValue()); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -292,15 +291,13 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = - new OAuth2TokenIntrospectionAuthenticationToken( - accessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null); + OAuth2TokenIntrospectionAuthenticationToken tokenIntrospectionAuthentication = new OAuth2TokenIntrospectionAuthenticationToken( + accessToken.getTokenValue(), clientPrincipal, OAuth2TokenType.ACCESS_TOKEN.getValue(), null); AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); @@ -311,8 +308,8 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { securityContext.setAuthentication(clientPrincipal); SecurityContextHolder.setContext(securityContext); - MockHttpServletRequest request = createTokenIntrospectionRequest( - accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN.getValue()); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -324,10 +321,9 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); @@ -340,8 +336,8 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { securityContext.setAuthentication(clientPrincipal); SecurityContextHolder.setContext(securityContext); - MockHttpServletRequest request = createTokenIntrospectionRequest( - accessToken.getTokenValue(), OAuth2TokenType.ACCESS_TOKEN.getValue()); + MockHttpServletRequest request = createTokenIntrospectionRequest(accessToken.getTokenValue(), + OAuth2TokenType.ACCESS_TOKEN.getValue()); MockHttpServletResponse response = new MockHttpServletResponse(); FilterChain filterChain = mock(FilterChain.class); @@ -367,14 +363,14 @@ public class OAuth2TokenIntrospectionEndpointFilterTests { } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } private OAuth2TokenIntrospection readTokenIntrospectionResponse(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.tokenIntrospectionHttpResponseConverter.read(OAuth2TokenIntrospection.class, httpResponse); } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java index 62ab2467..5940e3a2 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/OAuth2TokenRevocationEndpointFilterTests.java @@ -69,11 +69,14 @@ import static org.mockito.Mockito.when; * @author Joe Grandja */ public class OAuth2TokenRevocationEndpointFilterTests { + private static final String DEFAULT_TOKEN_REVOCATION_ENDPOINT_URI = "/oauth2/revoke"; + private AuthenticationManager authenticationManager; + private OAuth2TokenRevocationEndpointFilter filter; - private final HttpMessageConverter errorHttpResponseConverter = - new OAuth2ErrorHttpMessageConverter(); + + private final HttpMessageConverter errorHttpResponseConverter = new OAuth2ErrorHttpMessageConverter(); @BeforeEach public void setUp() { @@ -89,36 +92,36 @@ public class OAuth2TokenRevocationEndpointFilterTests { @Test public void constructorWhenAuthenticationManagerNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationManager cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationManager cannot be null"); } @Test public void constructorWhenTokenRevocationEndpointUriNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> new OAuth2TokenRevocationEndpointFilter(this.authenticationManager, null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("tokenRevocationEndpointUri cannot be empty"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("tokenRevocationEndpointUri cannot be empty"); } @Test public void setAuthenticationConverterWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationConverter(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationConverter cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationConverter cannot be null"); } @Test public void setAuthenticationSuccessHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationSuccessHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationSuccessHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationSuccessHandler cannot be null"); } @Test public void setAuthenticationFailureHandlerWhenNullThenThrowIllegalArgumentException() { assertThatThrownBy(() -> this.filter.setAuthenticationFailureHandler(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authenticationFailureHandler cannot be null"); + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationFailureHandler cannot be null"); } @Test @@ -149,40 +152,34 @@ public class OAuth2TokenRevocationEndpointFilterTests { @Test public void doFilterWhenTokenRevocationRequestMissingTokenThenInvalidRequestError() throws Exception { - doFilterWhenTokenRevocationRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> request.removeParameter(OAuth2ParameterNames.TOKEN)); + doFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN, + OAuth2ErrorCodes.INVALID_REQUEST, request -> request.removeParameter(OAuth2ParameterNames.TOKEN)); } @Test public void doFilterWhenTokenRevocationRequestMultipleTokenThenInvalidRequestError() throws Exception { - doFilterWhenTokenRevocationRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN, + doFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN, OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter(OAuth2ParameterNames.TOKEN, "token-2")); } @Test public void doFilterWhenTokenRevocationRequestMultipleTokenTypeHintThenInvalidRequestError() throws Exception { - doFilterWhenTokenRevocationRequestInvalidParameterThenError( - OAuth2ParameterNames.TOKEN_TYPE_HINT, - OAuth2ErrorCodes.INVALID_REQUEST, - request -> request.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, OAuth2TokenType.ACCESS_TOKEN.getValue())); + doFilterWhenTokenRevocationRequestInvalidParameterThenError(OAuth2ParameterNames.TOKEN_TYPE_HINT, + OAuth2ErrorCodes.INVALID_REQUEST, request -> request.addParameter(OAuth2ParameterNames.TOKEN_TYPE_HINT, + OAuth2TokenType.ACCESS_TOKEN.getValue())); } @Test public void doFilterWhenTokenRevocationRequestValidThenSuccessResponse() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = - new OAuth2TokenRevocationAuthenticationToken( - accessToken, clientPrincipal); + OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken( + accessToken, clientPrincipal); when(this.authenticationManager.authenticate(any())).thenReturn(tokenRevocationAuthentication); @@ -205,15 +202,13 @@ public class OAuth2TokenRevocationEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationConverterThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = - new OAuth2TokenRevocationAuthenticationToken( - accessToken, clientPrincipal); + OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken( + accessToken, clientPrincipal); AuthenticationConverter authenticationConverter = mock(AuthenticationConverter.class); when(authenticationConverter.convert(any())).thenReturn(tokenRevocationAuthentication); @@ -237,15 +232,13 @@ public class OAuth2TokenRevocationEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationSuccessHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); - OAuth2AccessToken accessToken = new OAuth2AccessToken( - OAuth2AccessToken.TokenType.BEARER, "token", + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2AccessToken accessToken = new OAuth2AccessToken(OAuth2AccessToken.TokenType.BEARER, "token", Instant.now(), Instant.now().plus(Duration.ofHours(1)), new HashSet<>(Arrays.asList("scope1", "scope2"))); - OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = - new OAuth2TokenRevocationAuthenticationToken( - accessToken, clientPrincipal); + OAuth2TokenRevocationAuthenticationToken tokenRevocationAuthentication = new OAuth2TokenRevocationAuthenticationToken( + accessToken, clientPrincipal); AuthenticationSuccessHandler authenticationSuccessHandler = mock(AuthenticationSuccessHandler.class); this.filter.setAuthenticationSuccessHandler(authenticationSuccessHandler); @@ -268,8 +261,8 @@ public class OAuth2TokenRevocationEndpointFilterTests { @Test public void doFilterWhenCustomAuthenticationFailureHandlerThenUsed() throws Exception { RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - Authentication clientPrincipal = new OAuth2ClientAuthenticationToken( - registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + Authentication clientPrincipal = new OAuth2ClientAuthenticationToken(registeredClient, + ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); AuthenticationFailureHandler authenticationFailureHandler = mock(AuthenticationFailureHandler.class); this.filter.setAuthenticationFailureHandler(authenticationFailureHandler); @@ -308,8 +301,8 @@ public class OAuth2TokenRevocationEndpointFilterTests { } private OAuth2Error readError(MockHttpServletResponse response) throws Exception { - MockClientHttpResponse httpResponse = new MockClientHttpResponse( - response.getContentAsByteArray(), HttpStatus.valueOf(response.getStatus())); + MockClientHttpResponse httpResponse = new MockClientHttpResponse(response.getContentAsByteArray(), + HttpStatus.valueOf(response.getStatus())); return this.errorHttpResponseConverter.read(OAuth2Error.class, httpResponse); } @@ -323,4 +316,5 @@ public class OAuth2TokenRevocationEndpointFilterTests { return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverterTests.java index 7b5a222c..028abe14 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretBasicAuthenticationConverterTests.java @@ -43,6 +43,7 @@ import static org.assertj.core.api.Assertions.entry; * @author Joe Grandja */ public class ClientSecretBasicAuthenticationConverterTests { + private ClientSecretBasicAuthenticationConverter converter = new ClientSecretBasicAuthenticationConverter(); @Test @@ -64,43 +65,44 @@ public class ClientSecretBasicAuthenticationConverterTests { public void convertWhenAuthorizationHeaderBasicWithMissingCredentialsThenThrowOAuth2AuthenticationException() { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(HttpHeaders.AUTHORIZATION, "Basic "); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test public void convertWhenAuthorizationHeaderBasicWithInvalidBase64ThenThrowOAuth2AuthenticationException() { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(HttpHeaders.AUTHORIZATION, "Basic clientId:secret"); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test - public void convertWhenAuthorizationHeaderBasicWithMissingSecretThenThrowOAuth2AuthenticationException() throws Exception { + public void convertWhenAuthorizationHeaderBasicWithMissingSecretThenThrowOAuth2AuthenticationException() + throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth("clientId", "")); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test - public void convertWhenAuthorizationHeaderBasicWithValidCredentialsThenReturnClientAuthenticationToken() throws Exception { + public void convertWhenAuthorizationHeaderBasicWithValidCredentialsThenReturnClientAuthenticationToken() + throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth("clientId", "secret")); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("clientId"); assertThat(authentication.getCredentials()).isEqualTo("secret"); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); } @Test @@ -108,16 +110,16 @@ public class ClientSecretBasicAuthenticationConverterTests { MockHttpServletRequest request = createPkceTokenRequest(); request.addParameter("custom-param", "custom-value-1", "custom-value-2"); request.addHeader(HttpHeaders.AUTHORIZATION, "Basic " + encodeBasicAuth("clientId", "secret")); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("clientId"); assertThat(authentication.getCredentials()).isEqualTo("secret"); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); - assertThat(authentication.getAdditionalParameters()) - .containsOnly( - entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), - entry(OAuth2ParameterNames.CODE, "code"), - entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), - entry("custom-param", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_BASIC); + assertThat(authentication.getAdditionalParameters()).containsOnly( + entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), + entry(OAuth2ParameterNames.CODE, "code"), entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), + entry("custom-param", new String[] { "custom-value-1", "custom-value-2" })); } private static String encodeBasicAuth(String clientId, String secret) throws Exception { @@ -135,4 +137,5 @@ public class ClientSecretBasicAuthenticationConverterTests { request.addParameter(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"); return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java index 894fb409..e1d91eca 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/ClientSecretPostAuthenticationConverterTests.java @@ -37,6 +37,7 @@ import static org.assertj.core.api.Assertions.entry; * @author Anoop Garlapati */ public class ClientSecretPostAuthenticationConverterTests { + private final ClientSecretPostAuthenticationConverter converter = new ClientSecretPostAuthenticationConverter(); @Test @@ -51,11 +52,10 @@ public class ClientSecretPostAuthenticationConverterTests { MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1"); request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-2"); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test @@ -72,11 +72,10 @@ public class ClientSecretPostAuthenticationConverterTests { request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1"); request.addParameter(OAuth2ParameterNames.CLIENT_SECRET, "client-secret-1"); request.addParameter(OAuth2ParameterNames.CLIENT_SECRET, "client-secret-2"); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test @@ -84,10 +83,12 @@ public class ClientSecretPostAuthenticationConverterTests { MockHttpServletRequest request = new MockHttpServletRequest(); request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1"); request.addParameter(OAuth2ParameterNames.CLIENT_SECRET, "client-secret"); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("client-1"); assertThat(authentication.getCredentials()).isEqualTo("client-secret"); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST); } @Test @@ -96,16 +97,16 @@ public class ClientSecretPostAuthenticationConverterTests { request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-1"); request.addParameter(OAuth2ParameterNames.CLIENT_SECRET, "client-secret"); request.addParameter("custom-param", "custom-value-1", "custom-value-2"); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("client-1"); assertThat(authentication.getCredentials()).isEqualTo("client-secret"); - assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST); - assertThat(authentication.getAdditionalParameters()) - .containsOnly( - entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), - entry(OAuth2ParameterNames.CODE, "code"), - entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), - entry("custom-param", new String[] { "custom-value-1", "custom-value-2" })); + assertThat(authentication.getClientAuthenticationMethod()) + .isEqualTo(ClientAuthenticationMethod.CLIENT_SECRET_POST); + assertThat(authentication.getAdditionalParameters()).containsOnly( + entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), + entry(OAuth2ParameterNames.CODE, "code"), entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), + entry("custom-param", new String[] { "custom-value-1", "custom-value-2" })); } private static MockHttpServletRequest createPkceTokenRequest() { @@ -115,4 +116,5 @@ public class ClientSecretPostAuthenticationConverterTests { request.addParameter(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"); return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverterTests.java index 13fb6488..1dcf3cf5 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/JwtClientAssertionAuthenticationConverterTests.java @@ -35,7 +35,9 @@ import static org.assertj.core.api.Assertions.entry; * @author Rafal Lewczuk */ public class JwtClientAssertionAuthenticationConverterTests { + private static final String JWT_BEARER_TYPE = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; + private final JwtClientAssertionAuthenticationConverter converter = new JwtClientAssertionAuthenticationConverter(); @Test @@ -109,24 +111,22 @@ public class JwtClientAssertionAuthenticationConverterTests { request.addParameter(OAuth2ParameterNames.CODE, "code"); request.addParameter("custom-param-1", "custom-value-1"); request.addParameter("custom-param-2", "custom-value-1", "custom-value-2"); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("client-1"); assertThat(authentication.getCredentials()).isEqualTo("jwt-assertion"); assertThat(authentication.getClientAuthenticationMethod().getValue()).isEqualTo(JWT_BEARER_TYPE); - assertThat(authentication.getAdditionalParameters()) - .containsOnly( - entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), - entry(OAuth2ParameterNames.CODE, "code"), - entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] {"custom-value-1", "custom-value-2"})); + assertThat(authentication.getAdditionalParameters()).containsOnly( + entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), + entry(OAuth2ParameterNames.CODE, "code"), entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); } private void assertThrown(MockHttpServletRequest request, String errorCode) { - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(errorCode); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(errorCode); } } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverterTests.java index dfa5ed68..dec8f509 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationConsentAuthenticationConverterTests.java @@ -41,9 +41,13 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Steve Riesenberg */ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { + private static final String VERIFICATION_URI = "/oauth2/device_verification"; + private static final String USER_CODE = "BCDF-GHJK"; + private static final String CLIENT_ID = "client-1"; + private static final String STATE = "abc123"; private OAuth2DeviceAuthorizationConsentAuthenticationConverter converter; @@ -220,8 +224,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { request.addParameter(OAuth2ParameterNames.CLIENT_ID, CLIENT_ID); request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE); - OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI); assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID); @@ -242,8 +246,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI); assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID); @@ -268,17 +272,16 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI); assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(authentication.getUserCode()).isEqualTo(USER_CODE); assertThat(authentication.getScopes()).containsExactly("message.read", "message.write"); - assertThat(authentication.getAdditionalParameters()) - .containsExactly(entry("param-1", "value-1"), - entry("param-2", new String[] {"value-1", "value-2"})); + assertThat(authentication.getAdditionalParameters()).containsExactly(entry("param-1", "value-1"), + entry("param-2", new String[] { "value-1", "value-2" })); } @Test @@ -292,8 +295,8 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = - (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationConsentAuthenticationToken authentication = (OAuth2DeviceAuthorizationConsentAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getAuthorizationUri()).endsWith(VERIFICATION_URI); assertThat(authentication.getClientId()).isEqualTo(CLIENT_ID); @@ -309,4 +312,5 @@ public class OAuth2DeviceAuthorizationConsentAuthenticationConverterTests { request.setRequestURI(VERIFICATION_URI); return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverterTests.java index 220331ee..ab641ce2 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceAuthorizationRequestAuthenticationConverterTests.java @@ -40,7 +40,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Steve Riesenberg */ public class OAuth2DeviceAuthorizationRequestAuthenticationConverterTests { + private static final String AUTHORIZATION_URI = "/oauth2/device_authorization"; + private static final String CLIENT_ID = "client-1"; private OAuth2DeviceAuthorizationRequestAuthenticationConverter converter; @@ -80,8 +82,8 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(authentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI); @@ -101,15 +103,14 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = - (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter.convert(request); + OAuth2DeviceAuthorizationRequestAuthenticationToken authentication = (OAuth2DeviceAuthorizationRequestAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(authentication.getAuthorizationUri()).endsWith(AUTHORIZATION_URI); assertThat(authentication.getScopes()).containsExactly("message.read", "message.write"); - assertThat(authentication.getAdditionalParameters()) - .containsExactly(entry("param-1", "value-1"), - entry("param-2", new String[] {"value-1", "value-2"})); + assertThat(authentication.getAdditionalParameters()).containsExactly(entry("param-1", "value-1"), + entry("param-2", new String[] { "value-1", "value-2" })); } private static MockHttpServletRequest createRequest() { @@ -118,4 +119,5 @@ public class OAuth2DeviceAuthorizationRequestAuthenticationConverterTests { request.setRequestURI(AUTHORIZATION_URI); return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverterTests.java index f1a52138..4aed04fa 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceCodeAuthenticationConverterTests.java @@ -42,8 +42,11 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Steve Riesenberg */ public class OAuth2DeviceCodeAuthenticationConverterTests { + private static final String CLIENT_ID = "client-1"; + private static final String TOKEN_URI = "/oauth2/token"; + private static final String DEVICE_CODE = "EfYu_0jEL"; private OAuth2DeviceCodeAuthenticationConverter converter; @@ -108,14 +111,13 @@ public class OAuth2DeviceCodeAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken(CLIENT_ID, null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceCodeAuthenticationToken authentication = - (OAuth2DeviceCodeAuthenticationToken) this.converter.convert(request); + OAuth2DeviceCodeAuthenticationToken authentication = (OAuth2DeviceCodeAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getDeviceCode()).isEqualTo(DEVICE_CODE); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); - assertThat(authentication.getAdditionalParameters()) - .containsExactly(entry("param-1", "value-1"), - entry("param-2", new String[] {"value-1", "value-2"})); + assertThat(authentication.getAdditionalParameters()).containsExactly(entry("param-1", "value-1"), + entry("param-2", new String[] { "value-1", "value-2" })); } private static MockHttpServletRequest createRequest() { @@ -124,4 +126,5 @@ public class OAuth2DeviceCodeAuthenticationConverterTests { request.setRequestURI(TOKEN_URI); return request; } + } diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverterTests.java index 0b8ba1ef..a8e2b7ad 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/OAuth2DeviceVerificationAuthenticationConverterTests.java @@ -43,7 +43,9 @@ import static org.assertj.core.api.Assertions.assertThatExceptionOfType; * @author Steve Riesenberg */ public class OAuth2DeviceVerificationAuthenticationConverterTests { + private static final String VERIFICATION_URI = "/oauth2/device_verification"; + private static final String USER_CODE = "BCDF-GHJK"; private OAuth2DeviceVerificationAuthenticationConverter converter; @@ -134,8 +136,8 @@ public class OAuth2DeviceVerificationAuthenticationConverterTests { request.addParameter(OAuth2ParameterNames.USER_CODE, USER_CODE.toLowerCase().replace("-", " . ")); updateQueryString(request); - OAuth2DeviceVerificationAuthenticationToken authentication = - (OAuth2DeviceVerificationAuthenticationToken) this.converter.convert(request); + OAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getPrincipal()).isInstanceOf(AnonymousAuthenticationToken.class); assertThat(authentication.getUserCode()).isEqualTo(USER_CODE); @@ -152,8 +154,8 @@ public class OAuth2DeviceVerificationAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceVerificationAuthenticationToken authentication = - (OAuth2DeviceVerificationAuthenticationToken) this.converter.convert(request); + OAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(authentication.getUserCode()).isEqualTo(USER_CODE); @@ -172,14 +174,13 @@ public class OAuth2DeviceVerificationAuthenticationConverterTests { securityContext.setAuthentication(new TestingAuthenticationToken("user", null)); SecurityContextHolder.setContext(securityContext); - OAuth2DeviceVerificationAuthenticationToken authentication = - (OAuth2DeviceVerificationAuthenticationToken) this.converter.convert(request); + OAuth2DeviceVerificationAuthenticationToken authentication = (OAuth2DeviceVerificationAuthenticationToken) this.converter + .convert(request); assertThat(authentication).isNotNull(); assertThat(authentication.getPrincipal()).isInstanceOf(TestingAuthenticationToken.class); assertThat(authentication.getUserCode()).isEqualTo(USER_CODE); - assertThat(authentication.getAdditionalParameters()) - .containsExactly(entry("param-1", "value-1"), - entry("param-2", new String[] {"value-1", "value-2"})); + assertThat(authentication.getAdditionalParameters()).containsExactly(entry("param-1", "value-1"), + entry("param-2", new String[] { "value-1", "value-2" })); } private static MockHttpServletRequest createRequest() { diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverterTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverterTests.java index 0278e597..5106e240 100644 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverterTests.java +++ b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/web/authentication/PublicClientAuthenticationConverterTests.java @@ -37,6 +37,7 @@ import static org.assertj.core.api.Assertions.entry; * @author Joe Grandja */ public class PublicClientAuthenticationConverterTests { + private PublicClientAuthenticationConverter converter = new PublicClientAuthenticationConverter(); @Test @@ -50,33 +51,30 @@ public class PublicClientAuthenticationConverterTests { public void convertWhenMissingClientIdThenInvalidRequestError() { MockHttpServletRequest request = createPkceTokenRequest(); request.removeParameter(OAuth2ParameterNames.CLIENT_ID); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test public void convertWhenMultipleClientIdThenInvalidRequestError() { MockHttpServletRequest request = createPkceTokenRequest(); request.addParameter(OAuth2ParameterNames.CLIENT_ID, "client-2"); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test public void convertWhenMultipleCodeVerifierThenInvalidRequestError() { MockHttpServletRequest request = createPkceTokenRequest(); request.addParameter(PkceParameterNames.CODE_VERIFIER, "code-verifier-2"); - assertThatThrownBy(() -> this.converter.convert(request)) - .isInstanceOf(OAuth2AuthenticationException.class) - .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) - .extracting("errorCode") - .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); + assertThatThrownBy(() -> this.converter.convert(request)).isInstanceOf(OAuth2AuthenticationException.class) + .extracting(ex -> ((OAuth2AuthenticationException) ex).getError()) + .extracting("errorCode") + .isEqualTo(OAuth2ErrorCodes.INVALID_REQUEST); } @Test @@ -84,16 +82,15 @@ public class PublicClientAuthenticationConverterTests { MockHttpServletRequest request = createPkceTokenRequest(); request.addParameter("custom-param-1", "custom-value-1"); request.addParameter("custom-param-2", "custom-value-1", "custom-value-2"); - OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter.convert(request); + OAuth2ClientAuthenticationToken authentication = (OAuth2ClientAuthenticationToken) this.converter + .convert(request); assertThat(authentication.getPrincipal()).isEqualTo("client-1"); assertThat(authentication.getClientAuthenticationMethod()).isEqualTo(ClientAuthenticationMethod.NONE); - assertThat(authentication.getAdditionalParameters()) - .containsOnly( - entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), - entry(OAuth2ParameterNames.CODE, "code"), - entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), - entry("custom-param-1", "custom-value-1"), - entry("custom-param-2", new String[] {"custom-value-1", "custom-value-2"})); + assertThat(authentication.getAdditionalParameters()).containsOnly( + entry(OAuth2ParameterNames.GRANT_TYPE, AuthorizationGrantType.AUTHORIZATION_CODE.getValue()), + entry(OAuth2ParameterNames.CODE, "code"), entry(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"), + entry("custom-param-1", "custom-value-1"), + entry("custom-param-2", new String[] { "custom-value-1", "custom-value-2" })); } private static MockHttpServletRequest createPkceTokenRequest() { @@ -104,4 +101,5 @@ public class PublicClientAuthenticationConverterTests { request.addParameter(PkceParameterNames.CODE_VERIFIER, "code-verifier-1"); return request; } + }