Difference between scopes and permissions in access token

Update 11/May/2020:

For M2M / Client Credentials Grant, the permissions for a M2M client is put in both scope and permissions claim. I’ve tested it and it works as expected, so the approach below should not be necessary anymore.


Original answer:

I might have missed some DMs in between, but even with the RBAC Core (not Authorization extension - doesn’t actually make a difference): I was able to reproduce the behaviour described by the OP.

In M2M settings, the scopes set for a client for accessing an API are just that: scopes, they’re not reflected as permissions in the permissions claim. Not sure why it’s not reflected when the “Enable RBAC” flag is turned on. Something for us to check internally with the product team.

The workaround that I see at the moment is to put the permissions in a custom claim, this way it’s the same for M2M access tokens as well as with other grant types.

You would create both a Hook (for Client Credentials Exchange) as well as a Rule.

The hook looking like this:

access_token['https://namespace/permissions'] = scope;

and the Rule accordingly:

The permissions in the rule would need to be read via node-sdk in the Rule, using the ‘getUserPermissions’ method.

(Note though that this calls the Management API at each authentication, so it counts against the API rate limits.)

function (user, context, callback) {
  var ManagementClient = require('auth0@2.17.0').ManagementClient;
  var management = new ManagementClient({
    token: auth0.accessToken,
    domain: auth0.domain
  });

  management.getUserPermissions({ id: user.user_id }, function (err, permissions) {
    if (err) {} // Handle error.
    var permissionNames = [];
    permissions.forEach(function(obj) { permissionNames.push(obj.permission_name); });    
    context.accessToken['https://namespace/permissions'] = permissionNames;
    callback(null, user, context);
  });
}

In both ways of requesting access tokens (M2M and issued on behalf of user) you will then always have this one custom claim that you can use to validate against in the backend/API. I know it feels really just like a workaround for the moment, but this way you would have a consistent claim to check.

{
  "https://namespace/permissions": [
    "use:privateroute1",
    "use:privateroute2"
  ],
  "sub": "yDqmPNzTrvsqDFpLE6XwrPSzkQqSzBtI@clients",
  [...]
  "scope": "use:privateroute1 use:privateroute2",
  "gty": "client-credentials"
}
3 Likes