Auth0 Actions are not just a replacement for Hooks; they represent a fundamental shift in how you extend Auth0, moving from discrete, single-purpose functions to a more unified, event-driven pipeline.

Let’s see this in action. Imagine you want to add a custom claim to a user’s ID token upon login, and send a welcome email.

Here’s how that would look in Auth0 Actions:

First, create a new Action. We’ll call it Login-Customizations.

/**
 * @param {Event} event - Details about the context of the event that triggered the Action.
 * @param {PreUserRegistration.Callback} callback - Function to call to continue the execution flow.
 */
exports.onExecutePostLogin = async (event, api) => {
  // Add a custom claim to the ID token
  api.idToken.setCustomClaim('https://your-app.com/roles', ['admin', 'user']);

  // Send a welcome email (requires an external service or another Action)
  // For simplicity, we'll just log here, but in a real scenario,
  // you'd use a service like SendGrid or an HTTP call.
  console.log(`Sending welcome email to user: ${event.user.email}`);

  // You can also add user metadata if needed
  api.user.setAppMetadata({
    welcomeEmailSent: true
  });
};

Now, go to the "Actions" section in your Auth0 dashboard, navigate to "Flows," and select the "Login" flow. You’ll see a visual editor. Drag your Login-Customizations Action into the flow.

The key difference from Hooks is that multiple Actions can be chained together in a single flow. You could add another Action here to, say, enrich user data from an external CRM before the custom claim is set. The event object passed to each Action contains a wealth of information about the user and the authentication process, and the api object provides methods to modify the flow and the tokens.

This event-driven, pipeline-based approach is the core of Actions. Instead of having separate "pre-user-registration" and "post-login" hooks that you configure independently, you define a sequence of Actions that run for specific events (like postLogin, preUserRegistration, credentialsExchange, etc.). Auth0 orchestrates the execution of these Actions based on the event that occurs during the authentication pipeline.

This allows for much more complex and modular logic. You can have an Action that handles custom email verification, another that integrates with a third-party identity provider, and a third that modifies token claims, all orchestrated within a single, manageable flow. The api object is your gateway to interacting with the authentication context, allowing you to read user data, modify tokens, call external services, and even block the authentication process if necessary.

The underlying mechanism is that Auth0 spins up a sandboxed Node.js environment for each Action execution. This environment has access to the event object (containing user information, connection details, request context, etc.) and the api object (providing methods to interact with Auth0 and the authentication process). When an event occurs (like a user logging in), Auth0 invokes the relevant Actions defined in the flow, passing the event and api objects.

A crucial detail often overlooked is the api.access.deny() method. While you might think of Actions primarily for adding functionality, they are equally powerful for enforcing security policies. You can use api.access.deny("User is not authorized for this action") within an Action to halt the authentication process entirely, providing a custom error message. This is critical for implementing fine-grained authorization checks directly within the login flow, rather than relying solely on post-authentication authorization logic.

The next step in mastering Actions involves understanding how to manage secrets and external dependencies, and how to effectively debug your Action code through the logs provided in the Auth0 dashboard.

Want structured learning?

Take the full Auth0 course →