Browse Source

Allow configurable scope validation strategy in OAuth2ClientCredentialsAuthenticationProvider

Closes gh-1377
pull/1429/head
adamleantech 2 years ago committed by Joe Grandja
parent
commit
5c3f1cb691
  1. 105
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java
  2. 58
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationException.java
  3. 45
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java
  4. 93
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java
  5. 3
      oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java
  6. 88
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContextTests.java
  7. 22
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java
  8. 70
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidatorTest.java
  9. 16
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java
  10. 23
      oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java

105
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContext.java

@ -0,0 +1,105 @@ @@ -0,0 +1,105 @@
/*
* 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.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.
*
* @author Adam Pilling
* @since 1.3.0
* @see OAuth2AuthenticationContext
* @see OAuth2ClientCredentialsAuthenticationToken
* @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)
*/
public final class OAuth2ClientCredentialsAuthenticationContext implements OAuth2AuthenticationContext {
private final Map<Object, Object> context;
private OAuth2ClientCredentialsAuthenticationContext(Map<Object, Object> context) {
this.context = Map.copyOf(context);
}
@SuppressWarnings("unchecked")
@Nullable
@Override
public <V> V get(Object key) {
return hasKey(key) ? (V) this.context.get(key) : null;
}
@Override
public boolean hasKey(Object key) {
Assert.notNull(key, "key cannot be null");
return this.context.containsKey(key);
}
/**
* Returns the {@link RegisteredClient registered client}.
*
* @return the {@link RegisteredClient}
*/
public RegisteredClient getRegisteredClient() {
return get(RegisteredClient.class);
}
/**
* Constructs a new {@link Builder} with the provided {@link OAuth2ClientCredentialsAuthenticationToken}.
*
* @param authentication the {@link OAuth2ClientCredentialsAuthenticationToken}
* @return the {@link Builder}
*/
public static Builder with(OAuth2ClientCredentialsAuthenticationToken authentication) {
return new Builder(authentication);
}
/**
* A builder for {@link OAuth2ClientCredentialsAuthenticationContext}.
*/
public static final class Builder extends AbstractBuilder<OAuth2ClientCredentialsAuthenticationContext, Builder> {
private Builder(OAuth2ClientCredentialsAuthenticationToken authentication) {
super(authentication);
}
/**
* Sets the {@link RegisteredClient registered client}.
*
* @param registeredClient the {@link RegisteredClient}
* @return the {@link Builder} for further configuration
*/
public Builder registeredClient(RegisteredClient registeredClient) {
return put(RegisteredClient.class, registeredClient);
}
/**
* Builds a new {@link OAuth2ClientCredentialsAuthenticationContext}.
*
* @return the {@link OAuth2ClientCredentialsAuthenticationContext}
*/
public OAuth2ClientCredentialsAuthenticationContext build() {
Assert.notNull(get(RegisteredClient.class), "registeredClient cannot be null");
return new OAuth2ClientCredentialsAuthenticationContext(getContext());
}
}
}

58
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationException.java

