Migrate from Hooks to Actions

Migrate from Hooks to Actions

When converting existing Hooks to Actions, you should associate the new Action with the Trigger that corresponds to the type of Hook. If you follow the steps below and use the mappings we identify within them, the functionality should be identical.

Plan your migration

Deployed Actions run after active Hooks, so you can either convert Hooks one at a time in the Dashboard or all at once using the Management API.

You will need to convert code, and then activate the Action and deactivate the Hook. Activating the Action and deactivating the Hook can be done quickly in succession, but depending on the order, there might be a short period of time where either both or neither are running.

Because of this, we recommend migrating your pipeline step by step: convert pieces of your Hooks code to Action code, test in a staging environment, then go live with one piece at a time. Because active Hooks run before deployed Actions, you can keep some logic in Hooks as you build and test other logic in Actions.

Tips when planning your migration

  • Use flags to avoid duplicating expensive or one-time operations.

  • Make sure to run changes at a time when impact and traffic will be lowest.

  • Consider using the Auth0 Deploy CLI to script, test, and quickly implement the migration all at once or iteratively.

Understand limitations

While Actions can handle the vast majority of things that Hooks can, you should be aware of a few limitations before you start your migration .(Remember: you can have both Hooks and Actions running as you migrate.)

For the full list of limitations, see Actions Limitations.

Convert code

To convert a Hook to an Action, you must replace Hook-specific code with Actions code. This section covers the tasks you will need to perform to turn a functioning Hook into its equivalent Action.

Tips when converting code

  • In general, look for the read-only properties of objects passed into the Hooks function on the Actions event object.

  • Use the Actions Code Editor in the Auth0 Dashboard to write your code; it will help by highlighting errors and supplying auto-complete suggestions.

  • Before you go live, thoroughly test your new Actions in a staging or test environment.

Copy Hook code to a new Action

  1. Log in to your production tenant, and copy the code from the Hook you want to convert.

  2. Switch to a non-production tenant, and navigate to Auth0 Dashboard > Actions > Library.

  3. Select Build Custom, then:

    • Enter a Name for your Action that matches the name of the Hook you're converting.

    • Locate Trigger, and select the appropriate trigger:

      Type of Hook Actions Trigger
      Client Credentials Exchange M2M/Client-Credentials
      Pre-User-Registration Pre User Registration
      Post-User-Registration Post User Registration
      Post-Change-Password Post Change Password
      Send Phone Message Send Phone Message

    • Locate Runtime, and select Node 16.

    • Select Create.

  4. In the code block of the Actions Code Editor, paste the Hook code you want to convert below the exported function.

  5. Make the changes detailed in the rest of this article as you move the code into the function. You should also read about the event object associated with the new Actions Trigger; you'll see links to the relevant documentation when you get to the Change how data is accessed section later in this guide.

Change the function declaration

Hooks functions are exported using a default export, while Actions functions use named exports. Depending on the type of Hook you are converting, the named export will change. Mappings include:

Type of Hook Named Export
Client Credentials Exchange onExecuteCredentialsExchange
Pre-User Registration onExecutePreUserRegistration
Post-User Registration onExecutePostUserRegistration
Post-Change Password onExecutePostChangePassword
Send Phone Message onExecuteSendPhoneMessage

Before

module.exports = async function myHooksFunction(){}

Was this helpful?

/

After

// Client Credentials Exchange
exports.onExecuteCredentialsExchange = async (event, api) => {}

// Pre-User Registration
exports.onExecutePreUserRegistration = async (event, api) => {}

// Post-User Registration
exports.onExecutePostUserRegistration = async (event) => {}

// Post-Change Password
exports.onExecutePostChangePassword = async (event) => {}

// Send Phone Message
exports.onExecuteSendPhoneMessage = async (event) => {}

Was this helpful?

/

Convert dependencies

