# Implicit list of users in adminAuth

**URL:** <https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813>\
**Category:** General\
**Tags:** security\
**Created:** [5 April 2019 13:28 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813 "2019-04-05T13:28:49Z")\
**Posts on this page:** 17\
**Page:** 1

<div class="post-metadata">

**Author:** ![stgv](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@stgv](https://discourse.nodered.org/u/stgv)\
**Post date:** [5 April 2019 13:28 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/1 "2019-04-05T13:28:49Z")

</div>

I'm looking into securing my company's Node-RED setup to only allow access to users in a specific GitLab group. I found a passport strategy for gitlab-oauth, which should work well enough with the `strategy`-type `adminAuth` setting. However, as it looks to me, I would still have to enter every member of the group individually in the `users` array, since the `done` function is supposed to check against it.

Does the `done` function _have_ to be called from the `verify`-function, though, or could I skip this additional check after verification on the OAuth response by simply creating a `user` struct and calling the `resolve` function with the verified user directly? Or is the `resolve` function not available/applicable in this scope because of the `callbackURI` requirements (which are handled as part of the `done` function, I would guess) and I would have to write a custom authentication script with direct usage of passport?

---

<div class="post-metadata">

**Author:** ![stgv](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@stgv](https://discourse.nodered.org/u/stgv)\
**Post date:** [8 April 2019 10:01 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/2 "2019-04-08T10:01:47Z")

</div>

All right, so I've tried a couple things myself so far, and think I have a bit of a better grip on the whole matter now. I manage to authenticate using a gitlab strategy, but I still need to have an entry in the `users` list. My settings so far:

```auto
adminAuth: {
          type:"strategy",
          strategy: {
              name:"gitlab",
              label: 'Sign-in via GitLab',
              icon:"fa-gitlab",
              strategy: require('passport-gitlab2').Strategy,
              options: {
                  clientID: GITLAB-CLIENT-ID,
                  clientSecret: TOP-SECRET-SECRET,
                  gitlabURL : "https://gitlab.com",
                  callbackURL: "http://localhost:1880/auth/strategy/callback",
                  verify: function(token, tokenSecret, profile, done) {
                      for (var i=0; i < profile.emails.length; ++i) {
                          if(profile.emails[i].value.search("@my-company\\.com") >= 0) {
                              return done(null, { username: profile.username });
                          }
                      };
                  }
              }
          },
          users: [{ username: "stgv", permissions: "*" }]
    },

```

Still, is there a way to do this log-on without having to hard-code all usernames with permission in the `users` array? Anything that can be passed to the done() function (or be used instead of it)?

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [8 April 2019 18:35 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/3 "2019-04-08T18:35:09Z")

</div>

Hi @stgv

Node-RED only uses the `users` property of `adminAuth` in order to retrieve an _already authenticated_ user's permissions.

If all of your users have the same permission, you can replace your `users` array with a function:

```auto
users: function(user) {
    return Promise.resolve({ username: user, permissions: "*" });
}

```

---

<div class="post-metadata">

**Author:** ![stgv](https://avatars.discourse-cdn.com/v4/letter/s/9de0a6/32.png) [@stgv](https://discourse.nodered.org/u/stgv)\
**Post date:** [9 April 2019 07:07 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/4 "2019-04-09T07:07:24Z")

</div>

Oh, nice! Didn't think of putting that function there directly - but it makes a lot of sense. Thanks a lot!

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [7 January 2020 22:39 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/5 "2020-01-07T22:39:39Z")

</div>

What if I want to give different permissions based on a group the user is on?

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [7 January 2020 22:41 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/6 "2020-01-07T22:41:06Z")

</div>

> [@marcus](#):
>
> What if I want to give different permissions based on a group the user is on

You still return the user's permissions - but you look them up from the group first. How you do that will depend entirely on how you choose to implement it.

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [7 January 2020 23:09 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/7 "2020-01-07T23:09:50Z")

</div>

by the end of the group checking I'm calling done function like this

`done(null, { username: { user: myuser, permissions: corret_permission }})`

because it seems the function on users entry only gets what is set to username (please correct me if I'm wrong)

and my users entry is kinda like this

```auto
users: function(username) {
	user = username.user
	perm = username.permissions
	
    return Promise.resolve({ 'username': user, 'permissions': perm });
}

```

it seems that some loop happens on this entry users, if I console.log() user and perm, first time it will print exactly what I set on done, but in the next run of this loop both user and perm are set to undefined.

I'm no developer and I'm pretty sure I'm probably doing something wrong here, can you help?

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [8 January 2020 14:52 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/8 "2020-01-08T14:52:12Z")

</div>

Sorry for the bump, but I could really use some help here 😕

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [8 January 2020 15:16 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/9 "2020-01-08T15:16:16Z")

</div>

Hi @marcus

without seeing those lines of code in context, I'm not clear where you are using them or what they are doing - in particular, that `done()` statement.

The `username` passed to the `users` function should be just the String username it wants you to return the permissions for. It shouldn't ever be an object like you are trying to use.

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [8 January 2020 15:19 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/10 "2020-01-08T15:19:05Z")

</div>

You mean I need to hard code the permission on users function? I don't understand how to specify different permission to different users, can you just show me an example?

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [8 January 2020 15:29 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/11 "2020-01-08T15:29:29Z")

</div>

No, you don't need to hardcode permissions in the `users` function. The `users` function can return a promise, which means you can do whatever work you need to lookup the permissions for a given `username`.

```auto
users: function(username) {
       return new Promise(function(resolve) {
           // Do whatever work is needed to check username
           // is a valid and lookup their permissions
           // ...
           // ...
           // get 'userPermissions' from somewhere
           if (valid) {
               // Resolve with the user object. It must contain
               // properties 'username' and 'permissions'
               var user = { username: "admin", permissions: userPermissions };
               resolve(user);
           } else {
               // Resolve with null to indicate this user does not exist
               resolve(null);
           }
       });
   },

```

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [8 January 2020 16:05 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/12 "2020-01-08T16:05:55Z")

</div>

TL;DR

**The only way to identify a valid user is using the username?**

Let me explain my scenario.

I'm trying to setup OAuth2 authentication agains a working on premise CAS like this:

```auto
    adminAuth: {
        type:"strategy",
        strategy: {
            name: "oauth2",
            label: 'Sign in SSO',
            icon:"fa-users",
            strategy: require('passport-oauth2').Strategy,
            options: {
                authorizationURL: 'https://xxxxxx/cas/oauth2.0/authorize',
                tokenURL: 'https://xxxxxx/cas/oauth2.0/accessToken',
                clientID: "xxxxxxxxxxxxxxx",
                clientSecret: "xxxxxxxxxxxx",
                callbackURL: "http://localhost/auth/strategy/callback",
                verify: function(token, tokenSecret, profile, done) {
                  done(null, profile)
                }
            }
        },

```

problem is: the `profile` is never returned, I've tried everything but it always returns an `empty hash`. It returns `access token` though, and with this I can access `/profile` which will return information regarding an authenticated user (e.g, it's AD groups) based on its access\_token. With that information I will setup it's credentials, either `*` or `read`.

Before you said I need to do this on the `Users` function, I was doing it on the verify, I can get the groups and the correct permissions there, and the problem was that I could never "send" this to the user function.

I'm now trying hit `/profile` directly on the user function, but to do that I need that token that I received at the authentication.

I even tried something like this on the verify function

```auto
verify: function(token, tokenSecret, profile, done) {
  profile.token = token
  done(null, profile)
}

```

and set users function like this:

```auto
users: function(profile) {
  return new Promise(function(resolve, profile) {
    const token = profile.token
    const https = require('https');
    console.log(token)
    https.get("https://xxxxxxxxxxxxxx/cas/oauth2.0/profile?access_token=" + token, (res) => {
      var bodyChunks = [];
      res.on('data', function(chunk) {
        bodyChunks.push(chunk);
      }).on('end', function() {
        var body = Buffer.concat(bodyChunks);
        var data = JSON.parse(body).attributes
        console.log(data)
        groups = data.memberOf.map(function(val, index){return val.split(',')[0].replace('CN=', '') })
        
        if (groups.include('mygroup')) {
          user = {'username': data.cn, 'permissions': '*'}
        } else {
          user = {'username': data.cn, 'permissions': 'read'}
        }
        
        resolve(user);
      })
    })
 });
}

```

I know the code is pretty ugly, but the real problem here is: **token comes as undefined**.

So, if I need to do whatever work is needed to validate the user on the users function how am I supposed to send the token? **Or the only way to identify a valid user is using the username?**

Sorry the long reply, let me know if I should open an different topic.

Best regards,  
Marcus Castro

---

<div class="post-metadata">

**Author:** ![knolleary](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/knolleary/32/3_2.png) [@knolleary](https://discourse.nodered.org/u/knolleary)\
**Post date:** [8 January 2020 16:39 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/13 "2020-01-08T16:39:54Z")

</div>

In your `verify` function of the oauth strategy, you could look up the user's permissions and cache them locally so you can return them in the later calls to the `users` function.

---

<div class="post-metadata">

**Author:** ![marcus](https://avatars.discourse-cdn.com/v4/letter/m/85e7bf/32.png) [@marcus](https://discourse.nodered.org/u/marcus)\
**Post date:** [8 January 2020 17:18 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/15 "2020-01-08T17:18:02Z")

</div>

Thanks very much [knolleary](https://discourse.nodered.org/u/knolleary)!

---

<div class="post-metadata">

**Author:** ![antoine-em](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/antoine-em/32/36364_2.png) [@antoine-em](https://discourse.nodered.org/u/antoine-em)\
**Post date:** [2 February 2021 10:02 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/16 "2021-02-02T10:02:29Z")

</div>

Sorry to re-open that thread but it is the closest answer I found so far.  
I am stuck at the same point, I don't understand how to pass the profile to the users section to check the permissions defined in the access token scope and map it to allow a read only or full access depending on the user scope permissions (defined in keycloak).  
Otherwise I can from the verify section read the necessary information to determine which level of permissions I should apply.

I don't understand how to "cache them locally" so if some explanations can be provided here I would be thankful.

---

<div class="post-metadata">

**Author:** ![antoine-em](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/antoine-em/32/36364_2.png) [@antoine-em](https://discourse.nodered.org/u/antoine-em)\
**Post date:** [3 February 2021 00:29 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/17 "2021-02-03T00:29:51Z")

</div>

To "fix" that, I am using at the moment a global var defined outside of the module.exports scope that is accessible in both the verify and users section.  
That said I am really not sure it is a clean or safe way to do it that way but it does the job so I can check the permissions from the access token in the users section either.

---

<div class="post-metadata">

**Author:** ![TotallyInformation](https://sea2.discourse-cdn.com/flex026/user_avatar/discourse.nodered.org/totallyinformation/32/31_2.png) [@TotallyInformation](https://discourse.nodered.org/u/TotallyInformation)\
**Post date:** [13 April 2022 13:25 UTC](https://discourse.nodered.org/t/implicit-list-of-users-in-adminauth/9813/18 "2022-04-13T13:25:57Z")

</div>


