15
15
*/
16
16
package org .springframework .security .oauth2 .server .authorization .web ;
17
17
18
+ import javax .servlet .FilterChain ;
19
+ import javax .servlet .ServletException ;
20
+ import javax .servlet .http .HttpServletRequest ;
21
+ import javax .servlet .http .HttpServletResponse ;
22
+ import java .io .IOException ;
23
+ import java .time .temporal .ChronoUnit ;
24
+ import java .util .HashMap ;
25
+ import java .util .Map ;
26
+ import java .util .Set ;
27
+
18
28
import org .springframework .core .convert .converter .Converter ;
19
29
import org .springframework .http .HttpMethod ;
20
30
import org .springframework .http .HttpStatus ;
35
45
import org .springframework .security .oauth2 .server .authorization .OAuth2AuthorizationService ;
36
46
import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AccessTokenAuthenticationToken ;
37
47
import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
48
+ import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
49
+ import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientCredentialsAuthenticationToken ;
38
50
import org .springframework .security .web .util .matcher .AntPathRequestMatcher ;
39
51
import org .springframework .security .web .util .matcher .RequestMatcher ;
40
52
import org .springframework .util .Assert ;
41
53
import org .springframework .util .MultiValueMap ;
42
54
import org .springframework .util .StringUtils ;
43
55
import org .springframework .web .filter .OncePerRequestFilter ;
44
56
45
- import javax .servlet .FilterChain ;
46
- import javax .servlet .ServletException ;
47
- import javax .servlet .http .HttpServletRequest ;
48
- import javax .servlet .http .HttpServletResponse ;
49
- import java .io .IOException ;
50
- import java .time .temporal .ChronoUnit ;
51
-
52
57
/**
53
58
* A {@code Filter} for the OAuth 2.0 Authorization Code Grant,
54
59
* which handles the processing of the OAuth 2.0 Access Token Request.
@@ -86,8 +91,8 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
86
91
private final AuthenticationManager authenticationManager ;
87
92
private final OAuth2AuthorizationService authorizationService ;
88
93
private final RequestMatcher tokenEndpointMatcher ;
89
- private final Converter <HttpServletRequest , Authentication > authorizationGrantAuthenticationConverter =
90
- new AuthorizationCodeAuthenticationConverter ();
94
+ private final Converter <HttpServletRequest , Authentication > authorizationGrantAuthenticationConverter ;
95
+
91
96
private final HttpMessageConverter <OAuth2AccessTokenResponse > accessTokenHttpResponseConverter =
92
97
new OAuth2AccessTokenResponseHttpMessageConverter ();
93
98
private final HttpMessageConverter <OAuth2Error > errorHttpResponseConverter =
@@ -119,6 +124,11 @@ public OAuth2TokenEndpointFilter(AuthenticationManager authenticationManager,
119
124
this .authenticationManager = authenticationManager ;
120
125
this .authorizationService = authorizationService ;
121
126
this .tokenEndpointMatcher = new AntPathRequestMatcher (tokenEndpointUri , HttpMethod .POST .name ());
127
+
128
+ Map <AuthorizationGrantType , Converter <HttpServletRequest , Authentication >> converters = new HashMap <>();
129
+ converters .put (AuthorizationGrantType .AUTHORIZATION_CODE , new AuthorizationCodeAuthenticationConverter ());
130
+ converters .put (AuthorizationGrantType .CLIENT_CREDENTIALS , new ClientCredentialsAuthenticationConverter ());
131
+ this .authorizationGrantAuthenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter (converters );
122
132
}
123
133
124
134
@ Override
@@ -131,8 +141,11 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
131
141
}
132
142
133
143
try {
134
- Authentication authorizationGrantAuthentication =
135
- this .authorizationGrantAuthenticationConverter .convert (request );
144
+ Authentication authorizationGrantAuthentication = this .authorizationGrantAuthenticationConverter .convert (request );
145
+ if (authorizationGrantAuthentication == null ) {
146
+ throwError (OAuth2ErrorCodes .UNSUPPORTED_GRANT_TYPE , "grant_type" );
147
+ }
148
+
136
149
OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
137
150
(OAuth2AccessTokenAuthenticationToken ) this .authenticationManager .authenticate (authorizationGrantAuthentication );
138
151
sendAccessTokenResponse (response , accessTokenAuthentication .getAccessToken ());
@@ -161,7 +174,7 @@ private void sendErrorResponse(HttpServletResponse response, OAuth2Error error)
161
174
this .errorHttpResponseConverter .write (error , null , httpResponse );
162
175
}
163
176
164
- private static OAuth2AuthenticationException throwError (String errorCode , String parameterName ) {
177
+ private static void throwError (String errorCode , String parameterName ) {
165
178
OAuth2Error error = new OAuth2Error (errorCode , "OAuth 2.0 Parameter: " + parameterName ,
166
179
"https://tools.ietf.org/html/rfc6749#section-5.2" );
167
180
throw new OAuth2AuthenticationException (error );
@@ -214,4 +227,39 @@ public Authentication convert(HttpServletRequest request) {
214
227
new OAuth2AuthorizationCodeAuthenticationToken (code , clientId , redirectUri );
215
228
}
216
229
}
230
+
231
+ /**
232
+ * Validates that client is authenticated by {@link OAuth2ClientAuthenticationFilter}
233
+ *
234
+ * @see OAuth2ClientAuthenticationToken
235
+ * @see OAuth2ClientAuthenticationFilter
236
+ * @see <a target="_blank" href="https://tools.ietf.org/html/rfc6749#section-4.4">Section 4.4 Client Credentials Grant</a>
237
+ */
238
+ private static class ClientCredentialsAuthenticationConverter implements Converter <HttpServletRequest , Authentication > {
239
+
240
+ private static final String SCOPE_PARAMETER_NAME = OAuth2ParameterNames .SCOPE ;
241
+ private final OAuth2ScopeParser scopesParser = new OAuth2ScopeParser ();
242
+
243
+ @ Override
244
+ public Authentication convert (HttpServletRequest request ) {
245
+ final Authentication authentication = SecurityContextHolder .getContext ().getAuthentication ();
246
+ final OAuth2ClientAuthenticationToken clientAuthenticationToken = (OAuth2ClientAuthenticationToken ) authentication ;
247
+
248
+ // "scope" is optional, see https://tools.ietf.org/html/rfc6749#section-4.4.2 and https://tools.ietf.org/html/rfc6749#section-3.3
249
+ String scopeParameter = request .getParameter (SCOPE_PARAMETER_NAME );
250
+ if (StringUtils .isEmpty (scopeParameter )) {
251
+ return new OAuth2ClientCredentialsAuthenticationToken (clientAuthenticationToken );
252
+ }
253
+
254
+ Set <String > requestedScopes = null ;
255
+ try {
256
+ requestedScopes = scopesParser .parse (scopeParameter );
257
+ }
258
+ catch (OAuth2ScopeParser .InvalidScopeFormatException e ) {
259
+ throwError (OAuth2ErrorCodes .INVALID_SCOPE , "scope" );
260
+ }
261
+
262
+ return new OAuth2ClientCredentialsAuthenticationToken (clientAuthenticationToken , requestedScopes );
263
+ }
264
+ }
217
265
}
0 commit comments