Hi Everyone,
I’m integrating Keycloak authentication into Node-RED using passport-keycloak-oauth2-oidc
. The authentication flow is successful, and I can see the correct user profile and roles returned from Keycloak. However, after authentication, the app redirects back to the login screen instead of granting access to the Node-RED editor.
What I have so far:
- I’ve followed the structure and logic used in the
node-red-auth-github
example. - When I use the static
users
option (i.e. hardcoding users and permissions insettings.js
), everything works as expected — users are authenticated and redirected correctly to the editor. - Now, I want to fetch roles dynamically from Keycloak and assign permissions based on those roles instead of using the static
users
option.
Keycloak Setup:
- Realm:
MyRealm
- Client ID:
nodered-client
- Roles assigned in Keycloak:
nodered-editor
,nodered-viewer
- I confirmed that roles like
nodered-editor
are returned in the profile insideresource_access
.
My Passport Strategy:
var path = require("path");
var oauth2Strategy = require("passport-keycloak-oauth2-oidc");
var requiredOptions = [
'clientID',
'clientSecret',
'realm',
'authServerURL',
'baseURL'
];
module.exports = function (opts) {
// Validate required options
for (var i = 0; i < requiredOptions.length; i++) {
if (!opts.hasOwnProperty(requiredOptions[i])) {
throw new Error("Missing auth option: " + requiredOptions[i]);
}
}
// Construct callback URL
var callbackURL = opts.baseURL +
((opts.baseURL[opts.baseURL.length - 1] === "/") ? "" : "/") +
"auth/strategy/callback";
// Define adminAuth config
var adminAuth = {
type: "strategy",
strategy: {
name: "keycloak",
label: "Sign in with Keycloak",
icon: "fa-lock",
strategy: oauth2Strategy.Strategy,
options: {
clientID: opts.clientID,
clientSecret: opts.clientSecret,
realm: opts.realm,
authServerURL: opts.authServerURL,
callbackURL: callbackURL,
publicClient: 'false',
scope: "openid profile email",
sslRequired: 'external',
verify: function (accessToken, refreshToken, profile, done) {
console.log("Keycloak profile:", profile);
const roles = profile.roles?.resource_access?.[opts.clientID]?.roles || [];
console.log("Roles for the user:", roles);
let permissions = [];
if (roles.includes("nodered-editor")) {
permissions = ["*"]; // Full access
} else if (roles.includes("nodered-viewer")) {
permissions = ["flows.read"]; // Read-only
}
const user = {username: profile.username || profile.preferred_username || profile.sub,
permissions: permissions,email: profile.email || null,};
return done(null, user);
console.log("Authenticated user:", user);
}
}
}
};
// Optional default permission
if (opts.hasOwnProperty("default")) {
adminAuth.default = opts.default;
}
return adminAuth;
};
adminAuth in settings.js:
adminAuth: require('mynodered-plugin')({
clientID: "nodered-client",
clientSecret: "secret",
realm: "MyRealm",
authServerURL: "https://auth-example.com",
baseURL: "https://example.com/"
}),
My user logs:
Any guidance or examples that could help get role-based dynamic authentication working with Keycloak would be greatly appreciated!