Skip to content

Commit

Permalink
Allow opt-in to omitting default parameters
Browse files Browse the repository at this point in the history
Closes gh-11298
  • Loading branch information
sjohnr committed Sep 20, 2024
1 parent 5fd61bb commit 8996371
Show file tree
Hide file tree
Showing 2 changed files with 133 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
Expand Down Expand Up @@ -67,6 +66,8 @@ public abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient<T

private Converter<T, HttpHeaders> headersConverter = new DefaultOAuth2TokenRequestHeadersConverter<>();

private final Converter<T, MultiValueMap<String, String>> defaultParametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();

private Converter<T, MultiValueMap<String, String>> parametersConverter = this::createParameters;

private BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor = OAuth2BodyExtractors
Expand Down Expand Up @@ -125,17 +126,7 @@ private RequestHeadersSpec<?> populateRequest(T grantRequest) {
* Token Request body
*/
MultiValueMap<String, String> createParameters(T grantRequest) {
ClientRegistration clientRegistration = grantRequest.getClientRegistration();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue());
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC
.equals(clientRegistration.getClientAuthenticationMethod())) {
parameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
}
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod())) {
parameters.set(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
}
return parameters;
return this.defaultParametersConverter.convert(grantRequest);
}

/**
Expand Down Expand Up @@ -195,13 +186,44 @@ public final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter
* Sets the {@link Converter} used for converting the
* {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}
* used in the OAuth 2.0 Access Token Request body.
* <p>
* For backwards compatibility with Spring Security 6.3 (and earlier), this method
* ensures that default parameters for this particular grant type are provided if the
* given parameters converter does not supply them. In order to fully override or omit
* parameters, supply this method with an instance of
* {@link DefaultOAuth2TokenRequestParametersConverter} via
* {@link DefaultOAuth2TokenRequestParametersConverter#of(Converter)} and only the
* returned parameters will be provided.
* @param parametersConverter the {@link Converter} used for converting the
* {@link AbstractOAuth2AuthorizationGrantRequest} to {@link MultiValueMap}
* @since 5.6
* @see DefaultOAuth2TokenRequestParametersConverter#of(Converter)
*/
public final void setParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {
Assert.notNull(parametersConverter, "parametersConverter cannot be null");
this.parametersConverter = parametersConverter;
// Allow opting into new behavior of fully overriding parameter values when
// user provides instance of DefaultOAuth2TokenRequestParametersConverter.
if (parametersConverter instanceof DefaultOAuth2TokenRequestParametersConverter) {
this.parametersConverter = parametersConverter;
}
else {
// For backwards compatibility with 6.3, ensure default parameters are always
// populated but allow parameter values to be overridden if provided.
// TODO: Remove in Spring Security 7
Converter<T, MultiValueMap<String, String>> defaultParametersConverter = this::createParameters;
this.parametersConverter = (authorizationGrantRequest) -> {
MultiValueMap<String, String> parameters = defaultParametersConverter
.convert(authorizationGrantRequest);
if (parameters == null) {
parameters = new LinkedMultiValueMap<>();
}
MultiValueMap<String, String> parametersToSet = parametersConverter.convert(authorizationGrantRequest);
if (parametersToSet != null) {
parameters.putAll(parametersToSet);
}
return parameters;
};
}
this.requestEntityConverter = this::populateRequest;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
/*
* Copyright 2002-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.
* 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.client.endpoint;

import org.springframework.core.convert.converter.Converter;
import org.springframework.security.oauth2.client.registration.ClientRegistration;
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
import org.springframework.util.Assert;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;

/**
* Default {@link Converter} used to convert an
* {@link AbstractOAuth2AuthorizationGrantRequest} to the default {@link MultiValueMap
* parameters} of an OAuth 2.0 Access Token Request.
* <p>
* This implementation does not provide grant-type specific parameters. The following
* parameters are provided:
*
* <ul>
* <li>{@code grant_type} - always provided</li>
* <li>{@code client_id} - provided unless the {@code clientAuthenticationMethod} is
* {@code client_secret_basic}</li>
* <li>{@code client_secret} - provided when the {@code clientAuthenticationMethod} is
* {@code client_secret_post}</li>
* </ul>
*
* @param <T> type of grant request
* @author Steve Riesenberg
* @since 6.4
* @see AbstractWebClientReactiveOAuth2AccessTokenResponseClient
* @see AbstractRestClientOAuth2AccessTokenResponseClient
*/
public final class DefaultOAuth2TokenRequestParametersConverter<T extends AbstractOAuth2AuthorizationGrantRequest>
implements Converter<T, MultiValueMap<String, String>> {

private final Converter<T, MultiValueMap<String, String>> defaultParametersConverter;

public DefaultOAuth2TokenRequestParametersConverter() {
this(DefaultOAuth2TokenRequestParametersConverter::defaultParameters);
}

private DefaultOAuth2TokenRequestParametersConverter(
Converter<T, MultiValueMap<String, String>> defaultParametersConverter) {
Assert.notNull(defaultParametersConverter, "defaultParametersConverter cannot be null");
this.defaultParametersConverter = defaultParametersConverter;
}

@Override
public MultiValueMap<String, String> convert(T grantRequest) {
return this.defaultParametersConverter.convert(grantRequest);
}

private static <T extends AbstractOAuth2AuthorizationGrantRequest> MultiValueMap<String, String> defaultParameters(
T grantRequest) {
ClientRegistration clientRegistration = grantRequest.getClientRegistration();
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
parameters.set(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue());
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC
.equals(clientRegistration.getClientAuthenticationMethod())) {
parameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
}
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod())) {
parameters.set(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
}
return parameters;
}

/**
* Wrap a custom {@link Converter} in order to override or omit default parameters
* with
* {@link AbstractWebClientReactiveOAuth2AccessTokenResponseClient#setParametersConverter(Converter)}.
* @param defaultParametersConverter the {@link Converter} used for converting the
* @param <T> type of grant request {@link AbstractOAuth2AuthorizationGrantRequest} to
* {@link MultiValueMap parameters}
* @return the wrapped {@link Converter}
*/
public static <T extends AbstractOAuth2AuthorizationGrantRequest> Converter<T, MultiValueMap<String, String>> of(
Converter<T, MultiValueMap<String, String>> defaultParametersConverter) {
return new DefaultOAuth2TokenRequestParametersConverter<>(defaultParametersConverter);
}

}

0 comments on commit 8996371

Please sign in to comment.