Actions Programming Model Changes

As part of our long-term vision for extensibility of Auth0 through custom code, we have refined and simplified the programming models that we introduced during the beta period of Actions. Authoring Actions will now be done in a more consistent and predictable way across Triggers. Event data has been aligned more closely with the Auth0 Management API and other facets of the Auth0 platform. Changing the behavior of the transaction via custom code now always happens by calling methods of a new api argument.

Migrating an Action created during the period prior to General Availability (GA) should typically involve the following steps:

  1. Adjust references to renamed and relocated event properties as outlined in the Breaking changes section.

  2. Instead of composing and returning an object describing the desired side-effects, update custom code to call the relevant api method as outlined in the Performing side effects section.

  3. For Actions that need to handle redirect callbacks, use the newly-exposed dedicated function. If you used code that relied on event.protocol === 'redirect-callback', review the Redirect with Actions page.

Breaking changes

Query and body parameters

Direct access to the query and body parameters is available using the event.request.query and event.request.body objects. These are exposed regardless of whether the authorization was initiated via a GET or POST request. Many protocol-specific query or body parameters sent as part of an authorization request are now also available as first-class values on the event.transaction object. We recommend that you use event.transaction rather than event.request.query and event.request.body unless your use case is not supported. A complete mapping of these changes is below:

Pre-GA Property GA Property
event.actor.ip event.request.ip
event.actor.hostname event.request.hostname
event.actor.geoIp event.request.geoip
event.actor.language event.request.language
event.actor.method event.request.method
event.actor.userAgent event.request.user_agent
event.actor.body event.request.body
event.actor.query event.request.query
event.actor.query.audience event.resource_server.identifier
event.actor.query.scope event.transaction.requested_scopes
event.actor.query.acr_values event.transaction.acr_values
event.actor.query.ui_locales event.transaction.ui_locales
event.protocol event.transaction.protocol
context.secrets event.secrets

User Profile properties

In general, the event.user object has had its properties changed from camel case to snake case in order to match the Auth0 User Profile structure. For example, event.user.appMetadata has been changed to event.user.app_metadata.

Performing side effects

In the pre-GA version of the post-login trigger, side effects were performed by returning an object from an Action. In Actions GA, an api object is provided to encapsulate these changes and provide better in-editor type hints and inline documentation.

Update user user_metadata

Pre-GA Trigger:

async function myFunction(event, context) {
  return {
    user: {
      userMetadata: {
        myParam: "foo"
      }
    }
  };
}

GA Trigger:

async function onExecutePostLogin(event, api) {
  api.user.setUserMetadata('myParam', 'foo');
}

You should not use this method in callbacks because invoking this method won't update metadata immediately. Instead, you can call this method several times throughout multiple Actions in the same flow (metadata set in one Action is applied to the transient object and is therefore available in subsequent Actions), and the engine will aggregate the changes and update the metadata all at once before the flow is completed.

Update user app_metadata

Pre-GA Trigger:

async function myFunction(event, context) {
  return {
    user: {
      appMetadata: {
        myParam: "foo"
      }
    }
  };
}

GA Trigger:

async function onExecutePostLogin(event, api) {
  api.user.setAppMetadata('myParam', 'foo');
}

You should not use this method in callbacks because invoking this method won't update metadata immediately. Instead, you can call this method several times throughout multiple Actions in the same flow (metadata set in one Action is applied to the transient object and is therefore available in subsequent Actions), and the engine will aggregate the changes and update the metadata all at once before the flow is completed.

Deny a login

Pre-GA Trigger:

async function myFunction(event, context) {
  throw new Error("Access denied.");
}

GA Trigger:

async function onExecutePostLogin(event, api) {
  api.access.deny("Access denied.");
}

Throwing an error will also deny a login, but calling api.access.deny is the preferred approach.

Add Custom Claims to the Access Token

Pre-GA Trigger:

async function myFunc(event, context) {
  return {
    accessToken: {
      customClaims: {
        'https://example.com/custom/claim': 'Custom claim value',
      }
    }
  };
}

GA Trigger:

async function myFunc(event, api) {
  api.accessToken.setCustomClaim('https://example.com/custom/claim', 'Custom claim value');
}

Add Custom Claims to the ID Token

Pre-GA Trigger:

async function myFunc(event, context) {
  return {
    idToken: {
      customClaims: {
        'https://example.com/custom/claim': 'Custom claim value',
      }
    }
  };
}

GA Trigger:

async function myFunc(event, api) {
  api.idToken.setCustomClaim('https://example.com/custom/claim', 'Custom claim value');
}

Dynamically enable multi-factor authentication

Pre-GA Trigger:

async function myFunction(event, context) {
  return {
    command: {
      type: "multifactor",
      provider: "any"
    }
  };
}

GA Trigger:

async function onExecutePostLogin(event, api) {
  api.multifactor.enable("duo");
}

Redirect the user

Pre-GA Trigger:

async function myFunction(event, context) {
  return {
    command: {
      type: "redirect",
      url: "https://my-app.example.com"
    }
  };
}

GA Trigger:

async function onExecutePostLogin(event, api) {
  api.redirect.sendUserTo("https://my-app.example.com");
}

To ensure parameters are being sent securely and to avoid replay attacks, passing data via redirects has changed significantly in Actions GA. For more information, see Redirect with Actions.

Manipulate scopes

Although we experimented with providing direct manipulation of ID and Access Token scopes during the Actions Beta, we do not support this functionality in Actions GA.