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 ?
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
You can get response object under control and add the cookie. Just showing logs for your reference.