ZITADEL Docs
Integrate & AuthenticateActionsV1

Code examples

Actions are a powerful tool to extend ZITADEL, and you might wonder what use cases actions can be used for.

This page provides a non-exhaustive list of possibilities which is provided by examples. If a use case is missing, feel free to contribute an issue or pull request to the repository, thanks in advance 🤗.

Customize OIDC response

Append claims returned on OIDC requests.

Triggers

Set a hardcoded claim

Extend the claims by a hardcoded value.

Code example
/** * Add an additional claim to the token / userinfo, if it's not already present * * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation * * @param ctx * @param api */function addClaim(ctx, api) {  api.v1.claims.setClaim('year', 2023)}

Set dynamic claim from user metadata

Extend the claims by dynamically reading metadata from a user and sets the picture-claim if idpPicture-metadata value is present.

Code example
/** * Set picture claim from IdP picture metadata * * Flow: Complement token, Trigger: Pre Userinfo creation, Pre access token creation * * @param ctx * @param api */function add_picture_claim_from_idp_metadata(ctx, api) {  // return if picture already set  if (ctx.v1.claims && ctx.v1.claims.picture) {    return;  }  const metadata = ctx.v1.user.getMetadata();  // assign picture from metadata, if present  metadata.metadata.forEach(({ key, value: picture }) => {    if (key === 'idpPicture' && picture) {      api.v1.claims.setClaim('picture', picture);    }  });}

Set dynamic claim from organization metadata

Extend the claims by dynamically reading metadata from an organization and sets the present metadata.

Code example
/** * This example demonstrates how to retrieve the organisation metadata. * In this case it will be used to set the values as custom claims in the tokens / userinfo and introspection response. *  * Requires: ZITADEL >=v2.45.0 * * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation * * @param ctx * @param api */function setOrgClaims(ctx, api) {    let metadata = ctx.v1.org.getMetadata()    if (metadata === undefined || metadata.count == 0) {        return    }    metadata.metadata.forEach(md => {        api.v1.claims.setClaim('org_claim_'+md.key, md.value);    })}

Custom role mapping in claims

Some products require specific role mapping from ZITADEL, no worries we got you covered 😉

Code example
/** * sets the roles an additional claim in the token with roles as value an project as 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)}

Custom role mapping including org metadata in claims

There's even a possibility to use the metadata of organizations the user is granted to

Code example
/** * This example demonstrates how to set a custom groups claim using also the organisation metadata of the user grants. *  * Requires: ZITADEL >=v2.51.0 * * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation *  * Assuming there are two organizations: *  - ID: 201982826478953724 *    Domain: zitadel.localhost *    Metadata: *     - key: crm-id *       value: 6d6dee11-31d3-49c3-9087-99216c9cb6f6 *  - ID: 263022974217486756 *    Domain: other-org.localhost *    Metadata: *     - key: crm-id *       value: 77b597dc-8a5b-4e55-a6b5-80a6a475c062 *  * and the user is granted the `user` role on both organizations, the group claim of the token looks like the following: *  * // added by the code below * "groups": ["user:6d6dee11-31d3-49c3-9087-99216c9cb6f6", "user:77b597dc-8a5b-4e55-a6b5-80a6a475c062", ...], * // added automatically * "urn:zitadel:iam:org:project:roles": { *   "user": { *     "201982826478953724": "zitadel.localhost", *     "263022974217486756": "other-org.localhost" *   } * } * * Flow: Complement token, Triggers: Pre Userinfo creation, Pre access token creation * * @param ctx * @param api */function groups(ctx, api) {    if (ctx.v1.user.grants == undefined || ctx.v1.user.grants.count == 0) {        return;    }      let groups = [];    ctx.v1.user.grants.grants.forEach(grant => {        let crmId = grant.getOrgMetadata().metadata.find(md => md.key == 'crm-id')        if (crmId) {        	grant.roles.forEach(role => {	            groups.push(role+":"+crmId.value)              })        }    })    api.v1.claims.setClaim('groups', groups)}

Customize SAML response

Append attributes returned on SAML requests.

Triggers

Custom role mapping in attributes

Some products require specific role mapping from ZITADEL, no worries we got you covered 😉

Code example
/** * Add an custom attribute to the SAMLResponse, if it's not already present. * This example will add the resourceOwner as custom attribute `OrgID`. * It additionally shows how get the user's project authorization and add each role in the form of `projectId:role` as a `Roles` attribute. * * Flow: Complement SAMLResponse, Triggers: Pre SAMLResponse creation * * @param ctx * @param api */function setCustomAttribute(ctx, api) {    const user = ctx.v1.getUser()    api.v1.attributes.setCustomAttribute('OrgID', '', user.resourceOwner)    if (ctx.v1.user.grants == undefined || ctx.v1.user.grants.count == 0) {        return;    }    let roles = [];    ctx.v1.user.grants.grants.forEach(grant => {        grant.roles.forEach(role => {            roles.push(grant.projectId+':'+role)          })    })    api.v1.attributes.setCustomAttribute('Roles', '', ...roles)  }

Set dynamic attribute from organization metadata

Extend the attributes by dynamically reading metadata from an organization and sets the present metadata.

Code example
/** * This example demonstrates how to retrieve the organisation metadata. * In this case it will be used to set the values as custom attributes on the SAML response. *  * Requires: ZITADEL >=v2.45.0 * * Flow: Complement SAMLResponse, Triggers: Pre SAMLResponse creation * * @param ctx * @param api */function setOrgAttribute(ctx, api) {    let metadata = ctx.v1.org.getMetadata()    if (metadata === undefined || metadata.count == 0) {        return    }    metadata.metadata.forEach(md => {        api.v1.attributes.setCustomAttribute('org_attribute_'+md.key,'', md.value);    })}

Manipulate user

You can automate manual tasks such as assigning default roles during user creation.

Set email always verified

Useful if you trust the provided information or don't want the users to verify their e-mail addresses.

Triggers

Code example
/** * Set verified email of a user to true. * Useful if external identity provider doesn't send email verified attribute or you like to add all users with a verified email. * * Flow: Internal Authentication or External Authentication, Trigger: Pre Creation * * @param ctx * @param api */function setEmailVerified(ctx, api) {    api.setEmailVerified(true)}

Assign roles to users

Allows you to assign default roles to a user after the user was created or federated.

Triggers

Code example
/** * Add a usergrant to a new created/registered user * * Flow: Internal Authentication or External Authentication, Trigger: Post creation * * @param ctx * @param api */function addGrant(ctx, api) {    api.userGrants.push({        projectID: '<the projects resource ID>',        roles: ['<the role key>']    });}

Add metadata to users

Adding metadata to users allows you to set default metadata on users.

Triggers

Code example
/** * Add metadata if metadata are available * * Flow: External Authentication or Internal Authentication, Trigger: Pre Creation, Post Authentication * * @param ctx * @param api */function add_metadata(ctx, api) {  if (api.metadata === undefined) {    return;  }    api.v1.user.appendMetadata('add', 'me');}

Use provided fields of identity providers

If you want to ensure that the data of a user are always up to date, you can automatically update user fields during authentication and save time of your customers and your team.

Trigger

Fields provided by Okta as OIDC IdP

If you use Okta as an identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Set first and lastname of a user on just in time provisioning for okta. * Useful if you like to fill the first and lastname with the name stored on okta, so the user doesn't have to fill himself. * Also set email to verified, so the user doesn't get a verification email * * Flow: External Authentication, Trigger: Post Authentication * * @param ctx * @param api */let logger = require("zitadel/log")function mapOktaOauth(ctx, api) {  logger.log('Populating extra information for new Okta user');  if (ctx.v1.externalUser.externalIdpId != "your-idp-id") {    return  }  api.setFirstName(ctx.v1.providerInfo.name);  api.setLastName(ctx.v1.providerInfo.name);  api.setEmailVerified(true)  api.setEmail(ctx.v1.providerInfo.email)  api.setPreferredUsername(ctx.v1.providerInfo.preferred_username)}

Fields provided by Gitlab

If you use Gitlab as an identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Set first and lastname of a user on just in time provisioning for gitlab. * Useful if you like to fill the first and lastname with the name stored on gitlab, so the user doesn't have to fill himself. * * Flow: External Authentication, Trigger: Post Authentication * * @param ctx * @param api */function mapGitLabOAuth(ctx, api) {  if (ctx.v1.externalUser.externalIdpId != "your-idp-id") {    return  }  api.setFirstName(ctx.v1.providerInfo.name);  api.setLastName(ctx.v1.providerInfo.name);}

Fields provided by GitHub

If you use GitHub as an identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Set first and lastname of a user on just in time provisioning for github. * Useful if you like to fill the first and lastname with the name stored on github, so the user doesn't have to fill himself. * * Flow: External Authentication, Trigger: Post Authentication * * @param ctx * @param api */function mapGitHubOAuth(ctx, api) {      if (ctx.v1.externalUser.externalIdpId != "your-idp-id") {    return  }  api.setFirstName(ctx.v1.providerInfo.name);  api.setLastName(ctx.v1.providerInfo.name);}

Claims provided by a generic OIDC identity provider

If you use a generic OIDC identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Set IdP picture as metadata * * Flow: External Authentication, Trigger: Post authentication * * @param ctx * @param api */function set_idp_picture_metadata(ctx, api) {  // for production use cases, implement logging or alerting here to ensure traceability  // return if api undefined  if (api === undefined) {    return;  }  const picture = ctx.getClaim('picture');  if (picture !== null) {    api.v1.user.appendMetadata('idpPicture', picture);  }}

Attributes provided by Okta as SAML IDP

If you use Okta as an identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Use SAML attributes from OKTA SAML SP as attributes for user creation/update * * Flow: External Authentication, Trigger: Post authentication * * @param ctx * @param api */function prefilRegisterFromOKTASAML(ctx, api) {	if (ctx.v1.externalUser.externalIdpId != "<SAML IDP ID>") {        return    }    // the attribute names below represent a attribute statement with basic format (configured in OKTA App)    let firstname = ctx.v1.providerInfo.attributes["givenname"];    let lastname = ctx.v1.providerInfo.attributes["surname"];    let email = ctx.v1.providerInfo.attributes["emailaddress"];    let username = ctx.v1.providerInfo.attributes["emailaddress"];    if (firstname != undefined) {    	api.setFirstName(firstname[0]);    }    if (lastname != undefined) {	    api.setLastName(lastname[0]);    }    if (email != undefined) {    	api.setEmail(email[0]);	    api.setEmailVerified(true);    }    if (username != undefined) {	    api.setPreferredUsername(username[0]);    }    }

Attributes provided by Microsoft Entra as SAML IDP

If you use Microsoft Entra as SAML identity provider, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Use SAML attributes from entra id SAML SP as attributes for user creation/update * * Flow: External Authentication, Trigger: Post authentication * * @param ctx * @param api */function prefilRegisterFromEntraId(ctx, api) {	if (ctx.v1.externalUser.externalIdpId != "<SAML SP id>") {        return    }    // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info    let firstname = ctx.v1.providerInfo.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname"];    let lastname = ctx.v1.providerInfo.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname"];    let email = ctx.v1.providerInfo.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress"];    let displayname = ctx.v1.providerInfo.attributes["http://schemas.microsoft.com/identity/claims/displayname"];    // username would look like this: adlerhurst_zitadel.com#EXT#@adlerhurstzitadel.onmicrosoft.com    let username = ctx.v1.providerInfo.attributes["http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name"];    if (firstname != undefined) {    	api.setFirstName(firstname[0]);    }    if (lastname != undefined) {	    api.setLastName(lastname[0]);    }    if (email != undefined) {    	api.setEmail(email[0]);	    api.setEmailVerified(true);    }    if (displayname != undefined) {	    api.setDisplayName(displayname[0]);    }    if (username != undefined) {	    api.setPreferredUsername(username[0]);    }    }

Attributes provided by a generic SAML identity provider

If you use a SAML identity provider like mocksaml, you can improve the onboarding experience of new users by prefilling some basic information during authentication.

Code example
/** * Use SAML attributes from external IDP as attributes for user creation/update * * Flow: External Authentication, Trigger: Post authentication * * @param ctx * @param api */function map(ctx, api) {    if (ctx.v1.externalUser.externalIdpId != "your_external_idp") {        return    }    // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info    let firstname = ctx.v1.providerInfo.attributes["urn:oid:2.5.4.42"];    let lastname = ctx.v1.providerInfo.attributes["urn:oid:2.5.4.4"];    let email = ctx.v1.providerInfo.attributes["urn:oid:1.3.6.1.4.1.5923.1.1.1.6"];    let displayname = ctx.v1.providerInfo.attributes["urn:oid:2.5.4.3"];    let username = ctx.v1.providerInfo.attributes["urn:oid:0.9.2342.19200300.100.1.1"];    if (firstname != undefined) {    	api.setFirstName(firstname[0]);    }    if (lastname != undefined) {	api.setLastName(lastname[0]);    }    if (email != undefined) {    	api.setEmail(email[0]);	api.setEmailVerified(true);    }    if (displayname != undefined) {	api.setDisplayName(displayname[0]);    }    if (username != undefined) {	api.setPreferredUsername(username[0]);    }}

Context-aware execution

Based on the context, the execution path of an action can change. ZITADEL allows complex execution paths, of course. 😎

Based on auth request information

Execution paths might change based on the application initiating the authentication.

Triggers

Code example
/** * Logs if the app is console * Useful if you like to execute an action for specific apps * * Flows: *  *  - Internal Authentication, Triggers: Pre Creation, Post Creation, Post Authentication *  - External Authentication, Triggers: Pre Creation, Post Creation, Post Authentication * * @param ctx * @param api */let logger = require('zitadel/log');function logConsole(ctx, api) {    if (ctx.v1.authRequest == undefined || ctx.v1.authRequest.applicationId == '') {        logger.log('🤖 No auth request or app id provided');        return;    }    if (ctx.v1.authRequest.applicationId == 'your-zitadel-console--client-id') {        logger.log('🤖 authentication with console');        return;    }    logger.log('🤖 authentication with another app: ' + ctx.v1.authRequest.applicationId);}  

This example uses zitadel's log module

Check authentication error

Your action can also check for errors during the login process.

Triggers

Code example
/** * Log successful and failed authentications with relevant information * * Flow: External/Internal Authentication, Trigger: Post authentication * * @param ctx * @param api */let logger = require("zitadel/log")function log(ctx, api) {    if (ctx.v1.authError == "none") {        logger.log("successful login: username/" + ctx.v1.authRequest.userName + ", timestamp/" + ctx.v1.authRequest.changeDate + ", appID/" + ctx.v1.authRequest.applicationId + ", remoteAddr/" + ctx.v1.httpRequest.remoteAddr + ", request/" + JSON.stringify(ctx.v1.httpRequest))    } else {        logger.log("authentication failed: error/" + ctx.v1.authError + ", username/" + ctx.v1.authRequest.userName + ", timestamp/" + ctx.v1.authRequest.changeDate + ", appID/" + ctx.v1.authRequest.applicationId + ", remoteAddr/" + ctx.v1.httpRequest.remoteAddr + ", request/" + JSON.stringify(ctx.v1.httpRequest))    }}

This example uses zitadel's log module

Throw an error

Allows you to limit the user interaction. The error thrown will be shown to the user if the action is not allowed to fail.

Code example
/** * Returns an error * The error looks as follows in the login ui: * some error at err (:9:2(2)) * * Flow: all flows */function err(ctx, api) {	throw "some error"}

Was this page helpful?

On this page