How to check role of user in express application

Hello,

First some context to my question:

  • As example I have a blog application build with node express.
  • The Authorization Code Flow is used to authenticate users.
  • There are 2 users, user A is a normal user with no roles. User B has the role admin, given to him via the auth0 dashboard.
  • In my application there is a route called /admin-dashboard only users with the role admin should be allowed to access this route.

In my application I would like to see/check what role a user has, so I can limit or allow access to certain routes. First I thought about using scopes, but from information in the docs it seems users can allow or deny any scope. This wouldnā€™t be of much use because users could then grant them self the scope admin:dashboard for example.

What would be the recommended approach to this problem. There was some information in the docs about rules but no information on how to attach a role to a user given to them in the auth0 dashboard. Only based on email. Or is it common practice to make this authorization myself as described below?

  1. Authenticate the user.
  2. Check the corresponding role in my database.
  3. Manually attach the role to the user object.

This example gets really close to want I want to do. The only downside is that it is using scopes. Is it possible to add scopes to the access token based on the users role. Or is this not the recommended way to use scopes.

Just found out that you can restrict the scope using the Enable RBAC option in the custom API settings. When a user request certain scopes the custom API checks the role and grants the scopes if allowed.

This doesnā€™t quite solve the problem because the example blog express app is not a custom API. Also when using the Authorization Code Flow to authenticate a user I donā€™t know if it wants the admin scope admin:dashboard or it is a normal user.

Hi @Luukth, I replied in your other thread - Trouble understanding scopes vs permissions - #3 by Marcos_Castany

This doesnā€™t quite solve the problem because the example blog express app is not a custom API.

Well, you can think a regular web application as a client (the frontend) and an API (the backend) at the same time. You can create the client and an API in the dashboard and use the built in roles.

Also when using the Authorization Code Flow to authenticate a user I donā€™t know if it wants the admin scope admin:dashboard or it is a normal user.

I addressed this in the other thread

If you need to know the specific Role of a user in your application, the only way of knowing it is by adding a custom claim via rules (example here - Sample Use Cases: Rules with Authorization)

Let me know if you have any other doubt. Iā€™m currently working on the docs to give better guidance.

Thanks,
Marcos

Hi @Marcos_Castany, many thanks for your reply on both threads. I didnā€™t fully understand the uses of scopes and roles/permissions, I do now :slight_smile:.

I have chosen to use roles in the (Profile) idToken. For now all my logic is in one express app, I can attach the role to the req.user.roles object using passport.

Lets hope these threads can help others.

1 Like

Thanks a lot for sharing it with the rest of community @Luukth!

1 Like

Can you sketch this out please? Iā€™m in the same boat you were in, but my head is swimming trying to get all of these terms distinct.

How do you get the roles into the user object?

I didnā€™t fully understand the uses of scopes and roles/permissions, I do now :slight_smile:.

I have chosen to use roles in the (Profile) idToken. For now all my logic is in one express app, I can attach the role to the req.user.roles object using passport. telldunkin

OK, I think I figured it out.

This conversation comes up in Google, when you look for how to access user permissions in the req.user object, so Iā€™ll document what I learned here:

You need to create an API (not use the default Auth0 Management API) and create your own set of permission types. In that API, you need to turn on RBAC, and turn on ā€˜add permissions to the access tokenā€™.

In the express code, following the example, you need to change the the /login code to:

  // Perform the login, after login Auth0 will redirect to callback
        router.get('/login', passport.authenticate('auth0', {
          scope: 'openid email profile',
          audience: '<your audience>'
        }), function (req, res) {
          res.redirect('/');
        });

Then you need to look at where the Auth0Strategy is created, and add code to the callback function:

var jsonwebtoken = require('jsonwebtoken');
var Auth0Strategy = require('passport-auth0');

 var strategy = new Auth0Strategy(
  {
    domain:     ...
    clientID:     ...
    clientSecret:  ...
    callbackURL: ...
  },
  function (accessToken, refreshToken, extraParams, profile, done) {
    // accessToken is the token to call Auth0 API (not needed in the most cases)
    // extraParams.id_token has the JSON Web Token
    // profile has all the information from the user

    // decode the access token (does not verify)
    var decoded = jsonwebtoken.decode(accessToken);
    profile.permissions = decoded.permissions;
    console.log(decoded,profile);
    // console.log("auth0 strategy callback",...arguments);
    return done(null, profile);
  }
);
passport.use(strategy);

This copies the permission information into req.user, so that req.user.permissions now has [ ā€œscope:firstā€, ā€œscope:secondā€ ] etc.

Further, if you app has a machine-to-machine connection, you need to:

  • Create a new Machine-to-Machine application

  • Link it to the new API

  • Create the middleware in your app:

    const checkJwt = jwt({
    // Dynamically provide a signing key
    // based on the kid in the header and 
    // the signing keys provided by the JWKS endpoint.
    secret: jwksRsa.expressJwtSecret({
      cache: true,
      rateLimit: true,
      jwksRequestsPerMinute: 5,
      jwksUri: <your uri>
    }),
    
    // Validate the audience and the issuer.
      audience: <your API audience uri>,
      issuer:  <your auth0.com url>,
      algorithms: ['RS256']
    

    });

If you include the checkJwt middleware in your path, then req.user will contain req.user.scopes, which has the permissions youā€™ve given to that particular machine-to-machine secret.

To check permissions, you can use middleware code like this:

const default_permissions = [ 'view: public' ];
function checkPermissionJson(scope_required) {
	return function(req,res,next)
	{
		var user = req.user || {};
		var scopes = default_permissions.concat(req.user.permissions || req.user.scopes || []);
		if(scopes.includes(scope_required)) return next();
		return res.status(400).json({error:"Insufficient privileges. Need "+scope_required+"; have "+scopes.join(',')})
	}
}

The only thing Iā€™ve not figured out yet is how to run both authentication checks on the same endpoint; for the moment Iā€™ve got M2M endpoints on paths like ā€œ/api/readā€ and I just use the default passport checks on ā€œ/json/readā€ but this feels awkward to me.

1 Like

Ok, found it. Give the checkJwt function the option credentialsRequired: false. Now you can run it on all relevant routes.

To check for an authenticated user, just check for the existence of req.user.
app.use('/api',function(req,res,next){ if(req.user) return next(); return res.status(400).send("need login");});

To check for an authenticated user with permissions, use the checkPermissions code.

Yay!

1 Like

Wow! Thanks a lot for sharing all that and glad you have figured it out!

This topic was automatically closed 15 days after the last reply. New replies are no longer allowed.