@ -0,0 +1,58 @@ @@ -0,0 +1,58 @@
/*
* 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;
}
}

45
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProvider.java

@ -15,13 +15,8 @@ @@ -15,13 +15,8 @@
*/
package org.springframework.security.oauth2.server.authorization.authentication;
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;
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;
@ -41,7 +36,9 @@ import org.springframework.security.oauth2.server.authorization.token.DefaultOAu @@ -41,7 +36,9 @@ import org.springframework.security.oauth2.server.authorization.token.DefaultOAu
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenContext;
import org.springframework.security.oauth2.server.authorization.token.OAuth2TokenGenerator;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import java.util.Set;
import java.util.function.Consumer;
import static org.springframework.security.oauth2.server.authorization.authentication.OAuth2AuthenticationProviderUtils.getAuthenticatedClientElseThrowInvalidClient;
@ -63,6 +60,8 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth @@ -63,6 +60,8 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
private final Log logger = LogFactory.getLog(getClass());
private final OAuth2AuthorizationService authorizationService;
private final OAuth2TokenGenerator<? extends OAuth2Token> tokenGenerator;
private Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator =
new OAuth2ClientCredentialsAuthenticationValidator();
/**
* Constructs an {@code OAuth2ClientCredentialsAuthenticationProvider} using the provided parameters.
@ -96,20 +95,18 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth @@ -96,20 +95,18 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.UNAUTHORIZED_CLIENT);
}
Set<String> authorizedScopes = Collections.emptySet();
if (!CollectionUtils.isEmpty(clientCredentialsAuthentication.getScopes())) {
for (String requestedScope : clientCredentialsAuthentication.getScopes()) {
if (!registeredClient.getScopes().contains(requestedScope)) {
throw new OAuth2AuthenticationException(OAuth2ErrorCodes.INVALID_SCOPE);
}
}
authorizedScopes = new LinkedHashSet<>(clientCredentialsAuthentication.getScopes());
}
OAuth2ClientCredentialsAuthenticationContext authenticationContext =
OAuth2ClientCredentialsAuthenticationContext.with(clientCredentialsAuthentication)
.registeredClient(registeredClient)
.build();
authenticationValidator.accept(authenticationContext);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Validated token request parameters");
}
Set<String> authorizedScopes = Set.copyOf(clientCredentialsAuthentication.getScopes());
// @formatter:off
OAuth2TokenContext tokenContext = DefaultOAuth2TokenContext.builder()
.registeredClient(registeredClient)
@ -168,4 +165,22 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth @@ -168,4 +165,22 @@ public final class OAuth2ClientCredentialsAuthenticationProvider implements Auth
return OAuth2ClientCredentialsAuthenticationToken.class.isAssignableFrom(authentication);
}
/**
* Sets the {@code Consumer} providing access to the {@link OAuth2ClientCredentialsAuthenticationContext}
* and is responsible for validating specific OAuth 2.0 Client Credentials parameters
* associated in the {@link OAuth2ClientCredentialsAuthenticationToken}.
* The default authentication validator is {@link OAuth2ClientCredentialsAuthenticationValidator}.
*
* <p>
* <b>NOTE:</b> The authentication validator MUST throw {@link OAuth2ClientCredentialsAuthenticationException} 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
*/
public void setAuthenticationValidator(Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator) {
Assert.notNull(authenticationValidator, "authenticationValidator cannot be null");
this.authenticationValidator = authenticationValidator;
}
}

93
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidator.java

@ -0,0 +1,93 @@ @@ -0,0 +1,93 @@
/*
* Copyright 2020-2023 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.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.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.
*
* <p>
* The default compares the provided scopes with those configured in the RegisteredClient.
* If validation fails, an {@link OAuth2ClientCredentialsAuthenticationException} is thrown.
*
* @author Adam Pilling
* @since 1.3.0
* @see OAuth2ClientCredentialsAuthenticationContext
* @see RegisteredClient
* @see OAuth2ClientCredentialsAuthenticationToken
* @see OAuth2ClientCredentialsAuthenticationProvider#setAuthenticationValidator(Consumer)
*/
public final class OAuth2ClientCredentialsAuthenticationValidator implements Consumer<OAuth2ClientCredentialsAuthenticationContext> {
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);
/**
* The default validator for {@link OAuth2ClientCredentialsAuthenticationToken#getScopes()}.
*/
public static final Consumer<OAuth2ClientCredentialsAuthenticationContext> DEFAULT_SCOPE_VALIDATOR =
OAuth2ClientCredentialsAuthenticationValidator::validateScope;
private final Consumer<OAuth2ClientCredentialsAuthenticationContext> authenticationValidator = DEFAULT_SCOPE_VALIDATOR;
@Override
public void accept(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {
this.authenticationValidator.accept(authenticationContext);
}
private static void validateScope(OAuth2ClientCredentialsAuthenticationContext authenticationContext) {
OAuth2ClientCredentialsAuthenticationToken clientCredentialsAuthenticationToken =
authenticationContext.getAuthentication();
RegisteredClient registeredClient = authenticationContext.getRegisteredClient();
Set<String> requestedScopes = clientCredentialsAuthenticationToken.getScopes();
Set<String> 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);
}
}
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);
}
}

3
oauth2-authorization-server/src/main/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OAuth2TokenEndpointConfigurer.java

