Rules not executing in order

Hi Auth0 Community,

I suddenly have a problem where rules script not executing is not executing in correct order(they run asynchronously). These were fine and tested before, until I got some complaint from the users a month ago. Upon checking it was the rules that is not executing in proper order.

The 1st rules is meant to call auth0-authorization api to give roles to a user
The 2nd rules is meant to write metadata based on roles given on the 1st rules

On a side note, this was working fine for a year or so, then I start getting complaint about 2 months ago.

Hi @andre.hermanto93,

Can you do some testing / logging to demonstrate the behaviour? You can use the Realtime Webtask Logs extension to help. Anything you console.log() in a rule will show up in the realtime webtask logs interface.

Hi @markd

So above are my 2 rules that doesn’t execute in order.

Assign role to selected users script is:

function (user, context, callback) {
  var acuteCare = '9da80d63-c29e-48bd-8cbb-a725f662df05';
  
  var emailsListAcuteCare = [
                             'test@test.com'
                          	];
  
  var accessGiven = [];

  if(emailsListAcuteCare.includes(user.email.toLowerCase())){
    accessGiven.push(acuteCare);
  }

  if(accessGiven.length > 0){
    var request = require("request");
    var options = { method: 'POST',
    url: 'https://URL/oauth/token',
    headers: { 'content-type': 'application/json' },
    body: '{"client_id":"ID","client_secret":"SECRET","audience":"urn:auth0-authz-api","grant_type":"client_credentials"}' };

    request(options, function (error, response, body) {
      if (error) throw new Error(error);
      var opt = { method: 'PATCH',
        url: `https://URL.au8.webtask.io/adf6e2f2b84784b57522e3b19dfc9201/api/users/${user.user_id}/roles`,
        headers: { 'content-type': 'application/json', 
              'authorization': `Bearer ${JSON.parse(body).access_token}` },
        body: JSON.stringify(accessGiven)
    };

    request(opt, function (er, response, body2) {
      console.log("This should come 1st");
      if (er) throw new Error(er);
     });
   });
  }
  
  callback(null, user, context);
}

Then my auth0-authorization-extension script is:

function (user, context, callback) {
  var _ = require('lodash');
  var EXTENSION_URL = "";

  var audience = '';
  audience = audience || (context.request && context.request.query && context.request.query.audience);
  if (audience === 'urn:auth0-authz-api') {
    return callback(new UnauthorizedError('no_end_users'));
  }

  audience = audience || (context.request && context.request.body && context.request.body.audience);
  if (audience === 'urn:auth0-authz-api') {
    return callback(new UnauthorizedError('no_end_users'));
  }

  getPolicy(user, context, function(err, res, data) {
    if (err) {
      console.log('Error from Authorization Extension:', err);
      return callback(new UnauthorizedError('Authorization Extension: ' + err.message));
    }

    if (res.statusCode !== 200) {
      console.log('Error from Authorization Extension:', res.body || res.statusCode);
      return callback(
        new UnauthorizedError('Authorization Extension: ' + ((res.body && (res.body.message || res.body) || res.statusCode)))
      );
    }

    // Update the user object.
    user.permissions = data.permissions;

    // Store this in the user profile (app_metadata).
    saveToMetadata(user, data.groups, data.roles, data.permissions, function(err) {
      console.log("This should come 2nd");
      return callback(err, user, context);
    });
  });

  // Get the policy for the user.
  function getPolicy(user, context, cb) {
    request.post({
      url: EXTENSION_URL + "/api/users/" + user.user_id + "/policy/" + context.clientID,
      headers: {
        "x-api-key": "API KEY"
      },
      json: {
        connectionName: context.connection || user.identities[0].connection,
        groups: user.groups
      },
      timeout: 5000
    }, cb);
  }

  // Store authorization data in the user profile so we can query it later.
  function saveToMetadata(user, groups, roles, permissions, cb) {
    user.app_metadata = user.app_metadata || {};
    user.app_metadata.authorization = {
      permissions: permissions
    };

    auth0.users.updateAppMetadata(user.user_id, user.app_metadata)
    .then(function() {
      cb();
    })
    .catch(function(err){
      cb(err);
    });
  }
}

Then upon execution the 2nd always executed first. Here’s the screenshot of the logs

Hi @markd,

Is there any update on this?

Cheers

The callback of the first rule

callback(null, user, context);

is outside of the request {...} , so that calling is executed without waiting for the request() to finish, as the request runs async.
It should be inside that request, like this:

request(opt, function (er, response, body2) {
  console.log("This should come 1st");
  if (er) {
    throw new Error(er) 
  } else {
    callback(null, user, context);
  }
 });

But also note, since you actually have 2 request in your first rule: these requests also don’t wait for each other, so you never know which of the two requests will finish first. Might make sense to call this in a nested as well.

1 Like