Skip to content

Commit 8996371

Browse files
committed
Allow opt-in to omitting default parameters
Closes gh-11298
1 parent 5fd61bb commit 8996371

File tree

2 files changed

+133
-13
lines changed

2 files changed

+133
-13
lines changed

oauth2/oauth2-client/src/main/java/org/springframework/security/oauth2/client/endpoint/AbstractWebClientReactiveOAuth2AccessTokenResponseClient.java

Lines changed: 35 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,6 @@
2424
import org.springframework.security.oauth2.client.registration.ClientRegistration;
2525
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
2626
import org.springframework.security.oauth2.core.endpoint.OAuth2AccessTokenResponse;
27-
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
2827
import org.springframework.security.oauth2.core.web.reactive.function.OAuth2BodyExtractors;
2928
import org.springframework.util.Assert;
3029
import org.springframework.util.LinkedMultiValueMap;
@@ -67,6 +66,8 @@ public abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient<T
6766

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

69+
private final Converter<T, MultiValueMap<String, String>> defaultParametersConverter = new DefaultOAuth2TokenRequestParametersConverter<>();
70+
7071
private Converter<T, MultiValueMap<String, String>> parametersConverter = this::createParameters;
7172

7273
private BodyExtractor<Mono<OAuth2AccessTokenResponse>, ReactiveHttpInputMessage> bodyExtractor = OAuth2BodyExtractors
@@ -125,17 +126,7 @@ private RequestHeadersSpec<?> populateRequest(T grantRequest) {
125126
* Token Request body
126127
*/
127128
MultiValueMap<String, String> createParameters(T grantRequest) {
128-
ClientRegistration clientRegistration = grantRequest.getClientRegistration();
129-
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
130-
parameters.set(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue());
131-
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC
132-
.equals(clientRegistration.getClientAuthenticationMethod())) {
133-
parameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
134-
}
135-
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod())) {
136-
parameters.set(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
137-
}
138-
return parameters;
129+
return this.defaultParametersConverter.convert(grantRequest);
139130
}
140131

