16
16
17
17
package org .springframework .security .oauth2 .client .endpoint ;
18
18
19
- import java .util .Collections ;
20
- import java .util .Set ;
21
-
22
19
import reactor .core .publisher .Mono ;
23
20
24
21
import org .springframework .core .convert .converter .Converter ;
27
24
import org .springframework .security .oauth2 .client .registration .ClientRegistration ;
28
25
import org .springframework .security .oauth2 .core .ClientAuthenticationMethod ;
29
26
import org .springframework .security .oauth2 .core .endpoint .OAuth2AccessTokenResponse ;
30
- import org .springframework .security .oauth2 .core .endpoint .OAuth2ParameterNames ;
31
27
import org .springframework .security .oauth2 .core .web .reactive .function .OAuth2BodyExtractors ;
32
28
import org .springframework .util .Assert ;
33
- import org .springframework .util .CollectionUtils ;
34
29
import org .springframework .util .LinkedMultiValueMap ;
35
30
import org .springframework .util .MultiValueMap ;
36
- import org .springframework .util .StringUtils ;
37
31
import org .springframework .web .reactive .function .BodyExtractor ;
38
32
import org .springframework .web .reactive .function .BodyInserters ;
39
- import org .springframework .web .reactive .function .client .ClientResponse ;
40
33
import org .springframework .web .reactive .function .client .WebClient ;
41
34
import org .springframework .web .reactive .function .client .WebClient .RequestHeadersSpec ;
42
35
54
47
*
55
48
* @param <T> type of grant request
56
49
* @author Phil Clay
50
+ * @author Steve Riesenberg
57
51
* @since 5.3
58
52
* @see <a href="https://tools.ietf.org/html/rfc6749#section-3.2">RFC-6749 Token
59
53
* Endpoint</a>
@@ -72,7 +66,7 @@ public abstract class AbstractWebClientReactiveOAuth2AccessTokenResponseClient<T
72
66
73
67
private Converter <T , HttpHeaders > headersConverter = new DefaultOAuth2TokenRequestHeadersConverter <>();
74
68
75
- private Converter <T , MultiValueMap <String , String >> parametersConverter = this :: populateTokenRequestParameters ;
69
+ private Converter <T , MultiValueMap <String , String >> parametersConverter = new DefaultOAuth2TokenRequestParametersConverter <>() ;
76
70
77
71
private BodyExtractor <Mono <OAuth2AccessTokenResponse >, ReactiveHttpInputMessage > bodyExtractor = OAuth2BodyExtractors
78
72
.oauth2AccessTokenResponse ();
@@ -86,18 +80,11 @@ public Mono<OAuth2AccessTokenResponse> getTokenResponse(T grantRequest) {
86
80
// @formatter:off
87
81
return Mono .defer (() -> this .requestEntityConverter .convert (grantRequest )
88
82
.exchange ()
89
- .flatMap ((response ) -> readTokenResponse ( grantRequest , response ))
83
+ .flatMap ((response ) -> response . body ( this . bodyExtractor ))
90
84
);
91
85
// @formatter:on
92
86
}
93
87
94
- /**
95
- * Returns the {@link ClientRegistration} for the given {@code grantRequest}.
96
- * @param grantRequest the grant request
97
- * @return the {@link ClientRegistration} for the given {@code grantRequest}.
98
- */
99
- abstract ClientRegistration clientRegistration (T grantRequest );
100
-
101
88
private RequestHeadersSpec <?> validatingPopulateRequest (T grantRequest ) {
102
89
validateClientAuthenticationMethod (grantRequest );
103
90
return populateRequest (grantRequest );
@@ -117,128 +104,20 @@ private void validateClientAuthenticationMethod(T grantRequest) {
117
104
}
118
105
119
106
private RequestHeadersSpec <?> populateRequest (T grantRequest ) {
107
+ MultiValueMap <String , String > parameters = this .parametersConverter .convert (grantRequest );
108
+ if (parameters == null ) {
109
+ parameters = new LinkedMultiValueMap <>();
110
+ }
111
+
120
112
return this .webClient .post ()
121
- .uri (clientRegistration ( grantRequest ).getProviderDetails ().getTokenUri ())
113
+ .uri (grantRequest . getClientRegistration ( ).getProviderDetails ().getTokenUri ())
122
114
.headers ((headers ) -> {
123
- HttpHeaders headersToAdd = getHeadersConverter () .convert (grantRequest );
115
+ HttpHeaders headersToAdd = this . headersConverter .convert (grantRequest );
124
116
if (headersToAdd != null ) {
125
117
headers .addAll (headersToAdd );
126
118
}
127
119
})
128
- .body (createTokenRequestBody (grantRequest ));
129
- }
130
-
131
- /**
132
- * Populates default parameters for the token request.
133
- * @param grantRequest the grant request
134
- * @return the parameters populated for the token request.
135
- */
136
- private MultiValueMap <String , String > populateTokenRequestParameters (T grantRequest ) {
137
- MultiValueMap <String , String > parameters = new LinkedMultiValueMap <>();
138
- parameters .add (OAuth2ParameterNames .GRANT_TYPE , grantRequest .getGrantType ().getValue ());
139
- return parameters ;
140
- }
141
-
142
- /**
143
- * Combine the results of {@code parametersConverter} and
144
- * {@link #populateTokenRequestBody}.
145
- *
146
- * <p>
147
- * This method pre-populates the body with some standard properties, and then
148
- * delegates to
149
- * {@link #populateTokenRequestBody(AbstractOAuth2AuthorizationGrantRequest, BodyInserters.FormInserter)}
150
- * for subclasses to further populate the body before returning.
151
- * </p>
152
- * @param grantRequest the grant request
153
- * @return the body for the token request.
154
- */
155
- private BodyInserters .FormInserter <String > createTokenRequestBody (T grantRequest ) {
156
- MultiValueMap <String , String > parameters = getParametersConverter ().convert (grantRequest );
157
- return populateTokenRequestBody (grantRequest , BodyInserters .fromFormData (parameters ));
158
- }
159
-
160
- /**
161
- * Populates the body of the token request.
162
- *
163
- * <p>
164
- * By default, populates properties that are common to all grant types. Subclasses can
165
- * extend this method to populate grant type specific properties.
166
- * </p>
167
- * @param grantRequest the grant request
168
- * @param body the body to populate
169
- * @return the populated body
170
- */
171
- BodyInserters .FormInserter <String > populateTokenRequestBody (T grantRequest ,
172
- BodyInserters .FormInserter <String > body ) {
173
- ClientRegistration clientRegistration = clientRegistration (grantRequest );
174
- if (!ClientAuthenticationMethod .CLIENT_SECRET_BASIC
175
- .equals (clientRegistration .getClientAuthenticationMethod ())) {
176
- body .with (OAuth2ParameterNames .CLIENT_ID , clientRegistration .getClientId ());
177
- }
178
- if (ClientAuthenticationMethod .CLIENT_SECRET_POST .equals (clientRegistration .getClientAuthenticationMethod ())) {
179
- body .with (OAuth2ParameterNames .CLIENT_SECRET , clientRegistration .getClientSecret ());
180
- }
181
- Set <String > scopes = scopes (grantRequest );
182
- if (!CollectionUtils .isEmpty (scopes )) {
183
- body .with (OAuth2ParameterNames .SCOPE , StringUtils .collectionToDelimitedString (scopes , " " ));
184
- }
185
- return body ;
186
- }
187
-
188
- /**
189
- * Returns the scopes to include as a property in the token request.
190
- * @param grantRequest the grant request
191
- * @return the scopes to include as a property in the token request.
192
- */
193
- abstract Set <String > scopes (T grantRequest );
194
-
195
- /**
196
- * Returns the scopes to include in the response if the authorization server returned
197
- * no scopes in the response.
198
- *
199
- * <p>
200
- * As per <a href="https://tools.ietf.org/html/rfc6749#section-5.1">RFC-6749 Section
201
- * 5.1 Successful Access Token Response</a>, if AccessTokenResponse.scope is empty,
202
- * then default to the scope originally requested by the client in the Token Request.
203
- * </p>
204
- * @param grantRequest the grant request
205
- * @return the scopes to include in the response if the authorization server returned
206
- * no scopes.
207
- */
208
- Set <String > defaultScopes (T grantRequest ) {
209
- return Collections .emptySet ();
210
- }
211
-
212
- /**
213
- * Reads the token response from the response body.
214
- * @param grantRequest the request for which the response was received.
215
- * @param response the client response from which to read
216
- * @return the token response from the response body.
217
- */
218
- private Mono <OAuth2AccessTokenResponse > readTokenResponse (T grantRequest , ClientResponse response ) {
219
- return response .body (this .bodyExtractor )
220
- .map ((tokenResponse ) -> populateTokenResponse (grantRequest , tokenResponse ));
221
- }
222
-
223
- /**
224
- * Populates the given {@link OAuth2AccessTokenResponse} with additional details from
225
- * the grant request.
226
- * @param grantRequest the request for which the response was received.
227
- * @param tokenResponse the original token response
228
- * @return a token response optionally populated with additional details from the
229
- * request.
230
- */
231
- OAuth2AccessTokenResponse populateTokenResponse (T grantRequest , OAuth2AccessTokenResponse tokenResponse ) {
232
- if (CollectionUtils .isEmpty (tokenResponse .getAccessToken ().getScopes ())) {
233
- Set <String > defaultScopes = defaultScopes (grantRequest );
234
- // @formatter:off
235
- tokenResponse = OAuth2AccessTokenResponse
236
- .withResponse (tokenResponse )
237
- .scopes (defaultScopes )
238
- .build ();
239
- // @formatter:on
240
- }
241
- return tokenResponse ;
120
+ .body (BodyInserters .fromFormData (parameters ));
242
121
}
243
122
244
123
/**
@@ -247,22 +126,11 @@ OAuth2AccessTokenResponse populateTokenResponse(T grantRequest, OAuth2AccessToke
247
126
* @param webClient the {@link WebClient} used when requesting the Access Token
248
127
* Response
249
128
*/
250
- public void setWebClient (WebClient webClient ) {
129
+ public final void setWebClient (WebClient webClient ) {
251
130
Assert .notNull (webClient , "webClient cannot be null" );
252
131
this .webClient = webClient ;
253
132
}
254
133
255
- /**
256
- * Returns the {@link Converter} used for converting the
257
- * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}
258
- * used in the OAuth 2.0 Access Token Request headers.
259
- * @return the {@link Converter} used for converting the
260
- * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link HttpHeaders}
261
- */
262
- final Converter <T , HttpHeaders > getHeadersConverter () {
263
- return this .headersConverter ;
264
- }
265
-
266
134
/**
267
135
* Sets the {@link Converter} used for converting the
268
136
* {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link HttpHeaders}
@@ -305,17 +173,6 @@ public final void addHeadersConverter(Converter<T, HttpHeaders> headersConverter
305
173
this .requestEntityConverter = this ::populateRequest ;
306
174
}
307
175
308
- /**
309
- * Returns the {@link Converter} used for converting the
310
- * {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}
311
- * used in the OAuth 2.0 Access Token Request body.
312
- * @return the {@link Converter} used for converting the
313
- * {@link AbstractOAuth2AuthorizationGrantRequest} to {@link MultiValueMap}
314
- */
315
- final Converter <T , MultiValueMap <String , String >> getParametersConverter () {
316
- return this .parametersConverter ;
317
- }
318
-
319
176
/**
320
177
* Sets the {@link Converter} used for converting the
321
178
* {@link AbstractOAuth2AuthorizationGrantRequest} instance to a {@link MultiValueMap}
@@ -326,7 +183,21 @@ final Converter<T, MultiValueMap<String, String>> getParametersConverter() {
326
183
*/
327
184
public final void setParametersConverter (Converter <T , MultiValueMap <String , String >> parametersConverter ) {
328
185
Assert .notNull (parametersConverter , "parametersConverter cannot be null" );
329
- this .parametersConverter = parametersConverter ;
186
+ if (parametersConverter instanceof DefaultOAuth2TokenRequestParametersConverter ) {
187
+ this .parametersConverter = parametersConverter ;
188
+ }
189
+ else {
190
+ Converter <T , MultiValueMap <String , String >> defaultParametersConverter = new DefaultOAuth2TokenRequestParametersConverter <>();
191
+ this .parametersConverter = (authorizationGrantRequest ) -> {
192
+ MultiValueMap <String , String > parameters = defaultParametersConverter
193
+ .convert (authorizationGrantRequest );
194
+ MultiValueMap <String , String > parametersToSet = parametersConverter .convert (authorizationGrantRequest );
195
+ if (parametersToSet != null ) {
196
+ parameters .putAll (parametersToSet );
197
+ }
198
+ return parameters ;
199
+ };
200
+ }
330
201
this .requestEntityConverter = this ::populateRequest ;
331
202
}
332
203
0 commit comments