Read values from previous action

Hi @chris.geihsler

One scenario we have is enriching the user with data from our database and referencing it in subsequent rules, e.g.

// Enrich the idToken with seller information
const setSeller = async (user, context, callback) => {
     if (!user.app_metadata.sellerId) {
        return callback(null, user, context);
     const seller = await fetchJson(`${user.app_metadata.sellerId}`);
     user.seller = seller;
     context.idToken[''] = seller;

// Hash the seller email to allow us to verify the seller's identity in intercom
const hashSellerEmail = (user, context, callback) => {
    if (!user.seller) {
       return callback(null, user, context);
    const hmac = crypto.createHmac(
    context.idToken[''] = hmac.digest(
    return callback(null, user, context);

And another scenario we have is providing fallback roles:

const setRoles = (user, context, callback) => {
     if (!user.app_metadata.roles) {
         user.app_metadata.roles = ['USER'];
     context.accessToken[''] = roles;
     return callback(null, user, context);

const denyRoles = (user, context, callback) => {
    const allowedRoles = context.clientMetadata.allowed_roles.split(',');
    // It's important, even though the default 'USER' role from `setRoles` isn't
    // persisted, it can be read in this rule here.
    if (!allowedRoles.some((allowedRole) => user.app_metadata.roles.includes(allowedRole))) {
        throw new Error('Unauthorized');
    callback(null, user, context);

Granted, in both scenarios we could get away with persisting the data to the user profile’s app_metadata in actions however in both scenarios it’d a) be a redundant API write and b) be confusing for Auth0 admin users as they may attempt to edit it and later find it’s been overridden again by a rule.

To be fair, maybe the way I’m slicing rules is too granular? I’ve always been unsure of the purpose of splitting out rules (and now actions) into separate functions…

I suppose it makes a lot of sense if you have non-critical rules which merely log to slack etc. which you may want to toggle on / off independently. However, for the core functionality I had considered just doing:

async function rule(__user, __context) {
  const composeRules = rules => (user, context) => {
    return rules.reduce(async (prevRule, rule) => {
      const [user, context] = await prevRule;
      return rule(user, context);
    }, Promise.resolve(user, context));

  // Rule 1
  async function setSeller(user, context) {
    return [user, context];

  // Rule 2
  async function denyRoles(user, context) {
    throw new Error('Unauthorised');
    return [user, context];

  const run = composeRules([setSeller, denyRoles]);

  try {
    const [user, context] = await run(__user, __context);
    return callback(null, user, context);
  } catch (ex) {
    return callback(ex);

Although I know there are limits to the rule / action size so would be gutted if we had to start writing our rule as if it was a tweet :sweat_smile:

1 Like