141132
/**
@@ -195,13 +186,44 @@ public final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter
195186
* Sets the {@link Converter} used for converting the
196187
* {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}
197188
* used in the OAuth 2.0 Access Token Request body.
189+
* <p>
190+
* For backwards compatibility with Spring Security 6.3 (and earlier), this method
191+
* ensures that default parameters for this particular grant type are provided if the
192+
* given parameters converter does not supply them. In order to fully override or omit
193+
* parameters, supply this method with an instance of
194+
* {@link DefaultOAuth2TokenRequestParametersConverter} via
195+
* {@link DefaultOAuth2TokenRequestParametersConverter#of(Converter)} and only the
196+
* returned parameters will be provided.
198197
* @param parametersConverter the {@link Converter} used for converting the
199198
* {@link AbstractOAuth2AuthorizationGrantRequest} to {@link MultiValueMap}
200199
* @since 5.6
200+
* @see DefaultOAuth2TokenRequestParametersConverter#of(Converter)
201201
*/
202202
public final void setParametersConverter(Converter<T, MultiValueMap<String, String>> parametersConverter) {
203203
Assert.notNull(parametersConverter, "parametersConverter cannot be null");
204-
this.parametersConverter = parametersConverter;
204+
// Allow opting into new behavior of fully overriding parameter values when
205+
// user provides instance of DefaultOAuth2TokenRequestParametersConverter.
206+
if (parametersConverter instanceof DefaultOAuth2TokenRequestParametersConverter) {
207+
this.parametersConverter = parametersConverter;
208+
}
209+
else {
210+
// For backwards compatibility with 6.3, ensure default parameters are always
211+
// populated but allow parameter values to be overridden if provided.
212+
// TODO: Remove in Spring Security 7
213+
Converter<T, MultiValueMap<String, String>> defaultParametersConverter = this::createParameters;
214+
this.parametersConverter = (authorizationGrantRequest) -> {
215+
MultiValueMap<String, String> parameters = defaultParametersConverter
216+
.convert(authorizationGrantRequest);
217+
if (parameters == null) {
218+
parameters = new LinkedMultiValueMap<>();
219+
}
220+
MultiValueMap<String, String> parametersToSet = parametersConverter.convert(authorizationGrantRequest);
221+
if (parametersToSet != null) {
222+
parameters.putAll(parametersToSet);
223+
}
224+
return parameters;
225+
};
226+
}
205227
this.requestEntityConverter = this::populateRequest;
206228
}
207229

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
/*
2+
* Copyright 2002-2024 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* https://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package org.springframework.security.oauth2.client.endpoint;
18+
19+
import org.springframework.core.convert.converter.Converter;
20+
import org.springframework.security.oauth2.client.registration.ClientRegistration;
21+
import org.springframework.security.oauth2.core.ClientAuthenticationMethod;
22+
import org.springframework.security.oauth2.core.endpoint.OAuth2ParameterNames;
23+
import org.springframework.util.Assert;
24+
import org.springframework.util.LinkedMultiValueMap;
25+
import org.springframework.util.MultiValueMap;
26+
27+
/**
28+
* Default {@link Converter} used to convert an
29+
* {@link AbstractOAuth2AuthorizationGrantRequest} to the default {@link MultiValueMap
30+
* parameters} of an OAuth 2.0 Access Token Request.
31+
* <p>
32+
* This implementation does not provide grant-type specific parameters. The following
33+
* parameters are provided:
34+
*
35+
* <ul>
36+
* <li>{@code grant_type} - always provided</li>
37+
* <li>{@code client_id} - provided unless the {@code clientAuthenticationMethod} is
38+
* {@code client_secret_basic}</li>
39+
* <li>{@code client_secret} - provided when the {@code clientAuthenticationMethod} is
40+
* {@code client_secret_post}</li>
41+
* </ul>
42+
*
43+
* @param <T> type of grant request
44+
* @author Steve Riesenberg
45+
* @since 6.4
46+
* @see AbstractWebClientReactiveOAuth2AccessTokenResponseClient
47+
* @see AbstractRestClientOAuth2AccessTokenResponseClient
48+
*/
49+
public final class DefaultOAuth2TokenRequestParametersConverter<T extends AbstractOAuth2AuthorizationGrantRequest>
50+
implements Converter<T, MultiValueMap<String, String>> {
51+
52+
private final Converter<T, MultiValueMap<String, String>> defaultParametersConverter;
53+
54+
public DefaultOAuth2TokenRequestParametersConverter() {
55+
this(DefaultOAuth2TokenRequestParametersConverter::defaultParameters);
56+
}
57+
58+
private DefaultOAuth2TokenRequestParametersConverter(
59+
Converter<T, MultiValueMap<String, String>> defaultParametersConverter) {
60+
Assert.notNull(defaultParametersConverter, "defaultParametersConverter cannot be null");
61+
this.defaultParametersConverter = defaultParametersConverter;
62+
}
63+
64+
@Override
65+
public MultiValueMap<String, String> convert(T grantRequest) {
66+
return this.defaultParametersConverter.convert(grantRequest);
67+
}
68+
69+
private static <T extends AbstractOAuth2AuthorizationGrantRequest> MultiValueMap<String, String> defaultParameters(
70+
T grantRequest) {
71+
ClientRegistration clientRegistration = grantRequest.getClientRegistration();
72+
MultiValueMap<String, String> parameters = new LinkedMultiValueMap<>();
73+
parameters.set(OAuth2ParameterNames.GRANT_TYPE, grantRequest.getGrantType().getValue());
74+
if (!ClientAuthenticationMethod.CLIENT_SECRET_BASIC
75+
.equals(clientRegistration.getClientAuthenticationMethod())) {
76+
parameters.set(OAuth2ParameterNames.CLIENT_ID, clientRegistration.getClientId());
77+
}
78+
if (ClientAuthenticationMethod.CLIENT_SECRET_POST.equals(clientRegistration.getClientAuthenticationMethod())) {
79+
parameters.set(OAuth2ParameterNames.CLIENT_SECRET, clientRegistration.getClientSecret());
80+
}
81+
return parameters;
82+
}
83+
84+
/**
85+
* Wrap a custom {@link Converter} in order to override or omit default parameters
86+
* with
87+
* {@link AbstractWebClientReactiveOAuth2AccessTokenResponseClient#setParametersConverter(Converter)}.
88+
* @param defaultParametersConverter the {@link Converter} used for converting the
89+
* @param <T> type of grant request {@link AbstractOAuth2AuthorizationGrantRequest} to
90+
* {@link MultiValueMap parameters}
91+
* @return the wrapped {@link Converter}
92+
*/
93+
public static <T extends AbstractOAuth2AuthorizationGrantRequest> Converter<T, MultiValueMap<String, String>> of(
94+
Converter<T, MultiValueMap<String, String>> defaultParametersConverter) {
95+
return new DefaultOAuth2TokenRequestParametersConverter<>(defaultParametersConverter);
96+
}
97+
98+
}

0 commit comments

Comments
 (0)