Enforcing permissions in our SPA

How should we determine whether a user has a specific permission in the frontend of our SPA? Let’s say we have “delete a Foo” functionality in our application which calls /delete/foo in the API, but we want to only permit users who have the “delete:foo” permission to be able to use it.

If we turn on “Enable RBAC” and “Add Permissions in the Access Token” then the API can check the claims in the access token and check that the user has the “delete:foo” permission. That is great and a good start. However we will also want to hide this functionality on the frontend too - it doesn’t make sense to show the user the “delete a Foo” button if they don’t have permission to use it. This is where I’m a little stuck or confused. We are using oidc-client-js which exposes the idToken claims “profile” but not the accessToken - which makes sense as the accessToken isn’t really supposed to be used or decided by the client.

I have tried a couple of things in custom Auth0 Rules - the first thing was to copy the contents of context.authorization into the idToken but this doesn’t expose the individual permissions, just the roles. So I can tell if a user is a “Foo Admin” but that doesn’t help with the problem I have, since I’d also need to let the frontend know that “Foo Admin” has the “delete:foo” permission.

The closest thing I managed was a pretty horrific hack to get the permissions included in the idToken via a custom rule by calling the management API in a Rule. The below will cause the permissions for the requested resource to be included in the idToken as a claim “https://example.io/permissions” …

function (user, context, callback) {
  const ns = "https://example.io/";
  const ManagementClient = require('auth0@2.23.0').ManagementClient;
  const management = new ManagementClient({
    token: auth0.accessToken,
    domain: auth0.domain
  });

  const params = { id: user.user_id };
  management.getUserPermissions(params, function (err, permissions) {
    if (err) {
      callback(err);
    } else {
      const requestedResourceIdentifier = context.request.query.audience; /* need to add NULL/undefined check here */
      const permissionsArray = permissions
          .filter(p => p.resource_server_identifier === requestedResourceIdentifier)
          .map(p => p.permission_name);       
      	  
      context.idToken[ns + 'permissions'] = permissionsArray;
      callback(null, user, context);
    }
  });
}

Does anyone know what the correct or idiomatic way to access permissions in an application’s frontend should be? I can’t help feeling that the above code I wrote is really stupid, and that this has to be a common problem that has a solution that does not involve adding custom JS in Auth0 that may be prone to bugs or might require(...) a library version that ends up being deprecated after a while.

1 Like