Api/v2/users/{user}/permissions call not completing only sometimes

UPDATE
I have identified the issue is in the second Action, the UserId is not the Auth0 user id that the account is now linked to. Upon logging in the second time around, the user id is the Auth0 user account. Now I need to find a way to handle that. Was hoping to redirect the user back through the /authenticate process and get the updated userid but haven’t been able to successfully manage.

I have 2 separate Actions that are executed upon a user logging in. The first Action is LinkAccount. This action determines if the user is an Enterprise account logging in for the first time and links that account to the Auth0 account that was previously created in our application. The second Action gets the user’s permissions and stuffs them into a “Role” claim in our token.

Separately , both of these Actions work perfectly. The first action links a user’s account no problem. The second Action populates the claim without issue as well. However, when an Enterprise user logs in for the first time only the second Action which is calling the user permission api doesn’t ever complete. The API call never returns any response at all. I thought it might have been a timing issue between the account getting linked and user logging in but even with adding a delay after the account gets linked, the API call still doesn’t return.

If I return to the log in page and re-log in with my Enterprise account, the second time around it works perfectly.

LinkAccount Action (works perfectly):

LinkAccount Action (works perfectly):
exports.onExecutePostLogin = async (event, api) => {

console.log("Action 1 >> LinkAccount", event.user);

// require axios
const axios = require("axios").default;
    // reasons to exit, user has logged in before or they are an Auth0 user signing in
    if(event.stats.logins_count !== 1) return;
    if(event.connection.strategy == "auth0") return;

  try{    
    // get provider from authenticating user id
    const provider = event.user.user_id.split("|")[0];
    // get user who is logging in
    const enterpriseUser = event.user.user_id;
    // get management Token
    const managementToken = (await axios.post(`${event.secrets.BASE_URL}/oauth/token`,
            { client_id: event.secrets.M2M_CLIENT_ID,
              client_secret: event.secrets.M2M_CLIENT_SECRET,
              audience: event.secrets.MANAGEMENT_API_AUDIENCE,
              grant_type: 'client_credentials'},
              { headers: {'Content-Type': 'application/json'} })).data.access_token;

        // attempt to find existing Username-Password-Authentication user
        var lookupUserResult = await axios.request({ method: 'GET', url: `${ event.secrets.BASE_URL }/api/v2/users`,
            params: {q: `email:"${event.user.upn ? event.user.upn : event.user.email}" AND identities.connection:"Username-Password-Authentication"`,
            search_engine: 'v3'}, headers: {authorization: `Bearer ${managementToken}`}});


        // determine if user was found
        if(lookupUserResult && lookupUserResult.data && lookupUserResult.data.length > 0){
            // found user, begin linking accounts
            // get Auth0 user account user_id
            const databaseUser = lookupUserResult.data[0].user_id;              
            // ensure the account is not the same
            if(databaseUser != enterpriseUser){
              // attempt to link accounts            
              var syncResult = await axios.request({
                  method: 'POST',
                  maxBodyLength: Infinity,
                  url: `${ event.secrets.BASE_URL }/api/v2/users/${ encodeURIComponent(databaseUser) }/identities`,
                  headers: {
                    'Accept': 'application/json',
                    'Authorization': `Bearer ${managementToken}`
                  },
                  data: {
                    "provider": provider,
                    "user_id": event.user.user_id
                    }
                });

              console.log("Action 1 >> LinkAccount > COMPLETED SUCCESSFULLY");
            }
      } else {        
        // delete enterprise user because they don't exist in Auth0
        var deleteResult = await axios.request({ method: 'DELETE', url: `${ event.secrets.BASE_URL }/api/v2/users/${ encodeURIComponent(enterpriseUser) }`,
          headers: {authorization: `Bearer ${managementToken}`}});
        // deny access to the application
        return api.access.deny("This email address does not have an account. Please contact your manager for access.");
      }
  }catch(ex){
    console.log("ERROR:", ex);
  }
};

GetRoles Action (Never returns the Permission Request):

exports.onExecutePostLogin = async (event, api) => {
const axios = require('axios');
let permissionRoles = [];

console.log("Action 2 >> GetRoles", event.user);

// store strategy in claim
api.accessToken.setCustomClaim("strategy", event.connection.strategy);

  // check Client ID
  if (event.client.client_id === event.secrets.CLIENT_ID
    || event.client.client_id === event.secrets.INTEGRATION_CLIENT_ID) {
   
    // default options
    const options = { headers: { 'Content-Type': 'application/json' }};
    const managementTokenData = {
        client_id: event.secrets.M2M_CLIENT_ID,
        client_secret: event.secrets.M2M_CLIENT_SECRET,
        audience: event.secrets.MANAGEMENT_API_AUDIENCE,
        grant_type: 'client_credentials'
      };

    const managementTokenResponse = await axios.post(`${event.secrets.BASE_URL}/oauth/token`, managementTokenData, options);
    const accessToken = managementTokenResponse.data.access_token;
    const encodedUserId = encodeURIComponent(event.user.user_id);      

  console.log("encodedUserId", encodedUserId);

    const permissionOptions = {
        method: 'GET',
        url: `${event.secrets.BASE_URL}/api/v2/users/${encodedUserId}/permissions`,
        headers: {authorization: `Bearer ${accessToken}`}
    };

console.log("Action 2 >> GetRoles > Begin Permission Request", permissionOptions.url);

        var permissionResponse = await axios.request(permissionOptions);

console.log("Action 2 >> GetRoles > Permission Request Complete", permissionResponse.data);                             /* THIS LINE DOESN'T EXECUTE UPON FIRST LOG IN*/

        permissionResponse.data.forEach((p)=>{
          if(event.resource_server?.identifier == p.resource_server_identifier){
            permissionRoles.push(p.permission_name);
          }        
        });

      return api.accessToken.setCustomClaim('role', permissionRoles);
   }
}

I resolved this issue. Once I got to the bottom of the actual problem, I was able to find the resolution to switch the primary user calling the api.authentication.setPrimaryUser to the auth0 user rather than the Enterprise user. The second action then used the correct userid.

1 Like

Hi @jharris,

I’m glad you could resolve your issue, and thank you for sharing your solution with the rest of the Community! :person_bowing:

If anything else comes up, please feel free to reach out.

Kind regards,
Rueben