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
You can control the response object and add cookies. Only logs are displayed for your reference.