...
In the third step, you need to enter the Redirect URIs and Post Logout URIs. Here’s a list of the URIs you need to add. They are the same for Redirect and Post Logout, but listed independently here for completeness. Replace “<baseUrl>” with the base URL of your environment.
Refer to Multitenant Region URLs to find the correct base URL for your deployment.
Redirect URIs
Code Block |
---|
- https://<baseUrl>/chat - https://<baseUrl>/knowledge-upload - https://<baseUrl>/theme - https://<baseUrl>/admin |
...
In Zitadel, set-up the
scope-management
service user, following this documentation https://unique-ch.atlassian.net/wiki/spaces/PUB/pages/588546089/Service +User+configuration#Creating-a-service-userUser configuration. This service user needs no Unique roles to function.In Zitadel, generate a Personal Access Token (PAT) for the created service user, details to be found here: https: //unique-ch.atlassian.net/wiki/spaces/PUB/pages/588546089/Service +User+configuration#Generating-personal-access-token-(PAT)User configuration. Copy the PAT after creation, you will need it in step 4 to store it in the Azure Key Vault.
In Zitadel, give the
scope-management
service user, theIAM Iam Owner Viewer
role andIam User Manager
roles on an instance level. To switch to the instance, simply click on Default Setting at the top right in Zitadel:Then add the Service user and give it the role under this button in Zitadel:
After the role was assigned to the user, it should show up like this in the list of the users with instance roles:
In the Azure Key Vault, search for the keyvault that contains the secret
manual-zitadel-scope-mgmt-pat
and add the generated PAT from step 2 there as a value.
...
5. Adding Zitadel Actions
tbd - work in progress
6. Configuring SSO
tbd - work in progress
OLD DOCS (will get removed soon - wip)
Scope
This tutorial only applies for clients with a Customer Managed Tenant.
...
Note |
---|
If you add any action the function must string match the name of the action else it is not called. |
Add one new action called addGrant
with this content
ZITADEL Actions are custom scripts that you can define to run at specific points during the authentication and authorization processes. After an action was added, it needs to be used by assigning it to one of the available Trigger Types.
Currently the following Actions need to be configured (as required).
Action: addGrant
Create a new
addGrant
actionNote: add the appropriate roles based on needs
Code Block |
---|
function addGrant(ctx, api) {
api.userGrants.push({
projectID: '<Resource ID of granted project Unique Apps>',
projectGrantID: '<Grant ID of granted project Unique Apps>',
roles: ['chat.chat.basic'] // default roles users get
});
} |
The addGrant
actions needs to react on the Trigger Type “Post Creation” because we want to add the default roles for the user after creation.
...
Action: addUserGroupsMetadataEntra
If Entra is used as an IDP, create the action
addUserGroupsMetadataEntra
...
Code Block | ||
---|---|---|
| ||
const 'chat.chat.unlimited', 'chat.chat.upload', 'chat.knowledge.read', 'chat.knowledge.write'] }); } |
This action has to be added as a Post Creation trigger in External Authentication flow.
Add another action called setEmailVerified
with this content
Code Block |
---|
function setEmailVerified(ctx, apilogger = require("zitadel/log") const http = require('zitadel/http') function addUserGroupsMetadataEntra(ctx, api) { logger.log('Writing group info on user metadata.'); const claims = ctx.claimsJSON(); const parsedGroups = JSON.parse(claims).groups; // Enable for debugging // logger.log('Claims: ' + JSON.stringify(claims)); // logger.log('Parsed groups: ' + JSON.stringify(parsedGroups)); // Get names with additional call to Graph API const batchRequests = []; for (const groupId of parsedGroups) { batchRequests.push({ "id": groupId, "method": "GET", "url": `/groups/${groupId}` }); } const graphApiBatchResponse = http.fetch('https://graph.microsoft.com/v1.0/$batch', { method: 'POST', headers: { "Authorization": `Bearer ${ctx.accessToken}`, "Content-Type": "application/json" }, body: { requests: batchRequests } }).json(); const entraGroups = []; for (const apiResponse of graphApiBatchResponse.responses) { const groupId = apiResponse.id; const groupName = apiResponse.body.displayName; entraGroups.push({ "id": groupId, "displayName": groupName, }); } // Check if groups are valid and do not contain empty string values if (entraGroups.some(group => group.id === "" || group.displayName === "")) { logger.log('Invalid groups found, skipping metadata update.'); return; } // append metadata to user api.v1.user.appendMetadata("groups", entraGroups); } |
After you’ve added the action, you need to assign it to a trigger.
...
Action: addUserGroupsMetadataOidc
Code:
Code Block |
---|
const logger = require("zitadel/log")
function addUserGroupsMetadataOidc(ctx, api) {
logger.log('Writing group info on user metadata.');
const claims = ctx.claimsJSON();
const parsedGroups = JSON.parse(claims).groups || [];
// Enable for debugging
// logger.log('Claims: ' + JSON.stringify(claims));
// logger.log('Parsed groups: ' + JSON.stringify(parsedGroups));
// To sync we need need the external groups' ID and display name
// Verify how the groups are sent on the ID token and adapt to the desired format.
let groups = parsedGroups.map(group => {
return {
"id": group.id,
"displayName": group.displayName,
};
});
// Check if groups are valid and do not contain empty string values
if (groups.some(group => group.id === "" || group.displayName === "")) {
logger.log('Invalid groups found, skipping metadata update.');
return;
}
// append metadata to user
api.v1.user.appendMetadata("groups", groups);
} |
Flow Type: External Authentication
Trigger Type: Post Authentication
Actions: addUserGroupsMetadataOidc
...
Action: addMicrosoftUserDataEntra
Code:
Code Block |
---|
const http = require('zitadel/http')
function addMicrosoftUserDataEntra(ctx, api) {
let country;
let department;
const profileApiResponse = http.fetch('https://graph.microsoft.com/v1.0/me?$select=country,department', {
method: 'GET',
headers: {
"Authorization": `Bearer ${ctx.accessToken}`,
"Content-Type": "application/json"
}
}).json();
country = profileApiResponse.country || null;
department = profileApiResponse.department || null;
api.v1.user.appendMetadata("country", country);
api.v1.user.appendMetadata("department", department);
} |
More fields can be fetched from the Microsoft Graph API: https://learn.microsoft.com/en-us/graph/api/resources/user?view=graph-rest-1.0#properties
Flow Type: External Authentication
Trigger Type: Post Authentication
...
Action: addUserGroupsMetadataSaml
Code:
Code Block |
---|
const logger = require("zitadel/log")
function addUserGroupsMetadataSaml(ctx, api) {
logger.log('Writing group info on user metadata.');
const sentGroups = ctx.v1.providerInfo?.attributes["groups"];
// Enable for debugging
// logger.log('Parsed groups: ' + JSON.stringify(sentGroups));
// To sync we need need the external groups' ID and display name
// Verify how the groups are sent on the ID token and adapt to the desired format.
let groups = sentGroups.map(group => {
return {
"id": group.id,
"displayName": group.displayName,
};
});
// Check if groups are valid and do not contain empty string values
if (groups.some(group => group.id === "" || group.displayName === "")) {
logger.log('Invalid groups found, skipping metadata update.');
return;
}
// append metadata to user
api.v1.user.appendMetadata("groups", groups);
} |
Flow Type: External Authentication
Trigger Type: Post Authentication
Action: addUserGroupsMetadataSaml
This action needs an additional action:
Action: prefilRegisterFromSAML
Code:
Code Block |
---|
function prefilRegisterFromSAML(ctx, api) { if (ctx.v1.externalUser?.externalIdpId != "xxxxxxx") { return } let firstname = ctx.v1.providerInfo?.attributes["GivenName"]; let lastname = ctx.v1.providerInfo?.attributes["Surname"]; let email = ctx.v1.providerInfo?.attributes["emailaddress"]; if (firstname) { api.setFirstName(firstname[0]); } if (lastname) { api.setLastName(lastname[0]); } if (email) { api.setEmail(email[0]); api.setEmailVerified(true); api.setPreferredUsername(email[0]); } |
This action has to be added as a Pre Creation trigger in External Authentication flow.
...
} |
Flow Type: External Authentication
Trigger Type: Post Authentication
Actions: prefilRegisterFromSAML
Don’t forget to add the IDP ID in the code above. You will find it in the IDP settings when clicking on the IDP and getting the ID from the URL
...
6. Configuring SSO
tbd - work in progress
...
Author |
---|