Hi Mikael,
The way I solved this was indeed in the Mapping Rule itself, I'll only go about the "extraction" phase where I retrieve the stored attribute and transform such that it is always an array:
const CUSTOM_ATTRIBUTES_TO_ACCESS_TOKEN = {
"aud": {
"claim_type": "array",
"claim_name": "aud"
},
"azp": {
"claim_type": "string",
"claim_name": "azp"
}
};
function collect_wanted_attributes(cid) {
let ret_obj = {};
if (cid == null) return ret_obj;
let attr_itr = stsuu.getAttributes();
while (attr_itr.hasNext()) {
let attr = attr_itr.next();
let attr_name = "" + attr.getName();
if (CUSTOM_ATTRIBUTES_TO_ACCESS_TOKEN.hasOwnProperty(attr_name)) {
if (CUSTOM_ATTRIBUTES_TO_ACCESS_TOKEN[attr_name]["claim_type"] === "array") {
logmsg(MRN, "DEBUG", "Mapping attribute values into an array");
claim_value = [];
let attr_vals = attr.getValues();
if (attr_vals.length == 0) continue;
if (attr_vals.length > 1) {
logmsg(MRN, "SENSITIVE", "Transforming Java String[]: " + JSON.stringify(claim_value, null, 2));
for (let i = 0; i < attr_vals.length; i++) {
claim_value.push("" + attr_vals[i]);
}
logmsg(MRN, "SENSITIVE", "Resulting array: " + JSON.stringify(claim_value, null, 2));
} else {
logmsg(MRN, "SENSITIVE", "Array attribute value = " + attr.getValues()[0]);
if ("" + attr.getValues()[0] != "") {
claim_value = ("" + attr.getValues()[0]).split(",");
logmsg(MRN, "SENSITIVE", "Resulting array: " + JSON.stringify(claim_value, null, 2));
}
}
} else {
logmsg(MRN, "DEBUG", "Mapping attribute value into regular js string.");
claim_value = "" + attr.getValues()[0];
}
if (attr_name == "oidc_username") {
if (sub == null) {
ret_obj[CUSTOM_ATTRIBUTES_TO_ACCESS_TOKEN[attr_name]["claim_name"]] = claim_value;
}
} else {
ret_obj[CUSTOM_ATTRIBUTES_TO_ACCESS_TOKEN[attr_name]["claim_name"]] = claim_value;
}
}
}
logmsg(MRN, "SENSITIVE", "Collected the following additional claims: " + JSON.stringify(ret_obj));
return ret_obj;
}
And before constructing the JWT, I "collect' my attributes first:
let additional_claims = collect_wanted_attributes(client_id);
let at = buildJwtAccessToken(stsuu, oauth_client, additional_claims);
And in the phase where you build the JWT Token (in my case the access token), I provided an additional parameter (this is built on top of Leo's code):
function buildJwtAccessToken(stsuu, oauth_client, provided_claims) {
let standard_claims = {
"exp": expire,
"iat": now,
"sub": "" + sub,
"aud": "" + oauth_client.getClientId(),
}
let claims = standard_claims;
for (let provided_claim in provided_claims) {
claims[provided_claim] = provided_claims[provided_claim];
}
}
The resulting JWT should contain an array, always.
Keep in mind this is an extract from my code, functions such as "logmsg()" will probably not work for you.
Hope it helps.
------------------------------
Dries Eestermans
IS4U
------------------------------