@ -20,7 +20,6 @@ import java.util.List; @@ -20,7 +20,6 @@ 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;
@ -217,7 +216,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure @@ -217,7 +216,7 @@ public final class OAuth2TokenEndpointConfigurer extends AbstractOAuth2Configure
return authenticationConverters;
}
private static List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
private List<AuthenticationProvider> createDefaultAuthenticationProviders(HttpSecurity httpSecurity) {
List<AuthenticationProvider> authenticationProviders = new ArrayList<>();
OAuth2AuthorizationService authorizationService = OAuth2ConfigurerUtils.getAuthorizationService(httpSecurity);

88
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationContextTests.java

@ -0,0 +1,88 @@ @@ -0,0 +1,88 @@
/*
* 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.<Authentication>getAuthentication()).isEqualTo(this.authorizationConsentAuthentication);
assertThat(context.getRegisteredClient()).isEqualTo(this.registeredClient);
assertThat(context.<String>get("custom-key-1")).isEqualTo("custom-value-1");
assertThat(context.<String>get("custom-key-2")).isEqualTo("custom-value-2");
}
}

22
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationProviderTests.java

@ -15,16 +15,10 @@ @@ -15,16 +15,10 @@
*/
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 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;
@ -56,6 +50,12 @@ import org.springframework.security.oauth2.server.authorization.token.OAuth2Toke @@ -56,6 +50,12 @@ 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;
@ -211,6 +211,16 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests { @@ -211,6 +211,16 @@ public class OAuth2ClientCredentialsAuthenticationProviderTests {
assertThat(accessTokenAuthentication.getAccessToken().getScopes()).isEqualTo(requestedScope);
}
@Test
public void authenticateWhenCustomAuthenticationValidatorThenInvokeValidator() {
Consumer<OAuth2ClientCredentialsAuthenticationContext> validator = mock(Consumer.class);
this.authenticationProvider.setAuthenticationValidator(validator);
authenticateWhenScopeRequestedThenAccessTokenContainsScope();
verify(validator).accept(any(OAuth2ClientCredentialsAuthenticationContext.class));
}
@Test
public void authenticateWhenNoScopeRequestedThenAccessTokenDoesNotContainScope() {
RegisteredClient registeredClient = TestRegisteredClients.registeredClient2().build();

70
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/authentication/OAuth2ClientCredentialsAuthenticationValidatorTest.java

@ -0,0 +1,70 @@ @@ -0,0 +1,70 @@
/*
* 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<String> 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<Arguments> validScopes() {
return Stream.of(Arguments.of(new HashSet<>()), Arguments.of(Set.of(SCOPE_1)));
}
}

16
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/client/TestRegisteredClients.java

@ -15,17 +15,19 @@ @@ -15,17 +15,19 @@
*/
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")
@ -39,7 +41,7 @@ public class TestRegisteredClients { @@ -39,7 +41,7 @@ public class TestRegisteredClients {
.redirectUri("https://example.com/callback-2")
.redirectUri("https://example.com/callback-3")
.postLogoutRedirectUri("https://example.com/oidc-post-logout")
.scope("scope1");
.scope(SCOPE_1);
}
public static RegisteredClient.Builder registeredClient2() {
@ -54,8 +56,8 @@ public class TestRegisteredClients { @@ -54,8 +56,8 @@ public class TestRegisteredClients {
.clientAuthenticationMethod(ClientAuthenticationMethod.CLIENT_SECRET_POST)
.redirectUri("https://example.com")
.postLogoutRedirectUri("https://example.com/oidc-post-logout")
.scope("scope1")
.scope("scope2");
.scope(SCOPE_1)
.scope(SCOPE_2);
}
public static RegisteredClient.Builder registeredPublicClient() {
@ -65,7 +67,7 @@ public class TestRegisteredClients { @@ -65,7 +67,7 @@ public class TestRegisteredClients {
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.clientAuthenticationMethod(ClientAuthenticationMethod.NONE)
.redirectUri("https://example.com")
.scope("scope1")
.scope(SCOPE_1)
.clientSettings(ClientSettings.builder().requireProofKey(true).build());
}
}

23
oauth2-authorization-server/src/test/java/org/springframework/security/oauth2/server/authorization/config/annotation/web/configurers/OidcClientRegistrationTests.java

@ -15,21 +15,10 @@ @@ -15,21 +15,10 @@
*/
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;
@ -39,7 +28,6 @@ import org.junit.jupiter.api.BeforeEach; @@ -39,7 +28,6 @@ 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;
@ -111,6 +99,15 @@ import org.springframework.test.web.servlet.MvcResult; @@ -111,6 +99,15 @@ 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;

Loading…
Cancel
Save