diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java index 1bf80d93..f95e347a 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,19 +15,21 @@ */ package org.springframework.security.oauth2.server.authorization.authentication; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; +import java.util.function.Consumer; + import org.springframework.lang.Nullable; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; import org.springframework.util.Assert; -import java.util.Map; -import java.util.function.Consumer; - /** * An {@link OAuth2AuthenticationContext} that holds an {@link OAuth2ClientCredentialsAuthenticationToken} and additional information - * and is used when validating the OAuth 2.0 Authorization Request used in the Client Credentials Grant. + * and is used when validating the OAuth 2.0 Client Credentials Grant Request. * * @author Adam Pilling - * @since 1.3.0 + * @since 1.3 * @see OAuth2AuthenticationContext * @see OAuth2ClientCredentialsAuthenticationToken * @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer) @@ -36,7 +38,7 @@ public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth private final Map context; private OAuth2ClientCredentialsAuthenticationContext(Map context) { - this.context = Map.copyOf(context); + this.context = Collections.unmodifiableMap(new HashMap<>(context)); } @SuppressWarnings("unchecked") diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationException.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationException.java deleted file mode 100644 index fca40b5c..00000000 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationException.java +++ /dev/null @@ -1,58 +0,0 @@ -/* - * Copyright 2020-2021 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.server.authorization.authentication; - -import org.springframework.lang.Nullable; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.core.OAuth2AuthenticationException; -import org.springframework.security.oauth2.core.OAuth2Error; - -/** - * This exception is thrown by {@link OAuth2ClientCredentialsAuthenticationProvider} - * when an attempt to authenticate the OAuth 2.0 Authorization Request (or Consent) fails. - * - * @author Adam Pilling - * @since 1.3.0 - * @see OAuth2ClientCredentialsAuthenticationToken - * @see OAuth2ClientCredentialsAuthenticationProvider - */ -public class OAuth2ClientCredentialsAuthenticationException extends OAuth2AuthenticationException { - private final OAuth2ClientCredentialsAuthenticationToken authorizationCodeRequestAuthentication; - - /** - * Constructs an {@code OAuth2ClientCredentialsAuthenticationException} 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) - */ - public OAuth2ClientCredentialsAuthenticationException( - OAuth2Error error, - @Nullable OAuth2ClientCredentialsAuthenticationToken authorizationCodeRequestAuthentication) { - super(error); - this.authorizationCodeRequestAuthentication = authorizationCodeRequestAuthentication; - } - - /** - * 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 - public OAuth2ClientCredentialsAuthenticationToken getClientCredentialsAuthentication() { - return this.authorizationCodeRequestAuthentication; - } - -} 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 6399321b..08466df1 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,8 +15,13 @@ */ package org.springframework.security.oauth2.server.authorization.authentication; +import java.util.LinkedHashSet; +import java.util.Set; +import java.util.function.Consumer; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.security.authentication.AuthenticationProvider; import org.springframework.security.core.Authentication; import org.springframework.security.core.AuthenticationException; @@ -37,9 +42,6 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; import org.springframework.util.Assert; -import java.util.Set; -import java.util.function.Consumer; - import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient; /** @@ -99,14 +101,14 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth OAuth2ClientCredentialsAuthenticationContext.with(clientCredentialsAuthentication) .registeredClient(registeredClient) .build(); - authenticationValidator.accept(authenticationContext); + this.authenticationValidator.accept(authenticationContext); + + Set authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes()); if (this.logger.isTraceEnabled()) { this.logger.trace("Validated token request parameters"); } - Set authorizedScopes = Set.copyOf(clientCredentialsAuthentication.getScopes()); - // @formatter:off OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder() .registeredClient(registeredClient) @@ -167,16 +169,15 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth /** * Sets the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext} - * and is responsible for validating specific OAuth 2.0 Client Credentials parameters + * and is responsible for validating specific OAuth 2.0 Client Credentials Grant Request parameters * associated in the {@link OAuth2ClientCredentialsAuthenticationToken}. * The default authentication validator is {@link OAuth2ClientCredentialsAuthenticationValidator}. * *

- * NOTE: The authentication validator MUST throw {@link OAuth2ClientCredentialsAuthenticationException} if validation fails. + * NOTE: The authentication validator MUST throw {@link OAuth2AuthenticationException} if validation fails. * - * @param authenticationValidator the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext} - * and is responsible for validating specific OAuth 2.0 Authorization Request parameters - * @since 1.3.0 + * @param authenticationValidator the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext} and is responsible for validating specific OAuth 2.0 Client Credentials Grant Request parameters + * @since 1.3 */ public void setAuthenticationValidator(Consumer authenticationValidator) { Assert.notNull(authenticationValidator, "authenticationValidator cannot be null"); diff --git a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java index 82181a5d..31146ba7 100644 --- a/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java +++ b/oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java @@ -1,5 +1,5 @@ /* - * Copyright 2020-2023 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,37 +15,34 @@ */ package org.springframework.security.oauth2.server.authorization.authentication; +import java.util.Set; +import java.util.function.Consumer; + import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory; + import org.springframework.core.log.LogMessage; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.core.OAuth2Error; +import org.springframework.security.oauth2.core.OAuth2AuthenticationException; import org.springframework.security.oauth2.core.OAuth2ErrorCodes; -import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames; import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; -import java.util.Set; -import java.util.function.Consumer; - /** * A {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext} * containing an {@link OAuth2ClientCredentialsAuthenticationToken} * and is the default {@link OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer) authentication validator} - * used for validating specific OAuth 2.0 Client Credentials parameters used in the Client Credentials Grant. + * used for validating specific OAuth 2.0 Client Credentials Grant Request parameters. * *

- * The default compares the provided scopes with those configured in the RegisteredClient. - * If validation fails, an {@link OAuth2ClientCredentialsAuthenticationException} is thrown. + * The default implementation validates {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}. + * If validation fails, an {@link OAuth2AuthenticationException} is thrown. * * @author Adam Pilling - * @since 1.3.0 + * @since 1.3 * @see OAuth2ClientCredentialsAuthenticationContext - * @see RegisteredClient * @see OAuth2ClientCredentialsAuthenticationToken * @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer) */ public final class OAuth2ClientCredentialsAuthenticationValidator 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(OAuth2ClientCredentialsAuthenticationValidator.class); /** @@ -62,32 +59,19 @@ public final class OAuth2ClientCredentialsAuthenticationValidator implements Con } private static void validateScope(OAuth2ClientCredentialsAuthenticationContext authenticationContext) { - OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthenticationToken = + OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthentication = authenticationContext.getAuthentication(); RegisteredClient registeredClient = authenticationContext.getRegisteredClient(); - Set requestedScopes = clientCredentialsAuthenticationToken.getScopes(); + Set requestedScopes = clientCredentialsAuthentication.getScopes(); Set allowedScopes = registeredClient.getScopes(); if (!requestedScopes.isEmpty() && !allowedScopes.containsAll(requestedScopes)) { if (LOGGER.isDebugEnabled()) { LOGGER.debug(LogMessage.format("Invalid request: requested scope is not allowed" + " for registered client '%s'", registeredClient.getId())); } - throwError(OAuth2ErrorCodes.INVALID_SCOPE, OAuth2ParameterNames.SCOPE, clientCredentialsAuthenticationToken); + throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE); } } - private static void throwError(String errorCode, String parameterName, - OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthenticationToken) { - OAuth2Error error = new OAuth2Error(errorCode, "OAuth 2.0 Parameter: " + parameterName, ERROR_URI); - OAuth2ClientCredentialsAuthenticationToken authorizationCodeRequestAuthenticationResult = - new OAuth2ClientCredentialsAuthenticationToken( - (Authentication) clientCredentialsAuthenticationToken.getPrincipal(), - clientCredentialsAuthenticationToken.getScopes(), - clientCredentialsAuthenticationToken.getAdditionalParameters()); - authorizationCodeRequestAuthenticationResult.setAuthenticated(true); - - throw new OAuth2ClientCredentialsAuthenticationException(error, authorizationCodeRequestAuthenticationResult); - } - } 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 12857847..fa23a173 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 @@ -20,6 +20,7 @@ import java.util.List; import java.util.function.Consumer; import jakarta.servlet.http.HttpServletRequest; + import org.springframework.http.HttpMethod; import org.springframework.security.authentication.AuthenticationManager; import org.springframework.security.authentication.AuthenticationProvider; @@ -216,7 +217,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure return authenticationConverters; } - private List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { + private static List createDefaultAuthenticationProviders(HttpSecurity httpSecurity) { List authenticationProviders = new ArrayList<>(); OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContextTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContextTests.java deleted file mode 100644 index 2ceb51e1..00000000 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContextTests.java +++ /dev/null @@ -1,88 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.server.authorization.authentication; - -import java.security.Principal; -import java.util.Map; -import java.util.Set; - -import org.junit.jupiter.api.Test; - -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; -import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; -import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -/** - * Tests for {@link OAuth2ClientCredentialsAuthenticationContext}. - * - * @author Steve Riesenberg - * @author Joe Grandja - */ -public class OAuth2ClientCredentialsAuthenticationContextTests { - private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); - private final Authentication principal = this.authorization.getAttribute(Principal.class.getName()); - private final OAuth2ClientCredentialsAuthenticationToken authorizationConsentAuthentication = - new OAuth2ClientCredentialsAuthenticationToken(this.principal, Set.of("a_scope"), Map.of("a_key", "a_value")); - - @Test - public void withWhenAuthenticationNullThenThrowIllegalArgumentException() { - assertThatThrownBy(() -> OAuth2ClientCredentialsAuthenticationContext.with(null)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("authentication cannot be null"); - } - - @Test - public void setWhenValueNullThenThrowIllegalArgumentException() { - OAuth2ClientCredentialsAuthenticationContext.Builder builder = - OAuth2ClientCredentialsAuthenticationContext.with(this.authorizationConsentAuthentication); - - assertThatThrownBy(() -> builder.registeredClient(null)) - .isInstanceOf(IllegalArgumentException.class); - assertThatThrownBy(() -> builder.put(null, "")) - .isInstanceOf(IllegalArgumentException.class); - } - - @Test - public void buildWhenRequiredValueNullThenThrowIllegalArgumentException() { - OAuth2ClientCredentialsAuthenticationContext.Builder builder = - OAuth2ClientCredentialsAuthenticationContext.with(this.authorizationConsentAuthentication); - assertThatThrownBy(builder::build) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("registeredClient cannot be null"); - } - - @Test - public void buildWhenAllValuesProvidedThenAllValuesAreSet() { - OAuth2ClientCredentialsAuthenticationContext context = - OAuth2ClientCredentialsAuthenticationContext.with(this.authorizationConsentAuthentication) - .registeredClient(this.registeredClient) - .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.getRegisteredClient()).isEqualTo(this.registeredClient); - 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/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java index 36712a9a..6b9d3fc4 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 @@ -1,5 +1,5 @@ /* - * Copyright 2020-2022 the original author or authors. + * Copyright 2020-2024 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -15,10 +15,17 @@ */ package org.springframework.security.oauth2.server.authorization.authentication; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.Set; +import java.util.function.Consumer; + import org.junit.jupiter.api.AfterEach; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.mockito.ArgumentCaptor; + import org.springframework.security.authentication.TestingAuthenticationToken; import org.springframework.security.core.Authentication; import org.springframework.security.oauth2.core.AuthorizationGrantType; @@ -50,12 +57,6 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenCustomizer; import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.Set; -import java.util.function.Consumer; - import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.mockito.ArgumentMatchers.any; @@ -132,6 +133,13 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { assertThat(this.authenticationProvider.supports(OAuth2AuthorizationCodeAuthenticationToken.class)).isFalse(); } + @Test + public void setAuthenticationValidatorWhenNullThenThrowIllegalArgumentException() { + assertThatThrownBy(() -> this.authenticationProvider.setAuthenticationValidator(null)) + .isInstanceOf(IllegalArgumentException.class) + .hasMessage("authenticationValidator cannot be null"); + } + @Test public void authenticateWhenClientPrincipalNotOAuth2ClientAuthenticationTokenThenThrowOAuth2AuthenticationException() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); @@ -211,16 +219,6 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScope); } - @Test - public void authenticateWhenCustomAuthenticationValidatorThenInvokeValidator() { - Consumer validator = mock(Consumer.class); - this.authenticationProvider.setAuthenticationValidator(validator); - - authenticateWhenScopeRequestedThenAccessTokenContainsScope(); - - verify(validator).accept(any(OAuth2ClientCredentialsAuthenticationContext.class)); - } - @Test public void authenticateWhenNoScopeRequestedThenAccessTokenDoesNotContainScope() { RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); @@ -314,6 +312,25 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { verify(this.accessTokenCustomizer).customize(any()); } + @Test + public void authenticateWhenCustomAuthenticationValidatorThenUsed() { + RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build(); + OAuth2ClientAuthenticationToken clientPrincipal = new OAuth2ClientAuthenticationToken( + registeredClient, ClientAuthenticationMethod.CLIENT_SECRET_BASIC, registeredClient.getClientSecret()); + OAuth2ClientCredentialsAuthenticationToken authentication = + new OAuth2ClientCredentialsAuthenticationToken(clientPrincipal, registeredClient.getScopes(), null); + + @SuppressWarnings("unchecked") + Consumer authenticationValidator = mock(Consumer.class); + this.authenticationProvider.setAuthenticationValidator(authenticationValidator); + + when(this.jwtEncoder.encode(any())).thenReturn(createJwt(registeredClient.getScopes())); + + this.authenticationProvider.authenticate(authentication); + + verify(authenticationValidator).accept(any(OAuth2ClientCredentialsAuthenticationContext.class)); + } + private static Jwt createJwt(Set scope) { Instant issuedAt = Instant.now(); Instant expiresAt = issuedAt.plus(1, ChronoUnit.HOURS); diff --git a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidatorTest.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidatorTest.java deleted file mode 100644 index 18a3dd6d..00000000 --- a/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidatorTest.java +++ /dev/null @@ -1,70 +0,0 @@ -/* - * Copyright 2020-2022 the original author or authors. - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * https://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package org.springframework.security.oauth2.server.authorization.authentication; - -import org.junit.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; -import org.springframework.security.core.Authentication; -import org.springframework.security.oauth2.server.authorization.OAuth2Authorization; -import org.springframework.security.oauth2.server.authorization.TestOAuth2Authorizations; -import org.springframework.security.oauth2.server.authorization.client.RegisteredClient; -import org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients; - -import java.security.Principal; -import java.util.HashSet; -import java.util.Map; -import java.util.Set; -import java.util.stream.Stream; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatNoException; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients.SCOPE_1; -import static org.springframework.security.oauth2.server.authorization.client.TestRegisteredClients.SCOPE_2; - -public class OAuth2ClientCredentialsAuthenticationValidatorTest { - private final RegisteredClient registeredClient = TestRegisteredClients.registeredClient().build(); - private final OAuth2Authorization authorization = TestOAuth2Authorizations.authorization(this.registeredClient).build(); - private final Authentication principal = this.authorization.getAttribute(Principal.class.getName()); - private final OAuth2ClientCredentialsAuthenticationValidator validator = new OAuth2ClientCredentialsAuthenticationValidator(); - - @ParameterizedTest - @MethodSource("validScopes") - public void acceptWhenRequestScopesAreEmptyOrValidThenDoesNotThrowException(Set testScopes) { - OAuth2ClientCredentialsAuthenticationToken token = - new OAuth2ClientCredentialsAuthenticationToken(this.principal, testScopes, Map.of()); - OAuth2ClientCredentialsAuthenticationContext context = OAuth2ClientCredentialsAuthenticationContext.with(token).registeredClient(registeredClient).build(); - - assertThatNoException().isThrownBy(() -> validator.accept(context)); - } - - @Test - public void acceptWhenRequestScopesAreNotAllValidThenThrowException() { - OAuth2ClientCredentialsAuthenticationToken token = - new OAuth2ClientCredentialsAuthenticationToken(this.principal, Set.of(SCOPE_1, SCOPE_2), Map.of()); - OAuth2ClientCredentialsAuthenticationContext context = OAuth2ClientCredentialsAuthenticationContext.with(token).registeredClient(registeredClient).build(); - - assertThatThrownBy(() -> validator.accept(context)) - .isInstanceOfSatisfying(OAuth2ClientCredentialsAuthenticationException.class, - t -> assertThat(t.getClientCredentialsAuthentication()).isEqualTo(token)); - } - - static Stream validScopes() { - return Stream.of(Arguments.of(new HashSet<>()), Arguments.of(Set.of(SCOPE_1))); - } -} 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 ac640192..90151201 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 @@ -15,19 +15,17 @@ */ package org.springframework.security.oauth2.server.authorization.client; +import java.time.Instant; +import java.time.temporal.ChronoUnit; + import org.springframework.security.oauth2.core.AuthorizationGrantType; import org.springframework.security.oauth2.core.ClientAuthenticationMethod; import org.springframework.security.oauth2.server.authorization.settings.ClientSettings; -import java.time.Instant; -import java.time.temporal.ChronoUnit; - /** * @author Anoop Garlapati */ public class TestRegisteredClients { - public static final String SCOPE_1 = "scope1"; - public static final String SCOPE_2 = "scope2"; public static RegisteredClient.Builder registeredClient() { return RegisteredClient.withId("registration-1") @@ -41,7 +39,7 @@ public class TestRegisteredClients { .redirectUri("https://example.com/callback-2") .redirectUri("https://example.com/callback-3") .postLogoutRedirectUri("https://example.com/oidc-post-logout") - .scope(SCOPE_1); + .scope("scope1"); } public static RegisteredClient.Builder registeredClient2() { @@ -56,8 +54,8 @@ public class TestRegisteredClients { .clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST) .redirectUri("https://example.com") .postLogoutRedirectUri("https://example.com/oidc-post-logout") - .scope(SCOPE_1) - .scope(SCOPE_2); + .scope("scope1") + .scope("scope2"); } public static RegisteredClient.Builder registeredPublicClient() { @@ -67,7 +65,7 @@ public class TestRegisteredClients { .authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE) .clientAuthenticationMethod(ClientAuthenticationMethod.NONE) .redirectUri("https://example.com") - .scope(SCOPE_1) + .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/configurers/OidcClientRegistrationTests.java b/oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java index 0a828c3e..be1ea066 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 @@ -15,10 +15,21 @@ */ package org.springframework.security.oauth2.server.authorization.config.annotation.web.configurers; +import java.time.Instant; +import java.time.temporal.ChronoUnit; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.function.Consumer; + +import javax.crypto.spec.SecretKeySpec; + +import jakarta.servlet.http.HttpServletResponse; + import com.nimbusds.jose.jwk.JWKSet; import com.nimbusds.jose.jwk.source.JWKSource; import com.nimbusds.jose.proc.SecurityContext; -import jakarta.servlet.http.HttpServletResponse; import okhttp3.mockwebserver.MockResponse; import okhttp3.mockwebserver.MockWebServer; import org.junit.jupiter.api.AfterAll; @@ -28,6 +39,7 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.mockito.ArgumentCaptor; + import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @@ -99,15 +111,6 @@ import org.springframework.test.web.servlet.MvcResult; import org.springframework.util.CollectionUtils; import org.springframework.web.util.UriComponentsBuilder; -import javax.crypto.spec.SecretKeySpec; -import java.time.Instant; -import java.time.temporal.ChronoUnit; -import java.util.Collections; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.function.Consumer; - import static org.assertj.core.api.Assertions.assertThat; import static org.hamcrest.CoreMatchers.containsString; import static org.mockito.ArgumentMatchers.any;