My Jakarta web application is using keycloak-jakarta-servlet-filter-adapter to handle authentication from Keycloak.
When the user is authenticated and has the required audience, CORS headers are well set to the preflight and actual responses.
When the user is authenticated but does not have the required audience, the preflight response has CORS headers set, but not the actual response (the one with code 403).
┌───────────────────────────────┐┌─────────────────────────────────┐
│ Token with required audience ││ Token without required audience │
│ ││ │
│ ┌────────────────┐ ││ ┌────────────────┐ │
│ ┌───────┐ │keycloak servlet│ ││ ┌───────┐ │keycloak servlet│ │
│ │Browser│ │filter adapter │ ││ │Browser│ │filter adapter │ │
│ └───┬───┘ └───────┬────────┘ ││ └───┬───┘ └───────┬────────┘ │
│ │ │ ││ │ │ │
│ │ OPTIONS ┌┴┐ ││ │ OPTIONS ┌┴┐ │
│ ├────────────►│ │ ││ ├────────────►│ │ │
│ │ │ │ ││ │ │ │ │
│ │ 200 CORS │ │ ││ │ 200 CORS │ │ │
│ │◄────────────┤ │ ││ │◄────────────┤ │ │
│ │ └┬┘ ││ │ └┬┘ │
│ │ GET ┌┴┐ ││ │ GET ┌┴┐ │
│ ├────────────►│ │ ││ ├────────────►│ │ │
│ │ │ │ ││ │ │ │ │
│ │ 200 CORS │ │ ││ │ 403 no CORS │ │ │
│ │◄────────────┤ │ ││ │◄────────────┤ │ │
│ │ └┬┘ ││ │ └┬┘ │
└───────────────────────────────┘└─────────────────────────────────┘
The problem is my frontend cannot handle the 403 response because the browser (tested on Chrome and Firefox) blocks the response because of the CORS policy, what I understand and accept.
The keycloak filter set the CORS headers either in the method PreAuthActionsHandler.preflightCors()
if the request method is "OPTIONS"
:
at org.keycloak.adapters.PreAuthActionsHandler.preflightCors()
at org.keycloak.adapters.PreAuthActionsHandler.handleRequest()
at org.keycloak.adapters.servlet.KeycloakOIDCFilter.doFilter()
or in the method AuthenticatedActionsHandler.corsRequest()
if the token is authenticated :
at org.keycloak.adapters.AuthenticatedActionsHandler.corsRequest()
at org.keycloak.adapters.AuthenticatedActionsHandler.handledRequest()
at org.keycloak.adapters.servlet.KeycloakOIDCFilter.doFilter()
The filter is not the responsible ! It was a Keycloak configuration issue.
When you give the uma_protection
role of a client to a user, the roles
client scope automatically adds the client to the audience of access token through the audience resolve
mapper.
To test the authorization I was removing the uma_protection
role of the client from the user. Thus, the client was not added to the audience of the access token anymore.
The goal is to add the client to the audience of the access token.
Don't know if other options are available, but the one that we chose is to create a client scope with a token mapper of type Audience
having the client included :
We then add this scope to each client that needs an access token allowed to request the first one: