Java – Spring security oauth2 – Add filter after oauth/token call

Spring security oauth2 – Add filter after oauth/token call… here is a solution to the problem.

Spring security oauth2 – Add filter after oauth/token call

I’m using Spring Boot 2.1.1.RELEASE (spring-security-oauth2-2.3.4.RELEASE).

I want to create a filter with priority after the TokenEndpoint#postAccessToken call. Why? Because in that filter, I want to get the token from the tokenStore and add it as a cookie to the response.

I

hope, this will give me what I want:

@Override
protected void configure(HttpSecurity http) throws Exception {
    http
    . (...)
    .addFilterAfter(new MyFilter(), BasicAuthenticationFilter.class);
}

But this is not the case. I can see that BasicAuthenticationFilter is called after successful validation on oauth/token but it doesn’t go into my MyFilter.

How do I call MyFilter after the oauth/token call?


You want to set cookie from authorization server or from resource server?
Is your auth server and resource server both are in same context? or different applications.?

I have two microservices. The first is the authorization server, which provides a JWT token (signed by its private key). The second microservice is a resource server that validates tokens against the authorization server public key, which is exposed by the Auth server through a REST endpoint

Do you want to set after receiving access_token from authorization server? What > do you want to do by setting cookie?

No. I want the authorization server to set a cookie when the front-end application calls oauth/token. This way, the browser is responsible for adding tokens to each request, not my front-end application. This protects me from XSS attacks because cookies will be set to httpOnly and secure.

Is your plan is to read cookie for getting access_token?

That’s right. But this should be done by the resource server (which hasn’t done so yet

).

simple way is to create an API for the same functionality. Which takes access_token as request parameter and sets the cookie.

Do you recommend using something like a proxy microservice between the front-end application and the authentication/resource server? A proxy microservice that sets jwt token to cookie and reads token from cookie?

Solution

No. I would like the authorization server to set a cookie when oauth/token call is made by the frontend application.

You need to add filters before all the filters, I mean filter order 1 so that the request arrives first and is sent last.

If it’s not spring-boot, it’s much easier to configure Spring using web.xml or java config. Since Spring Boot does not rely on web.xml all filters are proxy filters, except for DelegatingFilterProxy (springSecurityFilterChain), we cannot add any filters until then.

  • The possible way to implement your requirements is to register the filter using order(1) in the FilterRegistrationBean.

  • Give the filter url pattern/oauth/token

  • In your filter, use the HttpServletResponseWrapper implementation to read the response and get the access_token and set the cookie according to your requirements.

Register the filter with the FilterRegistrationBean in any configuration class

@Configuration
public class AppInitializer
{
    @Bean
    public FilterRegistrationBean<AccessTokenAlterFilter> sessionTimeoutFilter()
    {
        FilterRegistrationBean<AccessTokenAlterFilter> registrationBean = new FilterRegistrationBean<>();
        AccessTokenAlterFilter filter = new AccessTokenAlterFilter();

registrationBean.setFilter(filter);
        registrationBean.addUrlPatterns("/oauth/token");
        registrationBean.setOrder(1);  set precedence
        return registrationBean;
    }
}

Your filter

import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.PrintWriter;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpServletResponseWrapper;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AccessTokenAlterFilter implements Filter
{

Logger OUT = LoggerFactory.getLogger(AccessTokenAlterFilter.class);

@Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException
    {
        OUT.info("[[[[[[[[[[[[STARTED]]]]]]]]]]]]]]");

CharResponseWrapper wrappedResponse = new CharResponseWrapper((HttpServletResponse) response);
        chain.doFilter(request, wrappedResponse);
        byte[] bytes = wrappedResponse.getByteArray();
        String out = new String(bytes);
        OUT.info("Response String: {}", out);

response.getOutputStream().write(out.getBytes());

OUT.info("[[[[[[[[[[[[ENDED]]]]]]]]]]]]]]");
    }

private static class ByteArrayServletStream extends ServletOutputStream
    {
        ByteArrayOutputStream baos;

ByteArrayServletStream(ByteArrayOutputStream baos)
        {
            this.baos = baos;
        }

public void write(int param) throws IOException
        {
            baos.write(param);
        }

@Override
        public boolean isReady()
        {
            return false;
        }

@Override
        public void setWriteListener(WriteListener listener)
        {}
    }

private static class ByteArrayPrintWriter
    {
        private ByteArrayOutputStream   baos    = new ByteArrayOutputStream();
        private PrintWriter             pw      = new PrintWriter(baos);
        private ServletOutputStream     sos     = new ByteArrayServletStream(baos);

public PrintWriter getWriter()
        {
            return pw;
        }

public ServletOutputStream getStream()
        {
            return sos;
        }

byte[] toByteArray()
        {
            return baos.toByteArray();
        }
    }

public class CharResponseWrapper extends HttpServletResponseWrapper
    {
        private ByteArrayPrintWriter    output;
        private boolean                 usingWriter;

public CharResponseWrapper(HttpServletResponse response)
        {
            super(response);
            usingWriter = false;
            output = new ByteArrayPrintWriter();
        }

public byte[] getByteArray()
        {
            return output.toByteArray();
        }

@Override
        public ServletOutputStream getOutputStream() throws IOException
        {
            if (usingWriter)
            {
                super.getOutputStream();
            }
            usingWriter = true;
            return output.getStream();
        }

@Override
        public PrintWriter getWriter() throws IOException
        {
            if (usingWriter)
            {
                super.getWriter();
            }
            usingWriter = true;
            return output.getWriter();
        }

public String toString()
        {
            return output.toString();
        }
    }
}

The previous process will still exist, as shown below
enter image description here

You can control the response object and add cookies. Only logs are displayed for your reference.

enter image description here

Related Problems and Solutions