Hello, I think I’m having issues with Keycloak not returning CORS headers for the preflight OPTIONS requests.
Everything works as expected when I try to login, and when using the application in pilot/production, but when I go from my localhost:4200 angular application to our pilot backend https://pilot-xxxxx.mydomain.com/api the OPTIONS preflight request fails with:
CORS header ‘Access-Control-Allow-Origin’ missing
First I thought it was unrelated to Keycloak, so I added a jax-rs CorsFilter (ContainerRequestFilter.java) that pretty much everyone says is the solution to my jax-rs Java API application:
@Provider
@PreMatching
public class CorsFilter implements ContainerRequestFilter, ContainerResponseFilter {
@Override
public void filter(ContainerRequestContext request) throws IOException {
if (isPreflightRequest(request)) {
request.abortWith(Response.ok().build());
}
}
private static boolean isPreflightRequest(ContainerRequestContext request) {
return request.getHeaderString("Origin") != null && request.getMethod().equalsIgnoreCase("OPTIONS");
}
@Override
public void filter(ContainerRequestContext request, ContainerResponseContext response) throws IOException {
final String origin = request.getHeaderString("Origin");
if (origin == null) {
return;
}
if (isPreflightRequest(request)) {
response.getHeaders().add("Access-Control-Allow-Credentials", "true");
response.getHeaders().add("Access-Control-Allow-Methods", "GET, POST, PUT, DELETE, OPTIONS, HEAD");
response.getHeaders().add("Access-Control-Allow-Headers", "X-Requested-With, Authorization, Accept-Version, Content-MD5, CSRF-Token, Content-Type");
}
/* Allow every Origin */
response.getHeaders().add("Access-Control-Allow-Origin", origin);
}
}
But this filter is never triggered for any OPTIONS preflight requests.
After doing some more research it seems that because the preflight request does not contain any Authentication headers it is passed to Keycloak for authentication before it hits the CorsFilter.
I then tried to disable authentication of OPTIONS requests in the web.xml:
<web-app>
...
<security-constraint>
<web-resource-collection>
<web-resource-name>Public</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method>OPTIONS</http-method>
</web-resource-collection>
</security-constraint>
<security-constraint>
<web-resource-collection>
<web-resource-name>Restricted Access</web-resource-name>
<url-pattern>/*</url-pattern>
<http-method-omission>OPTIONS</http-method-omission>
</web-resource-collection>
<auth-constraint/>
</security-constraint>
....
</web-app>
But this also does not seem to work.
Then I read that I had to enable cors in the keycloak.json configuration file. I figured that would be the same as configuring it on the oidc-client in my Wildfly 31 standalone:
<subsystem xmlns="urn:wildfly:elytron-oidc-client:2.0">
<secure-deployment name="my-war.war">
<provider-url>xxxxxx</provider-url>
<ssl-required>NONE</ssl-required>
<enable-cors>true</enable-cors>
<autodetect-bearer-only>true</autodetect-bearer-only>
<verify-token-audience>false</verify-token-audience>
<realm>my-realm</realm>
<client-id>my-client</client-id>
<use-resource-role-mappings>true</use-resource-role-mappings>
<credential name="secret" secret="xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"/>
</secure-deployment>
</subsystem>
But I saw no effect after enabling cors in the standalone.
I have also tried adding the configuration: cors-allowed-headers and cors-allowed-methods with no effect.
<cors-allowed-headers>x-requested-with, Content-Type, origin, authorization, accept, client-security-token</cors-allowed-headers>
<cors-allowed-methods>POST, GET, OPTIONS</cors-allowed-methods>
Does anyone have any suggestions/advice for other steps I can try, or is able to spot the issue?
And do you think this is related to the preflight request going to Keycloak instead of my CorsFilter?
Error in the browser:
Cross-Origin Request Blocked: The Same Origin Policy disallows reading the remote resource at https://pilot-xxxxxxxxxxxxxxxxx/api/2.0/alert/active. (Reason: CORS header ‘Access-Control-Allow-Origin’ missing). Status code: 200.