CORS preflight request freezes our app

Hello folks,

in need some help with a tricky CORS problem :wink:

We are currently developing a small Dash application. Our app is hosted in the cloud, so we need some sort of security. We have connected our app to our enterprise identity management system (KeyCloak).
Therefore we use the framework flask_oidc (1.4.0). We configured the client_secrets.json and protect every view-function of our dash-app:

    def _protect_dashviews(dash_app):
        for view_func in dash_app.server.view_functions:
            if view_func.startswith(dash_app.config.url_base_pathname):
                dash_app.server.view_functions[view_func] = oidc.require_login(
                    dash_app.server.view_functions[view_func])

This works pretty fine. On the initial visit of our app the user is redirected to the login page of our idm and after a successful login back to our app. Except of one point: After some time our oidc token seems to time out. When the page _/dash-update-component is called, the javascript behind this component sends a HTTP-Request to /auth/realms/appid-0798/protocol/openid-connect/auth?xyz. Sadly this request is declined because of CORS. The preflight request of this request gets the following response:

Access to fetch at 'https://login.idm.company.com/auth/realms/appid-0798/protocol/openid-connect/auth?xyz (redirected from ‘https://app.cloud.net/_dash-update-component’) from origin ‘https://app.cloud.net’ has been blocked by CORS policy: Response to preflight request doesn’t pass access control check: No ‘Access-Control-Allow-Origin’ header is present on the requested resource. If an opaque response serves your needs, set the request’s mode to ‘no-cors’ to fetch the resource with CORS disabled.

I understand why this preflight request is sent. And i understand that the responding idm system needs to respond with a ‘Access-Control-Allow-Origin’ header. This could be a possible fix, but in the moment it is not possible to modify the settings of the idm system.
Is there any chance to fix this on our side (inside the app)? Is there any possibility to modify the component or request, so no preflight request is sent?

Thanks in advance.
Regards, Florian

3 Likes

I am having this EXACT same issue with MSAL python protecting dash views with access/refresh tokens to Microsoft Graph. Were you able to solve this problem with your app and if so how?

Hello @Bw984 ,

sadly i was not able to solve this problem entirely, but i believe this is a problem of our keycloak (idm).
I solved this problem by omitting the refresh of the token. We just do an initial login at the first visit of the session and save a flag in the cookie if the login was successful.

I hope the following code snippet helps to understand:
I use the following framework: flask_dance

init.py

oauth_blueprint = OAuth2ConsumerBlueprint(
    "xy-oauth", __name__, client_id="xy", login_url='/login',
    client_secret=os.getenv('IDM_CLIENT_SECRET'),
    base_url=IDM_BASE_URL,
    token_url=IDM_BASE_URL + "token",
    authorization_url=IDM_BASE_URL + "auth",
    authorized_url='/oidc_callback')
app.server.register_blueprint(oauth_blueprint, url_prefix="/")

def require_login(view_func):
    """
    Use this to decorate view functions that require a user to be logged
    in. If the user is not already logged in, they will be sent to the
    Provider to log in, after which they will be returned.

    .. versionadded:: 1.0
       This was :func:`check` before.
    """

    @wraps(view_func)
    def decorated(*args, **kwargs):
        if not oauth_blueprint.session.authorized:
            return redirect(url_for("xy-oauth.login"))
        return view_func(*args, **kwargs)
    return decorated


def _protect_dashviews(app):
    for view_func in app.server.view_functions:
        if view_func.startswith(app.config.url_base_pathname):
            app.server.view_functions[view_func] = require_login(
                app.server.view_functions[view_func])

app.py

_protect_dashviews(app)
app.run_server(host='0.0.0.0', debug=False)

Let me know if you have further questions.

Regards, Florian

I tracked my problem down to multiple gunicorn workers working on the same set of callbacks after a user interaction. I did not have flask sessions setup through a common database such as redis and some of the gunicorn workers were getting re-directed to my MS login page since they didn’t have a logged in user in THEIR session.

I was able to fix the issue by changing my flask session system to redis, now each user has a true single source session object to utilize to verify auth. I tracked down the issue by going through the network tab on Chrome’s developer tools. I couldn’t figure out why my login page was calling dash update components and it lead me down the path of discovery regarding sessions and gunicorn workers.