Hook at post-registration to assign a role

Hi,

I’ve defined a few roles in my API and when a user signs up, I’d like to assign him a default role.

I’ve seen a few examples in the community forums where this was done in a rule but I’d like to do this with the post-registration hook as I find it is cleaner.

I was wondering if someone had a code snippet to do this to save me some time at trying/experimenting until I find the right way…

There seems to be two ways of doing it:

  1. Using ManagementClient and its assignRolesToUser() method
    In this post, somebody explains how to do it in a rule with the ManagementClient class.

  2. Do a POST on the Management API of Auth0
    In this documentation page, it tells me to do a POST call on Auth0 API.

Which way should I go? If I do a POST call, where do I find the value of MGMT_API_ACCESS_TOKEN and how do I set the scopes read:roles and update:users. In the code snippet for the POST call to Auth0 API, it uses the Axios library to do the POST. Is this library available in hooks and if not, wich library is supported in hooks to do POST calls?

Thank you for your time,

Louis-Philipe

Hi @lpcarignan,

(You are probably aware of this, but to clarify for others) The Post-Registration Hook will only run after creating a user in a database connection. In other words, if a user signs up via a social or enterprise connection, the script will not run.

Another thing to consider is that the Post-Registration extensibility point is non-blocking (asynchronous), so if you have anything else in the auth pipeline that depends on the roles being assigned, you might run into a race condition (not very likely, but possible). Rules generally run in the order in which you configure them, so there is a bit more control.

Also, in Rules you will have the auth0 object available which allows you to interact easily with the Management API:

  var ManagementClient = require('auth0@2.9.1').ManagementClient;
  var management = new ManagementClient({
    token: auth0.accessToken,  // <-- Access Token already available
    domain: auth0.domain // <-- Domain
  });

You can do this in a Post-Registration Hook. Still, unless there is a strong reason to assign roles in Hooks (i.e., you are manually creating users to a database and need the users to have roles before they actually log in), it is usually more convenient and covers more user sign-up scenarios to use a Rule.

Here is how you can assign roles via the Post-Registration Hook:

  1. Create a new Machine-to-Machine (M2M) application for the Hook. Authorize it to use the Management API and give it the update:roles and create:role_members scopes. You’ll need the domain, Client ID, and Client Secret for the next step.
  2. Create a Post Registration Hook and click on the pencil icon to begin editing it. Click on the wrench on the editor’s top left and click “secrets” to store your M2M application Client ID and Client Secret.
  3. Click on the wrench at the top left of the editor again and click “NPM Modules”, click “Add Module” and search for auth0 and axios (or whichever request client you like) and add them.
  4. Add the following Hook and save. Replace the role ID and the tenant domain vars. Back in the Hooks settings, make sure that this hook is enabled.
module.exports = async function (user, context, cb) {
  const ManagementClient = require('auth0').ManagementClient;
  const axios = require('axios');
  const domain = 'YOUR_TENANT_DOMAIN';
  const client_id = context.webtask.secrets.HOOK_CLIENT_ID;
  const client_secret = context.webtask.secrets.HOOK_CLIENT_SECRET;

  const url = `https://${domain}/oauth/token`;
  const audience = `https://${domain}/api/v2/`;
  const role = 'YOUR_ROLE_ID'

  try {
    const response = await axios.post(url, {
      client_id,
      client_secret,
      audience,
      grant_type: 'client_credentials'
    });

    const token = response.data.access_token;
    console.log(token)
    const management = new ManagementClient({token, domain, audience});

    const params = { id: `auth0|${user.id}`};
    const data = { "roles": [role] };

    management.users.assignRoles(params, data, function (err) {
      if (err) {
        // Handle error.
        console.error(err);
      }
      console.log("success");
      cb(null, user, context);
    });
  } catch (err) {
    // Handle error.
    console.error(err);
  }
};

When a user signs up as aa database connection, they will have the default role.

Here is an FAQ for the Rule route: How do I add a default role to a new user on first login?

Thank you so much Stephanie for your complete answer.

Your answer saved me some time as I will be accepting users with a social connection so hooks will be out of the equation.

While I won’t need the code you have provided in your answer, it has helped me understand how I can call libraries inside of a hook.

I’ve also discovered that while the user will have the role, the context won’t get updated when I call my following rule which puts roles and permission in my id token.

Would it be fair to assume that in your code above, I also have to set the role to the context so it can be properly fetched by my next rule?

Louis-Philippe

Glad to hear the answer was helpful!

If you are assigning the role in one rule, then the next rule should be able to pick up on the user’s role. The dependent rule should be lower in the list of rules like this:

Does your second rule look something like this:

function (user, context, callback) {
  const namespace = 'http://YOUR_APP_URL';
  const assignedRoles = (context.authorization || {}).roles;

  const idTokenClaims = context.idToken || {};
  const accessTokenClaims = context.accessToken || {};

  idTokenClaims[`${namespace}/roles`] = assignedRoles;
  accessTokenClaims[`${namespace}/roles`] = assignedRoles;

  context.idToken = idTokenClaims;
  context.accessToken = accessTokenClaims;

  callback(null, user, context);
}

Yes, it is that exact code. I’m using the real-time web tasks logs to monitor the console.log of my rules.

When I sign up a new user, I assign the role with rule Add role to user.

Then, rule Add role to ID token gets executed where I have a console.log at the beginning of the rule like this:

const assignedRoles = (context.authorization || {}).roles;
console.log("Assigned:" + JSON.stringify(assignedRoles));

The variables assignedRoles is empty.

If I logout and log back in, then the rule Add role to ID token gets executed correctly and I have my role in my id token as expected.

The logs seem to show sequential execution of the rules.

Would it be possible that calls under the ManagementClient object are asynchronous? The ManagementClient object gives me the impression it is a wrapper for POST call to the OAuth Management API of my account. Just the way we pass in parameters to its methods.

If you can try it out and see what happens on your side, it would help me out.

I just tried a while loop in the rule Add role to user. Here’s a snippet of my rule

management.assignRolestoUser(
    { id : user.user_id}, 
    { "roles" :["rol_123456789"]},  // sample role ID of "Standard API Enduser"
    function (err) {
      if (err) {
        console.log('Error assigning role: ' + err);
      }    
      console.log("We assigned the role");
      var stop = new Date().getTime();
      while(new Date().getTime() < stop + 3000) {
        var t = 0;
      }
      callback(null, user, context);
  });

On sign up, the sign up screen hangs up for a few seconds and then returns to my application so I’m pretty sure the while loop was executed.

I looked at my logs and my role wasn’t assigned in rule Add role to ID token.

After testing it out a bit I realized that I typically add the role in the token in the callback fn that I pass to the Management API client. You are correct. The role gets assigned, but the next rule can’t add the role to the token. You will need to add something like this to the callback fn that you pass to the management API client:

const assignedRoles = (context.authorization || {}).roles;
assignedRoles.push('registered');
1 Like

Not a problem @stephanie.chamblee. I’m sure our conversation here will help future developers understand the Auth0 platform and get their Auth0 integration up to speed faster.

Thank you for all of your help,

Louis-Philippe

1 Like