node-red-dashboard icon indicating copy to clipboard operation
node-red-dashboard copied to clipboard

Infinite loop when express-openid-connect middleware is used

Open dpslavov opened this issue 6 months ago • 0 comments

Current Behavior

When a custom middleware based on "express-openid-connect" is used during the time of the first session everything operates normally but once it expires and new one needs to be established an infinite loop is created. This is because "callback" route critical for this middleware is served by PWA's service worker from its cache instead of the middleware itself which needs to return a session in form of a cookie. A simple solution for this particular case is provided by below patch, more generic one could consist of configuration variable for excluded routes for example.

--- a/ui/src/sw.js
+++ b/ui/src/sw.js
@@ -22,6 +22,8 @@ if (import.meta.env.DEV) {
     // this permits the request to be handled by the server which will do a redirect as required
     const configPath = self.location.pathname.split('/')[1]
     denylist.push(new RegExp(`/${configPath}/[^?]*/(\\?.*)*$`))
+    // don't precache the callback route which is handled by express-openid-connect middleware
+    denylist.push(new RegExp(`/${configPath}/callback\\?`))
 }

 // to allow work offline for allowed routes only

Expected Behavior

No response

Steps To Reproduce

  1. Configure custom OIDC middleware in settings.js, example configuration used with Hashicorp Vault:
dashboard: {
    middleware: (req, res, next) => {
        const issuerBaseURL = "";
        const baseURL = "";
        const clientID = "";
        const clientSecret = "";
        const secret = "";
        const group ="";

        // Workaround for double middleware call bug
        if (req.hasOwnProperty("oidc") && req.oidc.isAuthenticated()) return next();
        const { auth } = require("express-openid-connect");
        const jose = require("jose");
        const oidcAuth = auth({
            issuerBaseURL: issuerBaseURL,
            baseURL: baseURL,
            clientID: clientID,
            clientSecret: clientSecret,
            secret: secret,
            authorizationParams: {
                response_type: "code",
                scope: "openid security"
            },
            afterCallback: async (request, response, session) => {
                const claims = jose.JWT.decode(session.id_token);
                if (!(claims.hasOwnProperty("groups") && claims.groups.includes(group)))
                    return response.sendStatus(401);
                return session;
            }
        });
        oidcAuth(req, res, next);
    }
}
  1. Logon to the dashboard
  2. Close browser tab and delete "appSession" cookie to simulate expired session
  3. Try to logon to the dashboard again

Environment

  • Dashboard version: 1.24.2
  • Node-RED version: 4.0.9
  • Node.js version: 20.19.0
  • npm version:
  • Platform/OS: Linux 6.1.0-34-armmp arm LE
  • Browser: Edge/Firefox

Have you provided an initial effort estimate for this issue?

I am not a FlowFuse team member

dpslavov avatar Jun 26 '25 19:06 dpslavov