Read values from previous action

With rules, we were able to effectively do this:

rule3(rule2(rule1(user, context, callback)));

Meaning subsequent rules could read properties added to the user / access_token / id_token etc. by a previous rule. e.g.

function rule1(user, context, callback) {
  user.a = 'foo';
  user.app_metadata.b = 'bar';
  context.idToken['https://example.com/c'] = 'baz';
  context.accessToken['https://example.com/d'] = 'qux';
  return callback(null, user, context);
}

function rule2(user, context, callback) {
  console.log(user.a); // 'foo'
  console.log(user.app_metadata.b); // 'bar'
  console.log(context.idToken['https://example.com/c']); // 'baz'
  console.log(context.accessToken['https://example.com/d']); // 'qux'
  return callback(null, user, context);
}

Is this possible with actions?

Interestingly enough, I’ve noticed that mutating user.app_metadata in an action actually sets it on the users profile – i.e it makes an API call to update the user DB; this is quite unexpected e.g.

exports.onExecutePostLogin = async (event, api) => {
    event.user.app_metadata.a = 'foo'; // Updates the user profile
    api.user.setAppMetadata('b', 'bar'); // So what's the point of this?
};
1 Like

Thanks for the feedback @riscarrott! As for the question about mutating app_metadata, that is not intended, and is a bug that we’ll fix soon. Sorry about that!

A design goal with Actions is that we’d like to keep the event object immutable and capture all side effects in the api object. So, the fact that app_metadata can be updated like this directly is a miss on our part.

As for communicating across actions, that is not currently possible apart from metadata updates made by previous Actions, although we’ve heard the need for this in a more general sense. We’ll address it in the future, but we’re still working out the best way to expose this in Actions.

Would you mind sharing a little bit more about your use case for sharing data across actions like this? What sorts of things are trying to do that aren’t possible if you can’t see claims, etc. added by previous Actions?

Thanks again for your question and sharing your experience with us!

2 Likes

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(`https://api.example.com/seller/${user.app_metadata.sellerId}`);
     user.seller = seller;
     context.idToken['https://example.com/seller'] = 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(
      'sha256',
      configuration.intercom_verification_secret
    );
    hmac.update(user.seller.email);
    context.idToken['https://example.com/seller_email_hash'] = hmac.digest(
        'hex'
    );
    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['https://example.com/roles'] = 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

Hi @chris.geihsler ,

do you have any suggestion on how to share information between actions execution?

We have a similar case to the one described by @riscarrott . As he has pointed out, we can’t merge all the logic in a single action otherwise it would become too big as a single action.

In addition to that, in our use case, in order to migrate from rules to action, we need to access information related to the rules execution. Is that possible in some way?

Thanks in advance,
Simone

Hey there!

As this topic is related to Actions and Rules & Hooks are being deprecated soon in favor of Actions, I’m excited to let you know about our next Ask me Anything session in the Forum on Thursday, January 18 with the Rules, Hooks and Actions team on Rules & Hooks and why Actions matter! Submit your questions in the thread above and our esteemed product experts will provide written answers on January 18. Find out more about Rules & Hooks and why Actions matter! Can’t wait to see you there!

Learn more here!