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