Fork me 🍴

Willian Antunes

Add claims to JWT through Actions on Auth0

β€’ 5 minute read

auth0, jwt

Table of contents
  1. What will we do?
  2. Managing permissions through claims
  3. Adding Action during post-login trigger
    1. Action limitations
    2. Why not Rules?
  4. Conclusion and next steps

Recently, I came up with a solution to handle single sign-on (SSO) internally for all enterprise applications inside the company. This scenario is typically known as B2E or workforce. Look at the Gartner report about Critical Capabilities for Access Management for more details. So, for example, if a company uses Azure AD for its employees, the companies's internal application can use it to grant access (authentication), and allow a user to execute something or not (permission).

What will we do?

This post is the first of two where I will demonstrate how we can achieve SSO with Django applications for the B2E scenario, focused on The Django admin site, through Auth0.

Managing permissions through claims

The idea is quite simple: after the user's login, we'll include all groups he belongs to inside a custom claim. Of course, this strategy is not production-ready, but let's keep things simple for the article's sake. Let's see an image that illustrates the whole flow:

Sequence diagram that shows 4 columns. It has the user, the admin portal, the auth0 tenant, and the XYZ company. Basically it demonstrates the authentication process among these entities.

The dashed orange circle denotes the step where we'll add a custom claim.

Adding Action during post-login trigger

To add an Action, we first should choose one of the flows available:

Actions has 6 flows: login, machine to machine, pre user registration, post user registration, post change password, and send phone messages.

Let's pick login flow following the sequence diagram we saw earlier. Next, click on the plus symbol and choose Build Custom.

Where you should click to build a custom action.

Put the name Enrich JWT with Groups from AD and click on Create.

To create an action, you should give it a name, choose a trigger, and then its runtime.

You should see the following image:

When you create an action, you goes to its "development area". From there you can code and deploy the action.

Reading the methods that we can implement, we should pick the onExecutePostLogin. The onContinuePostLogin allows us to require further steps after authentication before letting the user continue to the application where the flow started. The Redirect with Actions article can give you further details.

Now, let's see the code:

exports.onExecutePostLogin = async (event, api) => {
  // Collecting secrets
  const tenant = event.secrets.TENANT
  const audience = event.secrets.AUDIENCE
  const clientId = event.secrets.APP_CLIENT_ID
  const clientSecret = event.secrets.APP_CLIENT_SECRET
  // Creating management API
  const ManagementClient = require("auth0").ManagementClient
  const managementClient = new ManagementClient({
    domain: `${tenant}.us.auth0.com`,
    scope: "read:users",
    clientId,
    clientSecret,
    audience,
  })
  // Main routine
  const userId = event.user.user_id
  const user = await managementClient.getUser({ id: userId })
  const shouldEnrichJWTWithGroups = user.hasOwnProperty("groups")
  if (shouldEnrichJWTWithGroups) {
    const claimKey = "https://www.willianantunes.com/ad/groups"
    const claimValue = user.groups
    api.idToken.setCustomClaim(claimKey, claimValue)
  }
}

An essential thing to mention here is the part where we use the Management API. It has rate limits, so we're leaving it this way just for the article's sake. With that said, let's explain the script:

  1. First, we collect "must have" secrets.
  2. We use the secrets from the previous step to instantiate a class to deal with the Management API.
  3. We retrieve all user attributes given the user ID.
  4. If the user has the root attribute groups, we add it as a custom claim in the ID token.

You might be thinking: Why do we use the Management API if the Action delivers user attributes also including "application metadata"? Sadly, some attributes are not available, and groups is one of them. I found this explanation on Auth0 Community, but still, it's not available.

Depending on how you configure your enterprise connection with the directory service, it may include the root attribute groups. For example, look at how I configured mine for testing purposes:

When you configure an enterprise connection for Azure AD, you can selected which attributes you want to retrieve. "Groups" is one for instance.

Action limitations

Consulting Auth0 Management API all the time is cumbersome. Sadly we can't circumvent that because of current Actions limitations. I recommend you read this community post where there is an official statement by the Auth0 community moderator.

Why not Rules?

The problem with Auth0 Rules is its expected deprecation sometime in the second half of 2022. Although it has many OOTB solutions, Actions will rule over it soon. You can consult Actions marketplace and check out some samples that can help you migrate or create new things.

Conclusion and next steps

Our next article will use the post-login trigger we wrote here with a full-blown solution, including Django with Mozilla Django OIDC and Auth0 Deploy CLI. Stay tuned πŸ˜‰!

See everything we did here on GitHub.

Posted listening to A nova bossa é violão, Paulinho Nogueira 🎢.


Have you found any mistakes πŸ‘€? Feel free to submit a PR editing this blog entry πŸ˜„.