3535import org .springframework .security .oauth2 .server .authorization .OAuth2AuthorizationService ;
3636import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AccessTokenAuthenticationToken ;
3737import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2AuthorizationCodeAuthenticationToken ;
38+ import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientAuthenticationToken ;
39+ import org .springframework .security .oauth2 .server .authorization .authentication .OAuth2ClientCredentialsAuthenticationToken ;
3840import org .springframework .security .web .util .matcher .AntPathRequestMatcher ;
3941import org .springframework .security .web .util .matcher .RequestMatcher ;
4042import org .springframework .util .Assert ;
4850import javax .servlet .http .HttpServletResponse ;
4951import java .io .IOException ;
5052import java .time .temporal .ChronoUnit ;
53+ import java .util .Arrays ;
54+ import java .util .HashMap ;
55+ import java .util .HashSet ;
56+ import java .util .Map ;
57+ import java .util .Set ;
5158
5259/**
5360 * A {@code Filter} for the OAuth 2.0 Authorization Code Grant,
@@ -86,8 +93,8 @@ public class OAuth2TokenEndpointFilter extends OncePerRequestFilter {
8693private final AuthenticationManager authenticationManager ;
8794private final OAuth2AuthorizationService authorizationService ;
8895private final RequestMatcher tokenEndpointMatcher ;
89- private final Converter <HttpServletRequest , Authentication > authorizationGrantAuthenticationConverter =
90- new AuthorizationCodeAuthenticationConverter ();
96+ private final Converter <HttpServletRequest , Authentication > authorizationGrantAuthenticationConverter ;
97+
9198private final HttpMessageConverter <OAuth2AccessTokenResponse > accessTokenHttpResponseConverter =
9299new OAuth2AccessTokenResponseHttpMessageConverter ();
93100private final HttpMessageConverter <OAuth2Error > errorHttpResponseConverter =
@@ -119,6 +126,11 @@ public OAuth2TokenEndpointFilter(AuthenticationManager authenticationManager,
119126this .authenticationManager = authenticationManager ;
120127this .authorizationService = authorizationService ;
121128this .tokenEndpointMatcher = new AntPathRequestMatcher (tokenEndpointUri , HttpMethod .POST .name ());
129+
130+ Map <AuthorizationGrantType , Converter <HttpServletRequest , Authentication >> converters = new HashMap <>();
131+ converters .put (AuthorizationGrantType .AUTHORIZATION_CODE , new AuthorizationCodeAuthenticationConverter ());
132+ converters .put (AuthorizationGrantType .CLIENT_CREDENTIALS , new ClientCredentialsAuthenticationConverter ());
133+ this .authorizationGrantAuthenticationConverter = new DelegatingAuthorizationGrantAuthenticationConverter (converters );
122134}
123135
124136@ Override
@@ -131,8 +143,16 @@ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse
131143}
132144
133145try {
134- Authentication authorizationGrantAuthentication =
135- this .authorizationGrantAuthenticationConverter .convert (request );
146+ String [] grantTypes = request .getParameterValues (OAuth2ParameterNames .GRANT_TYPE );
147+ if (grantTypes == null || grantTypes .length == 0 ) {
148+ throwError (OAuth2ErrorCodes .INVALID_REQUEST , "grant_type" );
149+ }
150+
151+ Authentication authorizationGrantAuthentication = this .authorizationGrantAuthenticationConverter .convert (request );
152+ if (authorizationGrantAuthentication == null ) {
153+ throwError (OAuth2ErrorCodes .UNSUPPORTED_GRANT_TYPE , "grant_type" );
154+ }
155+
136156OAuth2AccessTokenAuthenticationToken accessTokenAuthentication =
137157(OAuth2AccessTokenAuthenticationToken ) this .authenticationManager .authenticate (authorizationGrantAuthentication );
138158sendAccessTokenResponse (response , accessTokenAuthentication .getAccessToken ());
@@ -161,7 +181,7 @@ private void sendErrorResponse(HttpServletResponse response, OAuth2Error error)
161181this .errorHttpResponseConverter .write (error , null , httpResponse );
162182}
163183
164- private static OAuth2AuthenticationException throwError (String errorCode , String parameterName ) {
184+ private static void throwError (String errorCode , String parameterName ) {
165185OAuth2Error error = new OAuth2Error (errorCode , "OAuth 2.0 Parameter: " + parameterName ,
166186"https://tools.ietf.org/html/rfc6749#section-5.2" );
167187throw new OAuth2AuthenticationException (error );
@@ -214,4 +234,29 @@ public Authentication convert(HttpServletRequest request) {
214234new OAuth2AuthorizationCodeAuthenticationToken (code , clientId , redirectUri );
215235}
216236}
237+
238+ private static class ClientCredentialsAuthenticationConverter implements Converter <HttpServletRequest , Authentication > {
239+
240+ @ Override
241+ public Authentication convert (HttpServletRequest request ) {
242+ final Authentication authentication = SecurityContextHolder .getContext ().getAuthentication ();
243+ final OAuth2ClientAuthenticationToken clientAuthenticationToken = (OAuth2ClientAuthenticationToken ) authentication ;
244+
245+ // grant_type (REQUIRED)
246+ String grantType = request .getParameter (OAuth2ParameterNames .GRANT_TYPE );
247+ if (!AuthorizationGrantType .CLIENT_CREDENTIALS .getValue ().equals (grantType )) {
248+ throwError (OAuth2ErrorCodes .UNSUPPORTED_GRANT_TYPE , OAuth2ParameterNames .GRANT_TYPE );
249+ }
250+
251+ // scope (OPTIONAL)
252+ // https://tools.ietf.org/html/rfc6749#section-4.4.2
253+ String scopeParameter = request .getParameter (OAuth2ParameterNames .SCOPE );
254+ if (StringUtils .isEmpty (scopeParameter )) {
255+ return new OAuth2ClientCredentialsAuthenticationToken (clientAuthenticationToken );
256+ }
257+
258+ Set <String > requestedScopes = new HashSet <>(Arrays .asList (StringUtils .delimitedListToStringArray (scopeParameter , " " )));
259+ return new OAuth2ClientCredentialsAuthenticationToken (clientAuthenticationToken , requestedScopes );
260+ }
261+ }
217262}
0 commit comments