Hooks and Actions handle dependencies in a similar way. With both, dependencies are added separately via UI or Management API and included in the code. Also with both, you can require any package that is available in the npm Registry.

  1. Search for require statements inside your Hook code.

  2. Remove version numbers, but make a note of them.

  3. Add the dependency by following the steps in the "Add a Dependency" section of Write Your First Action (if the dependency is not a core NodeJS module; if the dependency is a core NodeJS module, you do not need to include it).

  4. Move the found require statements outside of the function declaration.

Convert secrets

Hooks and Actions handle secrets in a similar way. With both, Secrets are added per Hook/Action via UI or Management API and included in the code.

To convert secrets from Hooks to Actions:

  1. Save the values needed for the specific Action you are working on.

  2. Add a Secret for each value you need to access from inside the Action. To learn how, read the Add a Secret section in Write Your First Action.

  3. Convert your code:

Before

async function (user, context, cb) {
    const { SECRET_NAME } = context.webtask.secrets;

    // ... additional code
}

Was this helpful?

/

After

async (event, api) => {
    const { SECRET_NAME } = event.secrets;

	// ... additional code
};

Was this helpful?

/

As with Hooks, Auth0 encrypts all secret values at rest.

Change how data is accessed

With Hooks, data about the user, client, request, and other contextual data are stored in multiple arguments passed into the Hook function. In Actions, this data has been reshaped and moved to the event object. Many of the properties moved over as-is, but some have been combined to increase clarity.

Depending on the type of Hook you are converting, the event object will change:

Before

async function (user, context, cb) {
	const clientId = context.clientID;
	const tenant = context.connection.tenant

	// ... additional code
}

Was this helpful?

/

After

async (event, api) => {
	const clientId = event.client.client_id;
	const tenant = event.tenant.id;

	// ... additional code
};

Was this helpful?

/

Convert callbacks

When a Hook is finished processing, it must call the callback() function to complete its execution. Conversely, Actions do not use a callback mechanism; therefore, you will need to remove all instances of callback() from your Actions function.

If you were previously using the callback() function in a Client Credentials Exchange or Pre User Registration Hook to fail the request or update a user, you will still be able to do this in Actions through a new api interface.

Client Credentials Exchange

If you were adding extra claims to the access token in a Client Credentials Exchange Hook:

// Client Credentials Exchange Hook
module.exports = function(client, scope, audience, context, cb) {
  var access_token = {};
  access_token.scope = scope;

  access_token['https://example.com/claim'] = 'bar';
  cb(null, access_token);
};

Was this helpful?

/

You can now use the Actions Client Credentials Exchange API Object:

// Client Credentials Exchange Action
exports.onExecuteCredentialsExchange = async (event, api) => {
  api.accessToken.setCustomClaim("https://example.com/claim", 'bar');  
};

Was this helpful?

/

Pre User Registration

If you were adding extra claims to the access token in a Pre User Registration Hook:

// Pre User Registration Hook
module.exports = function (user, context, cb) {
	if (user.app_metadata.condition === "success") {
      var response = {};
      response.user = { user_metadata: { favorite_color: "purple" } };
      // This Hook succeeded, proceed with the next Hook.
	  return callback(null, response);
	}

	if (user.app_metadata.condition === "failure") {
		// This Hook failed, stop the login with an error response.
		return callback(new Error("Failure message"));
	}

	// ... additional code
};

Was this helpful?

/

You can now use the Pre User Registration API Object:

// Pre User Registration Action
exports.onExecutePreUserRegistration = async (event, api) => {
	if (event.user.app_metadata.condition === "success") {
		// This Action succeeded, proceed with next Action.
		api.user.setUserMetadata("favorite_color", "purple");
		return;
	}

	if (event.user.app_metadata.condition === "failure") {
		// This Action failed, stop the call with an error response.
		return api.access.deny("Failure message");
	}

	// ... additional code
};

Was this helpful?

/

Complete the migration

Once your new Actions code has been written and tested, you must activate the Action and deactivate the Hook. These two tasks can be done quickly in succession, but depending on the order, there might be a short period of time where either both or neither are running. Because active Hooks run before deployed Actions, you can keep some logic in Rules as you build and test other logic in Actions.