Auth0 OpenID Connect compliant login: How to add app_metadata to id_token using custom claims?

Auth0 is a great service for adding login functionality to web applications. And if you are using Auth0 there’s a high chance you might have wondered about this: How to add app_metadata to id_token when logging in with Auth0?

After reading a lot of forum posts and Auth0 documentation I can now tell you that the right way to do this is to create a Auth0 rule.

Screenshot of Auth0 rule setup.

Be sure to use the "try this rule" functionality to get better insight into the rule's behaviour.

The rule in this screenshot is based on something I had to do for a work project. We designed our application to store what resources users’ have access to within the Auth0 users themselves.

This is possible because app_metadata is read-only.

When logging in with Auth0 we typically get a access_token together with an id_token (if scopes are configured correctly). The access_token is used to call Auth0 APIs, and the id_token is used to verify claims about the user. In our case we needed to answer, “does this user in fact have access to these resources?”

The id_token is a signed JSON web token. This means we can verify that this token has not been tampered with. When our backend APIs are called we make sure to verify the authenticity of the id_token, before considering the included accessTo property. And in order for this accessTo property to be available within the id_token we needed to create this rule.

function (user, context, callback) {
  const { accessTo } = user.app_metadata || {};
  const namespace = 'https://example.org/';
  if (context.idToken) {
    context.idToken[namespace + 'accessTo'] = accessTo;
  }
  callback(null, user, context);
}

Let’s break down the code in the rule:

  1. I use destructuring to retrieve accessTo data from user.app_metadata. I also use some defaulting to ensure that the code does not break down if app_metadata somehow is not defined on the user. Also, the rule does not try to add accessTo data unless there exists a context.idToken. The reason is that depending on the login configuration you might not be asking for an idToken.
  2. In order to be Open ID Connect compliant we must use a namespaced property when adding data to the id_token. This is called a custom claim. This namespace is used to avoid colliding with standard OICD properties. This was an important detail that tripped me up for a while, until I read enough documentation to understand it. Learn more about Auth0 custom claims.
  3. Finally, when initiating an auth0 login you need to specify scopes, in our application we use "openid email profile". You might get away with using fewer scopes. Learn about scopes in the Auth0 documentation.

If this blog post helped you, please do give me shout out on Twitter. :)