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 . 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

We recommend copying your Hook code into a new Action and using the Actions Code Editor in the Auth0 Dashboard; it will help you identify outstanding issues with your code.
  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 HookActions Trigger
      Client Credentials ExchangeM2M/Client-Credentials
      Pre-User-RegistrationPre User Registration
      Post-User-RegistrationPost User Registration
      Post-Change-PasswordPost Change Password
      Send Phone MessageSend Phone Message
    • Locate Runtime, and select Node 18.
    • 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 HookNamed Export
Client Credentials ExchangeonExecuteCredentialsExchange
Pre-User RegistrationonExecutePreUserRegistration
Post-User RegistrationonExecutePostUserRegistration
Post-Change PasswordonExecutePostChangePassword
Send Phone MessageonExecuteSendPhoneMessage
Before
module.exports = async function myHooksFunction(){}
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) => {}

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.
If your npm modules are not on the latest version, this is a great time to get up to date!
  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
}
After
async (event, api) => {
    const { SECRET_NAME } = event.secrets;

	// ... additional code
};
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
}
After
async (event, api) => {
	const clientId = event.client.client_id;
	const tenant = event.tenant.id;

	// ... additional code
};
Unlike the Hooks context object, data stored on or modified in properties of the event object does not persist in subsequent Actions. If your Hook is setting data on these properties to trigger core functionality, you will need to use the api interface available in the Machine to Machine and Pre User Registration Actions Flows to persist data across Actions.

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);
};
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');  
};

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
};
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
};

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.