When I tried to use Nextcloud as an Auth Provider for Node-RED, I was not able to find good documentation on this topic, so I am summarizing my experience here. This was tested with Nextcloud 29.0.9 and Node-RED v4.0.5.
- Create a new OAuth client in your Nextcloud by going to
Administration Settings -> Security(pick the lower menu option under the Administration title – not your personal Security settings). Scroll down to OAuth 2.0 clients and add a new one (pick a descriptive name and put down the URL of your Node-RED instance followed by/auth/strategy/callback(e.g.https://node-red.mydomain.de/auth/strategy/callback)- Add a new client and pick a descriptive title
- For
Redirection URIput down the URL of your Node-RED instance followed by/auth/strategy/callback(e.g.https://node-red.mydomain.de/auth/strategy/callback) - Write down the values for
Client IdentifierandSecret keysomewhere
- Install the
passport-oauth2(and thexml2js) npm package in your Node-RED instance. How you can do this, depends on how you installed Node-RED. You can find more information here. For me, using a Docker-based setup, I just installed the package via an interactive shell inside of the container (e.g. run something likedocker exec -it your_node_red_container /bin/bash, switch to the data folder viacd /dataand then donpm i passport-oauth2 xml2js).
The easiest case is manually defining the Nextcloud usernames, which are allowed into your Node-RED instance. Inside of your settings.js config file, add or edit the adminAuth property like this:
adminAuth: {
type: "strategy",
strategy: {
name: "oauth2",
label: "My Nextcloud",
icon: "fa-cloud",
strategy: require("passport-oauth2").Strategy,
options: {
// EDIT ME
authorizationURL: "https://nextcloud.mydomain.de/apps/oauth2/authorize",
// EDIT ME
tokenURL: "https://nextcloud.mydomain.de/apps/oauth2/api/v1/token",
// EDIT ME: This is the client identifier you got from Nextcloud
clientID: "abc123",
// EDIT ME: This is the secret key you got from Nextcloud
clientSecret: "abc123",
// EDIT ME: This is the same URL you put down for Redirection URI in Nextcloud
callbackURL: "https://node-red.mydomain.de/auth/strategy/callback",
verify: async function (accessToken, refreshToken, params, profile, done) {
const username = params["user_id"];
done(null, username);
},
},
},
// EDIT ME: Here you can define the (Nextcloud) usernames of all people who should have access to your Node-RED instance
users: [{
username: "Admin",
permissions: "*",
},
{
username: "myuser1",
permissions: "*",
}],
},For my use case I wanted to verify users not based on their names, but instead of their Nextcloud group(s), so that all users that are part of the admin Nextcloud group can access the Node-RED instance. Unfortunately the OAuth approach does not return any information other than the username from Nextcloud to Node-RED. So we need to make a second fetch, to get additional info (like their groups) on a username.
adminAuth: {
type: "strategy",
strategy: {
name: "oauth2",
label: "My Nextcloud",
icon: "fa-cloud",
strategy: require("passport-oauth2").Strategy,
options: {
// EDIT ME
authorizationURL: "https://nextcloud.mydomain.de/apps/oauth2/authorize",
// EDIT ME
tokenURL: "https://nextcloud.mydomain.de/apps/oauth2/api/v1/token",
// EDIT ME: This is the client identifier you got from Nextcloud
clientID: "abc123",
// EDIT ME: This is the secret key you got from Nextcloud
clientSecret: "abc123",
// EDIT ME: This is the same URL you put down for Redirection URI in Nextcloud
callbackURL: "https://node-red.mydomain.de/auth/strategy/callback",
verify: async function (accessToken, refreshToken, params, profile, done) {
// EDIT ME
const ALLOWED_NEXTCLOUD_GROUPS = ["node-red", "admin"];
// EDIT ME
const NEXTCLOUD_URL = "https://nextcloud.mydomain.de";
const parseString = require('xml2js').parseString;
const username = params["user_id"];
const userResponse = await fetch(`${NEXTCLOUD_URL}/ocs/v1.php/cloud/users/${username}`, {
headers: {
"OCS-APIRequest": "true",
"Authorization": `Bearer ${accessToken}`
}
});
const userInfoXml = await userResponse.text();
parseString(userInfoXml, function (err, result) {
if (err) done(null, false);
const groups = result?.ocs?.data?.[0]?.groups[0]?.element;
const hasAllowedGroup = groups.filter(value => ALLOWED_NEXTCLOUD_GROUPS.includes(value));
if (groups && Boolean(hasAllowedGroup.length)) {
done(null, username);
} else {
done(null, false);
}
});
},
},
},
users: function (username) {
return new Promise(function (resolve) {
// EDIT ME
const NEXTCLOUD_URL = "https://nextcloud.mydomain.de";
if (username) {
const user = {
username,
image: `${NEXTCLOUD_URL}/avatar/${username}/64/dark?v=2`,
permissions: "*"
};
resolve(user);
}
resolve(null);
});
},
},