Help needed: Custom claim not appearing in token/userinfo from Complement Token Action
I'm working on integrating Zitadel with pgAdmin using OAuth2. My goal is to include a custom
roles
claim in the ID token and userinfo response, so I can leverage OAUTH2_ADDITIONAL_CLAIMS
in pgAdmin for access control.
I've created a Complement Token script to flatten the grants
structure and set a custom roles
claim. However, neither this dynamic claim nor even a simple static claim (for testing) appears in the ID token or userinfo output.
For example, even this simplified test script doesn't work:
function flatRoles(ctx, api) {
api.v1.claims.setClaim("test", "hello");
}
I've ensured:
- The script is enabled and attached to the correct Trigger: Pre Userinfo Creation, Pre Access Token Creation
- The user has valid roles
- I'm inspecting userinfo
and the token
in pgAdmin logs
Still, only the default Zitadel claims are returned. My custom claim (test
or roles
) is never visible.
❓ Am I missing something for Complement Token Actions to take effect? Do I need to adjust scopes or project settings to expose custom claims?
Here's a screenshot of my action configuration and a copy of the script. Any guidance would be much appreciated!
Thanks in advance!
My Script:
/**
* Flatten roles into a top-level "roles" claim.
* Format: "roles": ["role1", "role2", ...]
*
* Flow: Complement Token
* Triggers: Pre Userinfo Creation, Pre Access Token Creation
*
* @param ctx
* @param api
*/
function flatRoles(ctx, api) {
const userGrants = ctx.v1.user?.grants?.grants;
if (!userGrants || userGrants.length === 0) {
return;
}
const flatRoles = [];
userGrants.forEach(grant => {
if (grant.roles && grant.roles.length > 0) {
grant.roles.forEach(role => flatRoles.push(role));
}
});
if (flatRoles.length > 0) {
api.v1.claims.setClaim("roles", flatRoles);
}
}

31 Replies
no one?
hey @Manuel Desdin thanks for your question and welcome to the server, I will look into this and will get back to you! 🙂
Came here because of the same issue. Tried to add static claim, even throwing in the script and nothing happens on login.
hey @Manuel Desdin good morning, can you pls check if you have Assert Roles on Authentication checked?.
ZITADEL • Identity infrastructure, simplified for you
ZITADEL • Identity infrastructure, simplified for you
This article demonstrates how to add custom claims to your tokens through customizable actions.
hi rajat, thanks for taking care, yes, yes, i see the claims listed in the response, but none of the custom ones
this is how it looks in my case

hey @Manuel Desdin what scopes are you passing, on your OAuth2 side?
you mean here?

i have also tested it by selecting all options, to include them everywhere, right now it is only part of the userinfo
based on my observations seems like pgadmin has to request a specific claim for the custom claims to be included.
yes, sure, give me a second to retrieve this
'OAUTH2_SCOPE':'openid email profile' and 'OAUTH2_ADDITIONAL_CLAIMS':{'roles':['pgadmin-administrator','pgadmin-user']}
if i don't set OAUTH2_ADDITIONAL_CLAIMS then no claim is requested
try appending
urn:zitadel:iam:org:project:roles
in the oauth2 scope 'OAUTH2_SCOPE': 'openid email profile urn:zitadel:iam:org:project:roles
oki, this is a good idea, i will do now and see
nothing yet, trying few things
i get the claims included in
urn:zitadel:iam:org:project:roles
, but not the custom ones
and this was so before, so far nothing has changedIf it helps, I figured out the action name must match exactly the function name in the script. @Manuel Desdin In your case try changing "Flatten roles into a claim" to "flatRoles"
oh yes 😅
wow, ok, let me try it
I had a test for adding static claim like: function addClaim(), I renamed the action to addClaim and boom, got the claim on frontend
I don't think it was mentioned in the docs
i must be missing something more, still not listed, but let me first do some more debugging to be sure
and i started to log inside the action too, will check this as well
True. Somehow I missed it yesterday
its easy to miss but then its fun to play around 😄

could it be this error?
action run failed: ReferenceError: api is not defined at <eval>:12:1(6)
sorry, this one is my bad
i was adding stuff to it out of desperation
line 12 has this: api.v1.claims.setClaim("myclaim", "testing")
and this is outside of the function
and this was it
i am very sorry, it was just a wrong naming for the action
thanks a lot!!! @Rajat @karripallo
hey @Manuel Desdin can I see your scipt again, just to make sure if yoiu missed soemthing
a second
/**
* Flatten roles into a top-level "roles" claim.
* Format: "roles": ["role1", "role2", ...]
*
* Flow: Complement Token
* Triggers: Pre Userinfo Creation, Pre Access Token Creation
*
* @param ctx
* @param api
*/
let logger = require("zitadel/log");
function flatRoles(ctx, api) {
const userGrants = ctx.v1.user?.grants?.grants;
if (!userGrants || userGrants.length === 0) {
logger.log("flatRoles: No user grants.");
return;
}
const flatRoles = [];
userGrants.forEach(grant => {
if (grant.roles && grant.roles.length > 0) {
grant.roles.forEach(role => flatRoles.push(role));
}
});
if (flatRoles.length > 0) {
api.v1.claims.setClaim("roles", flatRoles);
}
}
looks okay! anything on your logs?
all ok on logs side!
and working as expected
Great success ✨
Oh so it worked
😄
yes, yes, the problem was only the wrong action naming
Oh wow ahaha!
But happy that it worked!
🎉 Looks like you just helped out another community member! Thanks for being so helpful <@833999961363316746>! You're now one step closer to leveling up—keep up the amazing peer support! 🚀