Skip to content

Instantly share code, notes, and snippets.

@pReya
Last active February 13, 2025 23:57
Show Gist options
  • Save pReya/0546d531117858417da7b8f042a0c58d to your computer and use it in GitHub Desktop.
Save pReya/0546d531117858417da7b8f042a0c58d to your computer and use it in GitHub Desktop.
Use Nextcloud as Authentication Provider for Node-RED via `passport-oauth2`.

Use Nextcloud as Authentication Provider for Node-RED

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.

Preparations

  1. 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 Identifier and Secret key somewhere
  2. Install the passport-oauth2 (and the xml2js) 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 like docker exec -it your_node_red_container /bin/bash, switch to the data folder via cd /data and then do npm i passport-oauth2 xml2js).

Simple Case: Manually define allowed usernames

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: "*",
        }],
    },

Advanced Case: Verify users based on their Nextcloud Groups

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);
            });

        },
    },
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment