Authorization Extention Migration from Rules to Actions

In several of our applications we make extensive use of the Authorization Extension, which relies on Rules. Rules need to be migrated to Actions by November 18th. Is there any official guidance on how to migrate this extension to use Actions? I’m unable to find any in my searches. In fact, I have found that this question has been posed multiple times over the course of several(!) years. Each time the question has either been unanswered or we are simply told to wait for forthcoming guidance:

  1. Auth0 extension rules vs hooks
  2. Authorization Extention Rule migration to Action
  3. Authorization Extension migration from rules to actions
  4. Authorization Extension Deprecation

At the time of this writing we are a little more than a month out from November 18th deadline to migrate from Rules to Actions. We’re running out of time. We either need to be provided this information soon, or the migration deadline needs to be extended.

Thank you.

1 Like

Hi Joe,
I would suggest that you have a look at the blog from Auth0 where they talk about on how to migrate from Rules to Actions

I think I got it working. I had to manually convert the rule that was originally autogenerated by the Authorization Extension. If anyone else needs to do this too, I was able to get the following code to work.

var _ = require('lodash');
var request = require('request');
exports.onExecutePostLogin = async (event, api) => {
	var EXTENSION_URL = <Insert your extension URL here>;

	var audience = '';
	audience = audience || (event.request && event.request.query && event.request.query.audience);
	if (audience === 'urn:auth0-authz-api') { 
		return api.access.deny('no_end_users')
	}

	audience = audience || (event.request && event.request.body && event.request.body.audience);
	if (audience === 'urn:auth0-authz-api') {
		return api.access.deny('no_end_users')
	}

	getPolicy(event.user, event.client.client_id, event.connection, function(err, res, data) {
		if (err) {
		  console.log('Error from Authorization Extension:', err);
		  return api.access.deny('Authorization Extension: ' + err.message);
		}

		if (res.statusCode !== 200) {
		  console.log('Error from Authorization Extension:', res.body || res.statusCode);
		  return api.access.deny('Authorization Extension: ' + ((res.body && (res.body.message || res.body) || res.statusCode)));
		}

		// Create the authorization that will be added to the user object.
		var userAuthorization = {
			groups: mergeRecords(event.user.groups, data.groups),
			roles: mergeRecords(event.user.roles, data.roles),
			permissions: mergeRecords(event.user.permissions, data.permissions)
		}
		saveToMetadata(userAuthorization, data.groups, data.roles, data.permissions, function(err) {
		  return;
		});
	})

  // Convert groups to array
  function parseGroups(data) {
    if (typeof data === 'string') {
      // split groups represented as string by spaces and/or comma
      return data.replace(/,/g, ' ').replace(/\s+/g, ' ').split(' ');
    }
    return data;
  }

  // Get the policy for the user.
  function getPolicy(user, clientId, connection, cb) {
    request.post({
      url: EXTENSION_URL + "/api/users/" + user.user_id + "/policy/" + clientId,
      headers: {
        "x-api-key": event.secrets.AUTHZ_EXT_API_KEY
      },
      json: {
        connectionName: connection || user.identities[0].connection,
        groups: parseGroups(user.groups)
      },
      timeout: 5000
    }, cb);
  }

  // Store authorization data in the user profile so we can query it later.
  function saveToMetadata(userAuthorization, groups, roles, permissions, cb) {
    var updatedAuthorization = {
      groups: mergeRecords(userAuthorization.groups, groups),
      roles: mergeRecords(userAuthorization.roles, roles),
      permissions: mergeRecords(userAuthorization.permissions, permissions)
	};

    api.user.setAppMetadata("authorization", updatedAuthorization)
    .then(function() {
      cb();
    })
    .catch(function(err){
      cb(err);
    });
  }

  // Merge the IdP records with the records of the extension.
  function mergeRecords(idpRecords, extensionRecords) {
    idpRecords = idpRecords || [ ];
    extensionRecords = extensionRecords || [ ];

    if (!Array.isArray(idpRecords)) {
      idpRecords = idpRecords.replace(/,/g, ' ').replace(/\s+/g, ' ').split(' ');
    }

    return _.uniq(_.union(idpRecords, extensionRecords));
  }	
};

You will also need to do the following:

  1. Insert the appropriate URL for your EXTENSION_URL, which you can find in the original rule.
  2. In Action Dependencies: Add both ‘lodash’ and ‘request’
  3. In Action Secrets: Add your AUTHZ_EXT_API_KEY

Note: My code may have bugs. Please point out any issues you find. Hopefully we can get the maintainers of the Authorization Extension to document or automate this process.

1 Like

Where can I find my AUTHZ_EXT_API_KEY?

This is a crazy problem that Auth0 has not provided any guidance on

I was able to find it by adding a log statement + viewing logs in realtime web logs extension.

Just added the following at top of existing hook:
console.log(configuration.AUTHZ_EXT_API_KEY);

In case helpful for others, here is how I re-wrote my Auth0 Authorization Extension Rule => Action. I didn’t need some of the generic merging + IDP handling, I just needed my authorization groups, roles, and permissions attached to my user in the JWT (I also didn’t want to update user metadata because that would require updating my front-end apps).

This requires the following:

  • Set AUTHORIZATION_EXTENSION_API_KEY in secrets
  • Add axios to dependencies
exports.onExecutePostLogin = async (event, api) => {
  const userID = event.user.user_id;
  const email = event.user.email;
  const connectionName = event.connection.name;
  const clientID = event.client.client_id;
  const authorizationExtensionApiKey = event.secrets.AUTHORIZATION_EXTENSION_API_KEY;

  console.log(`Getting authorization data for ${email} (${userID})...`)
  const authorizationData = await getAuthorizationData(userID, connectionName, clientID, authorizationExtensionApiKey);
  if (authorizationData === null) {
    return;
  }
  console.log(`...obtained authorization data: ${JSON.stringify(authorizationData)}`);

  // Setting groups, roles, and permissions from auth0 authorization extension in user token
  api.idToken.setCustomClaim("groups", authorizationData.groups);
  api.idToken.setCustomClaim("roles", authorizationData.roles);
  api.idToken.setCustomClaim("permissions", authorizationData.permissions);
  console.log("Set groups, roles, and permissions in id token");
};

async function getAuthorizationData(userID, connectionName, clientID, authorizationExtensionApiKey) {
  try {
    const url = `${EXTENSION_URL}/api/users/${userID}/policy/${clientID}`;
    const postData = { connectionName }
    const headers = {
      "x-api-key": authorizationExtensionApiKey,
    }
    const response = await axios.post(url, postData, { headers });
    return response.data;
  } catch (error) {
    console.error(`Error getting authorization data for ${userID}`, error);
    return null;
  }
}
1 Like

I’m trying to rewrite the Authorisation Extension from rules to actions as well. The think that I cannot understand how to get AUTHZ_EXT_API_KEY aka x-api-key header. The rules and hooks are not editable anymore so it’s not possible to hijack it from logs. Thanks

Thanks for your answer. It helps me.

Following up on my post from October - there have been subtle bugs ever since we made this transition. The most egregious is that we can’t update the permissions of a new user after they are registered with the Authorization Extension. No matter what we do the user’s app_metadata does not get updated by the Action. I suspect, but cannot conclusively confirm, that the HTTP POST to the Authorization Extension fails and Auth0 falls back to a cache that you can see under User Management → Users. In order to resolve this issue we have had to revert from the Action back to the Rule. This is obviously a tenuous situation since we are now past the end of life for Rules and have no confirmation on how much longer they will be available. I am going to raise a support ticket with Auth0 about this.

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