Configuring Custom Claims in ZITADEL
Developer Advocate
Introduction
With the release of ZITADEL v2.22.0, we announced the availability of Flat Role Claims. This feature enables you to easily integrate user roles into your tokens through customizable actions, providing greater flexibility and control over the claims in your access tokens, ID tokens, and user info.
Flat role claims are a way of representing user roles in a simplified and easily readable format. Instead of using nested structures or complex JSON objects, flat role claims utilize a list of strings where each string represents a user's role, often combined with additional identifiers, such as organization or project IDs. For example, a flat role claim could look like this:
["{orgId}:{role}", "{orgId}:{role}", ...]
Before supporting flat role claims, ZITADEL represented role claims using a JSON object structure. While this format provided all the necessary information, it could be difficult to parse and integrate with certain third-party tools and applications that expected roles in a simpler format.
ZITADEL switched to supporting flat role claims to provide more flexibility and ease of integration with various tools and systems. For example, better compatibility with tools such as OAuth2 Proxy or using ZITADEL as an OIDC provider with Hashicorp Vault. Flat role claims are easier to read, parse, and work with, reducing the complexity of managing user roles without having to recreate the role information as user metadata.
A Practical Use Case
Let’s consider that an organization has a project management application that integrates with ZITADEL for user authentication. The project management application requires user roles and permissions to be included in the ID token as custom claims in a specific format.
The organization uses ZITADEL's built-in roles and permissions for its users. However, the project management application expects user roles in a flat format (e.g., orgId:role
), while ZITADEL provides roles as a JSON object.
To address this requirement, ZITADEL allows the organization to reformat the user roles into the required flat format and include them as custom claims in the ID token. This way, the project management application can receive the ID token containing the custom claim and use it to grant the user access based on their roles and permissions.
By supporting flat role claims, ZITADEL enables the organization to customize the ID token to meet the specific requirements of their third-party application.
How to Configure Flat Role Claims in ZITADEL
Terminology: Actions, Triggers, and Flows
ZITADEL Actions is a key feature to extend the functionality of ZITADEL and continuously improve upon ZITADEL features and expand use cases. Actions allow you to create extended functionality by writing scripts using JavaScript. For example, the script of an action called doSomething should have a function called doSomething and would look like this:
function doSomething(ctx, api){
// read from ctx and manipulate with api
}
Trigger Types define the point during the execution of a request. Each trigger defines which readable information (ctx
) and mutual properties (api
) are passed into the called function as well as which libraries are available for the function.
Flows are the links between an action and a trigger type. Currently, ZITADEL provides the following flows:
- Internal Authentication
- External Authentication
- Complement Token
The Complement Token Flow is the process flow of token creation and token introspection. The flow contains the following triggers:
Pre Userinfo creation: This is the trigger that is called before userinfo
is set in the token or response, and takes in the parameters (ctx
and api
) and their respective fields, such as claims
, getUser()
, user
, getMetadata()
, grants
, and setClaim()
.
Pre access token creation: This is the trigger that is called before claims are set in the access token and the token type is set to JWT. It takes in the parameters (ctx
and api
) and their fields, including claims
, getUser()
, user, getMetadata()
, grants
, setClaim()
, and appendLogIntoClaims()
.
You can define custom claims within the Complement Token Flow. You can create an action and use roles from ZITADEL to add a claim to your tokens/userinfo in the format of your choice.
A Sample Action
This code snippet is a JavaScript function namedflatRoles
that sets additional role claims in a token, combining the project ID and role name as a key-value pair. The function is part of the Complement token flow and is triggered during Pre Userinfo creation and Pre access token creation.
/**
* Sets the roles as an additional claim in the token with roles as the value and project as the key.
*
* The role claims of the token look like the following:
*
* // Added by the code below
* "my:zitadel:grants": ["{projectId}:{roleName}", "{projectId}:{roleName}", ...],
* // added automatically
* "urn:zitadel:iam:org:project:roles": {
* "asdf": {
* "201982826478953724": "zitadel.localhost"
* }
* }
*
* Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation
*
* @param ctx
* @param api
*/
function flatRoles(ctx, api) {
if (ctx.v1.user.grants === undefined || ctx.v1.user.grants.count == 0) {
return;
}
let grants = [];
ctx.v1.user.grants.grants.forEach(claim => {
claim.roles.forEach(role => {
grants.push(claim.projectId + ':' + role)
})
})
api.v1.claims.setClaim('my:zitadel:grants', grants)
}
- The function accepts two parameters:
ctx
andapi
.
ctx
: Contains context information about the user, including user grants.api
: Provides methods to interact with ZITADEL's API.
- The function checks if
ctx.v1.user.grants
is undefined or has no elements; if true, it returns immediately, as there's nothing to process. - If there are user grants, the function initializes an empty array called
grants
, which will be used to store the flattened roles. - Next, it iterates through the user's grants and roles, concatenating the
projectId
androle
with a colon (":") separator, and then pushes the result into thegrants
array. - Finally, the function sets a new claim called
my:zitadel:grants
with the content of the grants array using theapi.v1.claims.setClaim()
method. - When the function is executed, it adds the flat role claims to the token in the format
{projectId}:{roleName}
.
Enable the Action in ZITADEL
To use this flat role claim feature in ZITADEL, follow these steps:
- Log in to the ZITADEL management console and navigate to the project where you want to use the flat role claim feature. Click on Actions.
- Click on the New button to create a new action.
- In the Create an Action section, give the action the same name as the function name, e.g., flatRoles. In the script field, paste the provided flatRoles code. Click on Add.
- The flatRoles action will now be listed.
- Next, we must select a Flow Type. Select Complement Token from the dropdown.
- Next, you must choose a trigger. Click Add trigger.
- Select Pre Userinfo creation as the Trigger Type and select flatRoles as the associated action.
- You will see the Pre Userinfo creation trigger listed.
- Follow the same steps to add the Pre access token creation trigger and add flatRoles as the associated action. Afterwards, you will see both triggers listed as shown below:
- Don't forget to select the Assert Roles on Authentication checkbox on the Project dashboard and click Save.
Now, when a user logs in and receives an ID token, the action will be executed, transforming the user roles into the required flat format and adding them as a custom claim with the key my:zitadel:grants
. This custom claim can then be used by third-party applications to manage user access based on their roles and permissions in the desired format.
Closing Remarks
In summary, supporting custom role claims in ZITADEL enhances the user experience by providing more customization options, better compatibility with third-party tools, and improved adaptability to different use cases
Thanks for reading!
ZITADEL is FREE! So, go on and try it out.