Additional parametrs for /oauth2/token

image

Hello JmixTeam

I was trying to add additional fields to the /oauth2/token api using this. But for some reason it’s not working.

I have looked at Simplify customizing the access token response · Issue #925 · spring-projects/spring-authorization-server · GitHub

I wrote CustomAccessTokenResponseHandler. But it is not working.
Because this is working at org.springframework.security.oauth2.server.authorization.web.OAuth2TokenEndpointFilter

private AuthenticationSuccessHandler authenticationSuccessHandler = this::sendAccessTokenResponse;

I didn’t find override this class

Please share sample demo app which demonstrates how to implement custom token response.

2 Likes

how to solve this issue? i need also. @krivopustov @gorelov

Hello!

By default Jmix configures /oauth2/token behaviour in SecurityFilterChain bean named authsr_AuthorizationServerSecurityFilterChain.
Thus, custom AuthenticationSuccessHandler may be set to OAuth2TokenEndpointFilter by obtaining it from authsr_AuthorizationServerSecurityFilterChain:

@Component
public class TokenEndpointFilterCustomizationBean {

    @Autowired
    @Qualifier("authsr_AuthorizationServerSecurityFilterChain")
    private SecurityFilterChain authorizationServerSecurityFilterChain;


    @PostConstruct
    public void modifyFilterChain() {

        Optional<OAuth2TokenEndpointFilter> tokenEndpointFilter = authorizationServerSecurityFilterChain.getFilters().stream()
                .filter(filter -> OAuth2TokenEndpointFilter.class.isAssignableFrom(filter.getClass()))
                .map(f -> (OAuth2TokenEndpointFilter) f)
                .findAny();

        if (tokenEndpointFilter.isEmpty()) {
            throw new RuntimeException("No OAuth2TokenEndpointFilter found");
        }

        OAuth2AccessTokenResponseAuthenticationSuccessHandler successHandler = new OAuth2AccessTokenResponseAuthenticationSuccessHandler();
        successHandler.setAccessTokenResponseCustomizer(c ->
        {
            Authentication authentication = c.get(Authentication.class);
            if (authentication instanceof OAuth2AccessTokenAuthenticationToken authToken) {
                var additionalParameters = new HashMap<>(authToken.getAdditionalParameters());
                additionalParameters.put("myAdditionalParamName", "myAdditionalParamValue");
                c.getAccessTokenResponse().additionalParameters(additionalParameters);
            }
        });

        tokenEndpointFilter.get().setAuthenticationSuccessHandler(successHandler);
    }
}

Here is an example project with this bean:
j24999ui-additional-param.zip (2.3 MB)

Regards,
Dmitry

Hello @taimanov

How can I get current user data here?

Hello @asroryuldashev57

Sorry for the late reply.

This question seems more related to Spring Security, rather than Jmix settings.

Currently Jmix by default configures Authorization Server in AuthServerAutoConfiguration.java.
Thus, all changes can be made in a standard manner, taking this configuration into consideration if the project does not have its own configuration for the token endpoint.

Current user infromation is not ready yet at the moment of /oauth2/token request. But if you override success handler, you will be able to retrieve user or client name from the Request. E.g. for password authorization grant type it may look like this:

@Component
public class TokenEndpointFilterCustomizationBean {

    @Autowired
    @Qualifier("authsr_AuthorizationServerSecurityFilterChain")
    private SecurityFilterChain authorizationServerSecurityFilterChain;

    @PostConstruct
    public void modifyFilterChain() {

        Optional<OAuth2TokenEndpointFilter> tokenEndpointFilter = authorizationServerSecurityFilterChain.getFilters().stream()
                .filter(filter -> OAuth2TokenEndpointFilter.class.isAssignableFrom(filter.getClass()))
                .map(f -> (OAuth2TokenEndpointFilter) f)
                .findAny();

        if (tokenEndpointFilter.isEmpty()) {
            throw new RuntimeException("No OAuth2TokenEndpointFilter found");
        }
        AuthenticationSuccessHandler successHandler =
                new AuthenticationSuccessHandlerWrapper(new OAuth2AccessTokenResponseAuthenticationSuccessHandler());
        tokenEndpointFilter.get().setAuthenticationSuccessHandler(successHandler);
    }

    public static class AuthenticationSuccessHandlerWrapper implements AuthenticationSuccessHandler {

        private static final Logger log = LoggerFactory.getLogger(AuthenticationSuccessHandlerWrapper.class);
        private OAuth2AccessTokenResponseAuthenticationSuccessHandler delegate;

        public AuthenticationSuccessHandlerWrapper(OAuth2AccessTokenResponseAuthenticationSuccessHandler delegate) {
            this.delegate = delegate;
        }

        @Override
        public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
            if (authentication instanceof OAuth2AccessTokenAuthenticationToken authToken) {
                var additionalParameters = new HashMap<>(authToken.getAdditionalParameters());
                additionalParameters.put("myAdditionalParamName", "myAdditionalParamValue");

                String username = request.getParameter("username");
                if (username != null) {
                    additionalParameters.put("myAdditionalUsernameBasedParameter", "someFunctionOfUser_" + username);
                }

                delegate.onAuthenticationSuccess(request, response,
                        new OAuth2AccessTokenAuthenticationToken(authToken.getRegisteredClient(),
                                (Authentication) authToken.getPrincipal(),
                                authToken.getAccessToken(),
                                authToken.getRefreshToken(),
                                additionalParameters));
            } else {
                delegate.onAuthenticationSuccess(request, response, authentication);
            }
        }
    }
}

Regards,
Dmitry