More CORS issues Access-Control-Allow-Origin header not being sent by Keycloak

Hello, I know this subject has been beaten to death before, and I’ve spent a good portion of the last 24 hours trying to resolve this and drill down.

Here’s what I’ve got:

(I have to muddy the URLs because the forum software doesn’t allow new users to post more than 2 links)

Keycloak server running in container at

http://
foo:8080/

(minor obscuration), this has been working well for months.

Using the mozilla-django-oidc oidc authentication backend for Django.

Until now everything has worked find with pure Django, the browser does first party SSO renewal requests transparently.

Now I’m working on a React based client using openapi-client-axios to do the REST queries.

When I have the mozilla_django_oidc.middleware.SessionRefresh middleware enabled, it causes a session refresh every 15 minutes.

This middleware tries to detect XHR through Django’s (misguided) is_ajax() function, looking for these headers:
‘X-Requested-With’: ‘XMLHttpRequest’

If those headers are not present, the middleware simply generates a 302 redirect, which the axios based client tries to follow.

Normally you don’t want XHR following 302 redirects for authentication, but as a test when I redirect the browser window to the refresh_url, the auth framework ensures that I’m redirected back to my API get request, so the original data is fetched. I want this transparency in operation.

Try as I might, I cannot get Keycloak to emit the Access-Control-Allow-Origin header.

The XHR query has Origin set to this (this is minorly obscured, it’s not localhost and is being served from a django dev server):

http://
test-server:8081

Keycloak has “http://test-server:8081” (no trailing slash, just like the Origin header) set as the Web Origin.

I have withCredentials set to true as well, this is required so the cookies are furnished to KC when the 302 is made.

I’ve downloaded the KC source and scoured the Cors.java over and over, it clearly is supposed to create a Access-Control-Allow-Origin header.

The request is not a preflight request, it’s a GET that results in a 302 redirect back to the authentication endpoint; everything looks right in the headers and cookies, but I’m just not seeing results.

These are some of the request headers when the 302 redirect is fetched by the XHR client:

Host: foo:8080
Origin: h t t p://test-server:8081
Referer: h t t p://test-server:8081/

If Origin is set in the request headers, KC should respond with a Access-Control-Allow-Origin header if the Origin exists in the allowed origins:

if (allowedOrigins.contains(ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD)) {
response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN, ACCESS_CONTROL_ALLOW_ORIGIN_WILDCARD);
} else {
response.getOutputHeaders().add(ACCESS_CONTROL_ALLOW_ORIGIN, origin);
}

I just don’t understand why this code is never getting executed.

A hacky workaround is to just disable the middleware, but that would mean token idle timeouts don’t work and I would simply rely on receiving a 403 to indicate the end of session.

I think this issue can be put on the back burner. I found instructions on how to configure the JBOSS server to emit the ACAO headers, this did not work as expected.

The authentication seems to get stuck in a loop and does not have the desired effect.

Now I need to investigate the keycloak.js adapter.

There is an axios-keycloak npm module that adds an interceptor.

If the axios-keycloak module works as expected, I might be able to modify the openapi-client-axios module I’m using to add the interceptor from axios-keycloak.

I did see that keycloak.js has some code for managing silent session refresh using iframes and stuff, it seems that this relies on the keycloak.js being loaded from the auth server and there being a special HTML page on my site.

I just find it frustrating that I have to either choose a bare bones API or choose a sane API that doesn’t do session refresh.

Nevermind that I haven’t seen anything that actually supports the OIDC session refresh recommendations.

The last choice, a hacky one, is to write a script/app that just does session refresh from the client side, using the keycloak.js adapter. This would just be a .js included in my page that would stub out a popup and redirect.

I may go this route, allowing me to warn users their session is expiring soon.

I installed the keycloak-js module and added a little authenticator test, no dice.

I have 3rd party cookie access disabled in my browser and it seems that transparent SSO renewal doesn’t work without that.

I’ve given up on a 15min session check, that can only work with simple requests. I guess I’ll just have to set an 8 hour session timeout, set the X-Requested-With header, and catch 403 errors to force a login.

I can’t believe that the state of JS and the browser is so broken right now. The only workaround I could think of is a hidden iframe that just auto-refreshes from a dedicated page inside the authenticated zone. But is it worth hacking to that level?

How does Google do session renewal with all their JS driven stuff? Do they just avoid CORS altogether, or do they have tight CORS control on their headers?

FYI, I solved my problem and this is how: I created a react widget that gets the session token lifetime from the server, then it checks the expiration time periodically. When a certain number of seconds are remaining it prompts the user to renew their session. I then modified the middleware to accept a special HTTP header for AJAX requests so that it would force a session token reauth for that request. It returns a 403 with a refresh url that will reauth the session token and send the user back to the main page. Given the limitations of 3DP cookies, it’s the best solution I can come up with to ensure sessions are timed out if a user is deauthenticated in the OIDC server. I hope the browser developers can agree on a transparent strategy for session token reauth for Authorization Code flow.