3

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

I would like to create a filter with precedence after TokenEndpoint#postAccessToken call. Why ? 'cause in that filter I want to take the token from the tokenStore and add it as a cookie to the response.

I would expect that, this will give me what I want:

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

But it doesn't. I can see, that BasicAuthenticationFilter is called after successfull authentication on oauth/token but it doesn't enter my MyFilter.

What am I suppose to do to call MyFilter after 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. First one is authorization server, that provides jwt tokens (signed by its private key). Second microservice is a resource server, that validates tokens based on authorization server public key (exposed via REST endpoint by Auth server)

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

No. I would like the authorization server to set a cookie when oauth/token call is made by the frontend application. That way the browser is responsible for adding a token to each request rather than my frontend app. That protects me against XSS attack, as the cookie will be set as httpOnly and secure.

Is your plan is to read cookie for getting access_token?

Correct. But that supposed to be done by resource server (haven't done that, yet)

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

Are you suggesting something like a proxy microservice that stands between frontend application and auth/resource servers ? proxy microservice that is setting jwt token as cookie, and read token from cookie ?

PraveenKumar Lalasangi
  • 3,255
  • 1
  • 23
  • 47
user3529850
  • 1,632
  • 5
  • 32
  • 51
  • https://stackoverflow.com/a/42202158/2104638 – Pasupathi Rajamanickam Oct 10 '19 at 21:35
  • simple way is to create an API for the same functionality. Which takes access_token as request parameter and sets the cookie. – PraveenKumar Lalasangi Oct 10 '19 at 22:00
  • If you explain your requirement it will be good. You want to set cookie from authorization server or from resource server? Do you want to set after receiving access_token from authorization server? Is your auth server and resource server both are in same context? or different applications.? What do you want to do by setting cookie? Is your plan is to read cookie for getting access_token? If you update question with your approach, it will help most of visitors. – PraveenKumar Lalasangi Oct 10 '19 at 22:17
  • How did you solve this ? I want to achieve the same ? @user3529850 – ammy Jun 26 '20 at 15:54

1 Answers1

3

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 filter before all filters i mean filter order 1 so that request first reaches and last dispatches.

If it was not spring-boot it was much easier with web.xml or java config way of spring configuration. As spring boot does not rely on web.xml all filters are proxy filters except DelegatingFilterProxy(springSecurityFilterChain) before this we can not add any filters.

  • Possible way of achieving your requirement is registering a filter in FilterRegistrationBean with order(1).

  • Give the filter url pattern/oauth/token

  • In your filter use HttpServletResponseWrapper implementation to read response and get access_token and set cookie as per your requirement.

In Any configuration class register filter into FilterRegistrationBean
@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();
        }
    }
}

Previous flow will be still there as given below
enter image description here

You can get response object under control and add the cookie. Just showing logs for your reference.

enter image description here

PraveenKumar Lalasangi
  • 3,255
  • 1
  • 23
  • 47
  • [Referance for reading text from HttpServletResponse object](https://stackoverflow.com/questions/14736328/looking-for-an-example-for-inserting-content-into-the-response-using-a-servlet-f) – PraveenKumar Lalasangi Oct 11 '19 at 10:59
  • requests `/oauth/**` are filtered out and those will never reach spring security configuration `WebSecurityConfigurerAdapter` but reaches only AuthorizationServerConfigurerAdapter configuration where we have no option of adding filter. And hence trying `.addFilterAfter` or `addFilterBefore` can't be succeeded in getting `oauth/**` request into defined filter. – PraveenKumar Lalasangi Oct 11 '19 at 11:34
  • @user3529850 is that solution! you expected? – PraveenKumar Lalasangi Oct 12 '19 at 06:09
  • user3529850 was curious to know whether it solved your problem or not. I am sure it should. But didn't get any feedback from you. – PraveenKumar Lalasangi Oct 12 '19 at 10:23
  • @user3529850 was expected feedback for my answer. – PraveenKumar Lalasangi Oct 17 '19 at 10:18
  • @FutureVisitor feel free to provide feedback. As i didn't receive feedback from OP. Any feedback will be appreciated. – PraveenKumar Lalasangi Oct 18 '19 at 14:40
  • first of all, Thank you, Sir. Second of all it works like a charm. Third of all, I had some other stuff to learn and no time to come back to that. Like I said, it's a perfect, working and valid solution, thanks again. Cheers ! – user3529850 Oct 18 '19 at 22:25
  • Actually I have one more question. The problem with your solution is that I cann't modify the response. Let's say I would like to add a `header` to the response after `chain.doFilter(request, wrappedResponse);` (in fact I want). Is it even possible, because it doesn't quite work. What I want to achieve is to take `access_token` from `out` object, put it as a header `Set-Cookie` in the response and send back to client. I don't think it's possible in your solution. But I might be wrong, hence the question. – user3529850 Oct 23 '19 at 17:59
  • @user It is possible to add cookie in response. How do you think it is not possible. You got control over response and read the response. In fact you are creating new response. Try to add cookie to the response. Consider adding a new question by putting all content what you have tried. – PraveenKumar Lalasangi Oct 24 '19 at 02:06
  • Your question was **add filter after oauth/token call** I gone one step ahead and shown how to read the response to get the token generated. Still you can set cookie to the response, not a big deal. Try it out. (Might be you have not tried my solution correctly) `response.getOutputStream().write(out.getBytes());` is nothing but you are modifying the response. `response.addCookie(new Cookie("access_token", "your_access_token"));` should work – PraveenKumar Lalasangi Oct 24 '19 at 03:52
  • body can be modified, headers not. Try to add `((HttpServletResponse) response).setHeader("TEST","TEST");` after `chain.doFilter(request, wrappedResponse);` and check that header is not set. – user3529850 Oct 24 '19 at 08:25
  • How did you solve this ? I want to achieve the same ? @user3529850 – ammy Jun 26 '20 at 15:54
  • 1
    @ammy I have done it long before. Have look at my github repo link. If i have cheanged nothing then it may help you. https://github.com/nlpraveennl/springsecurity/tree/802defb27ab1c625732d3055d0c691637c8043d3/H_BootOAuth2ResourceServer – PraveenKumar Lalasangi Jun 29 '20 at 04:27